diff --git a/AppKit/AppKit.j b/AppKit/AppKit.j new file mode 100644 index 0000000000..4567fdf1d2 --- /dev/null +++ b/AppKit/AppKit.j @@ -0,0 +1,60 @@ +/* + * AppKit.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CALayer.j" +import "CPAnimation.j" +import "CPApplication.j" +import "CPButton.j" +import "CPClipView.j" +import "CPCollectionView.j" +import "CPColor.j" +import "CPColorPanel.j" +import "CPColorWell.j" +import "CPCompatibility.j" +import "CPControl.j" +import "CPCookie.j" +import "CPDocument.j" +import "CPDocumentController.j" +import "CPEvent.j" +import "CPFont.j" +import "CPFontManager.j" +import "CPGeometry.j" +import "CPImage.j" +import "CPImageView.j" +import "CPMenu.j" +import "CPMenuItem.j" +import "CPPanel.j" +import "CPPasteboard.j" +import "CPPopUpButton.j" +import "CPProgressIndicator.j" +import "CPResponder.j" +import "CPScroller.j" +import "CPScrollView.j" +import "CPSegmentedControl.j" +import "CPShadow.j" +import "CPSlider.j" +import "CPTextField.j" +import "CPToolbar.j" +import "CPToolbarItem.j" +import "CPView.j" +import "CPWindow.j" +import "CPWindowController.j" diff --git a/AppKit/AppKit.steam b/AppKit/AppKit.steam new file mode 100644 index 0000000000..14e67d8d09 --- /dev/null +++ b/AppKit/AppKit.steam @@ -0,0 +1,46 @@ + + + + + name + AppKit + Targets + + + name + AppKit + Excluded + + CPOutlineView.j + CPTableColumn.j + CPTableView.j + + + + Configurations + + + name + Debug + Settings + + PREPROCESS + + FLAGS + -DDEBUG + + + + name + Release + Settings + + PREPROCESS + + PREINTERPRET + + + + + + diff --git a/AppKit/CPAnimation.j b/AppKit/CPAnimation.j new file mode 100644 index 0000000000..b3a10f93fb --- /dev/null +++ b/AppKit/CPAnimation.j @@ -0,0 +1,191 @@ +/* + * CPAnimation.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import "CAMediaTimingFunction.j" + + +CPAnimationEaseInOut = 0, +CPAnimationEaseIn = 1, +CPAnimationEaseOut = 2, +CPAnimationLinear = 3; + +ACTUAL_FRAME_RATE = 0; + +@implementation CPAnimation : CPObject +{ + CPTimeInterval _startTime; + CPTimeInterval _duration; + + CPAnimationCurve _animationCurve; + CAMediaTimingFunction _timingFunction; + + float _frameRate; + CPAnimationProgress _progress; + + id _delegate; + CPTimer _timer; +} + +- (id)initWithDuration:(float)aDuration animationCurve:(CPAnimationCurve)anAnimationCurve +{ + self = [super init]; + + if (self) + { + _duration = aDuration; + _animationCurve = anAnimationCurve; + _frameRate = 60.0; + } + + return self; +} + +- (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve +{ + _animationCurve = anAnimationCurve; + + var timingFunctionName = kCAMediaTimingFunctionLinear; + + switch (_animationCurve) + { + case CPAnimationEaseInOut: timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut; + break; + + case CPAnimationEaseIn: timingFunctionName = kCAMediaTimingFunctionEaseIn; + break; + + case CPAnimationEaseOut: timingFunctionName = kCAMediaTimingFunctionEaseOut; + break; + } + + _timingFunction = [CAMediaTimingFunction functionWithName:timingFunctionName]; +} + +- (CPAnimationCurve)animationCurve +{ + return _animationCurve; +} + +- (void)setDuration:(CPTimeInterval)aDuration +{ + _duration = aDuration; +} + +- (CPTimeInterval)duration +{ + return _duration; +} + +- (void)setFramesPerSecond:(float)framesPerSecond +{ + _frameRate = framesPerSecond; +} + +- (float)frameRate +{ + return _frameRate; +} + +- (id)delegate +{ + return _delegate; +} + +- (void)setDelegate:(id)aDelegate +{ + _delegate = aDelegate; +} + +- (void)startAnimation +{ + // If we're already animating, or our delegate stops us, animate. + if (_timer || _delegate && ![_delegate animationShouldStart:self]) + return; + + _progress = 0.0; + ACTUAL_FRAME_RATE = 0; + _startTime = new Date(); + // FIXME: THIS SHOULD BE A CPTIMER!!! + _timer = window.setInterval(function() { + [self animationTimerDidFire:_timer]; + [[CPRunLoop currentRunLoop] performSelectors]; + }, 1); // must be 1ms not 0 for IE. //_duration * 1000 / _frameRate); +} + +- (void)animationTimerDidFire:(CPTimer)aTimer +{ + var elapsed = new Date() - _startTime, + progress = MIN(1.0, 1.0 - (_duration - elapsed / 1000.0) / _duration); +++ACTUAL_FRAME_RATE; + [self setCurrentProgress:progress]; + + if (progress == 1.0) + { + window.clearTimeout(_timer); + _timer = nil; + + if ([_delegate respondsToSelector:@selector(animationDidEnd:)]) + [_delegate animationDidEnd:self]; + } + +} + +- (void)stopAnimation +{ + if (!_timer) + return; + + window.clearTimeout(_timer); + _timer = nil; + + [_delegate animationDidStop:self]; +} + +- (BOOL)isAnimating +{ + return _timer; +} + +- (void)setCurrentProgress:(CPAnimationProgress)aProgress +{ + _progress = aProgress; +} + +- (CPAnimationProgress)currentProgress +{ + return _progress; +} + +- (float)currentValue +{ + if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)]) + return [_delegate animation:self valueForProgress:_progress]; + + if (_animationCurve == CPAnimationLinear) + return _progress; + + alert("IMPLEMENT ANIMATION CURVES!!!"); +} + +@end diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j new file mode 100644 index 0000000000..63b12bbaaf --- /dev/null +++ b/AppKit/CPApplication.j @@ -0,0 +1,552 @@ +/* + * CPApplication.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import "CPCompatibility.j" +import "CPEvent.j" +import "CPMenu.j" +import "CPResponder.j" +import "CPDocumentController.j" + +CPApp = nil; + +CPApplicationWillFinishLaunchingNotification = @"CPApplicationWillFinishLaunchingNotification"; +CPApplicationDidFinishLaunchingNotification = @"CPApplicationDidFinishLaunchingNotification"; + +CPRunStoppedResponse = -1000; +CPRunAbortedResponse = -1001; +CPRunContinuesResponse = -1002; + +@implementation CPApplication : CPResponder +{ + CPArray _eventListeners; + + CPEvent _currentEvent; + + CPArray _windows; + CPWindow _keyWindow; + CPWindow _mainWindow; + + CPMenu _mainMenu; + CPDocumentController _documentController; + + CPModalSession _currentSession; + + // + id _delegate; + + CPDictionary _namedArgs; + CPArray _args; +} + ++ (CPApplication)sharedApplication +{ + if (!CPApp) + CPApp = [[CPApplication alloc] init]; + + return CPApp; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + _eventListeners = []; + + _windows = []; + + [_windows addObject:nil]; + + // FIXME: This should be read from the cib. + _mainMenu = [[CPMenu alloc] initWithTitle:@"MainMenu"]; + + // FIXME: We should implement autoenabling. + [_mainMenu setAutoenablesItems:NO]; + + var bundle = [CPBundle bundleForClass:[CPApplication class]], + newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"N"]; + + [newMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/New.png"] size:CGSizeMake(16.0, 16.0)]]; + [newMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/NewHighlighted.png"] size:CGSizeMake(16.0, 16.0)]]; + + [_mainMenu addItem:newMenuItem]; + + var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"O"]; + + [openMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Open.png"] size:CGSizeMake(16.0, 16.0)]]; + [openMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/OpenHighlighted.png"] size:CGSizeMake(16.0, 16.0)]]; + + [_mainMenu addItem:openMenuItem]; + + var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"], + saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@""]; + + [saveMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Save.png"] size:CGSizeMake(16.0, 16.0)]]; + [saveMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/SaveHighlighted.png"] size:CGSizeMake(16.0, 16.0)]]; + + [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"S"]]; + [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:@""]]; + + [saveMenuItem setSubmenu:saveMenu]; + + [_mainMenu addItem:saveMenuItem]; + + var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""], + editMenu = [[CPMenu alloc] initWithTitle:@"Edit"], + + undoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:CPUndoKeyEquivalent], + redoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:CPRedoKeyEquivalent]; + + [undoMenuItem setKeyEquivalentModifierMask:CPUndoKeyEquivalentModifierMask]; + [redoMenuItem setKeyEquivalentModifierMask:CPRedoKeyEquivalentModifierMask]; + + [editMenu addItem:undoMenuItem]; + [editMenu addItem:redoMenuItem]; + + [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"X"]], + [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"C"]], + [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"V"]]; + + [editMenuItem setSubmenu:editMenu]; + [editMenuItem setHidden:YES]; + + [_mainMenu addItem:editMenuItem]; + + [_mainMenu addItem:[CPMenuItem separatorItem]]; + } + + return self; +} + +// Configuring Applications + +- (void)setDelegate:(id)aDelegate +{ + if (_delegate == aDelegate) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + if (_delegate) + { + [defaultCenter + removeObesrver:_delegate + name:CPApplicationWillFinishLaunchingNotification + object:self]; + + [defaultCenter + removeObesrver:_delegate + name:CPApplicationDidFinishLaunchingNotification + object:self]; + } + + _delegate = aDelegate; + + if ([_delegate respondsToSelector:@selector(applicationWillFinishLaunching:)]) + [defaultCenter + addObserver:_delegate + selector:@selector(applicationWillFinishLaunching:) + name:CPApplicationWillFinishLaunchingNotification + object:self]; + + if ([_delegate respondsToSelector:@selector(applicationDidFinishLaunching:)]) + [defaultCenter + addObserver:_delegate + selector:@selector(applicationDidFinishLaunching:) + name:CPApplicationDidFinishLaunchingNotification + object:self]; +} + +- (id)delegate +{ + return _delegate; +} + +- (void)finishLaunching +{ + var bundle = [CPBundle mainBundle], + types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"]; + + if ([types count] > 0) + _documentController = [CPDocumentController sharedDocumentController]; + + var delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"]; + + if (delegateClassName) + { + var delegateClass = objj_getClass(delegateClassName); + + if (delegateClass) + if ([_documentController class] == delegateClass) + [self setDelegate:_documentController]; + else + [self setDelegate:[[delegateClass alloc] init]]; + } + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + [defaultCenter + postNotificationName:CPApplicationWillFinishLaunchingNotification + object:self]; + + if (_documentController) + [_documentController newDocument:self]; + + [defaultCenter + postNotificationName:CPApplicationDidFinishLaunchingNotification + object:self]; + + [[CPRunLoop currentRunLoop] performSelectors]; +} + +- (void)run +{ + [self finishLaunching]; +} + +// Managing the Event Loop + +- (void)runModalForWindow:(CPWindow)aWindow +{ + [self runModalSession:[self beginModalSessionForWindow:aWindow]]; +} + +- (void)stopModalWithCode:(int)aCode +{ + if (!_currentSession) + { + return; + // raise exception; + } + + _currentSession._state = aCode; + _currentSession = _currentSession._previous; + + if (aCode == CPRunAbortedResponse) + [self _removeRunModalLoop]; +} + +- (void)_removeRunModalLoop +{ + var count = _eventListeners.length; + + while (count--) + if (_eventListeners[count]._callback == _CPRunModalLoop) + { + _eventListeners.splice(count, 1); + + return; + } +} + +- (void)stopModal +{ + [self stopModalWithCode:CPRunStoppedResponse] +} + +- (void)abortModal +{ + [self stopModalWithCode:CPRunAbortedResponse]; +} + +- (CPModalSession)beginModalSessionForWindow:(CPWindow)aWindow +{ + return _CPModalSessionMake(aWindow, 0); +} + +- (void)runModalSession:(CPModalSession)aModalSession +{ + aModalSession._previous = _currentSession; + _currentSession = aModalSession; + + var theWindow = aModalSession._window; + + [theWindow center]; + [theWindow makeKeyAndOrderFront:self]; + +// [theWindow._bridge _obscureWindowsBelowModalWindow]; + + [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO]; +} + +- (CPWindow)modalWindow +{ + if (!_currentSession) + return nil; + + return _currentSession._window; +} + +- (BOOL)_handleKeyEquivalent:(CPEvent)anEvent +{ + if ([_mainMenu performKeyEquivalent:anEvent]) + return YES; + + return NO; +} + +- (void)sendEvent:(CPEvent)anEvent +{ + // Check if this is a candidate for key equivalent... + if ([anEvent type] == CPKeyDown && + [anEvent modifierFlags] & (CPCommandKeyMask | CPControlKeyMask) && + [[anEvent characters] length] > 0 && + [self _handleKeyEquivalent:anEvent]) + return; + + if (_eventListeners.length) + { + if (_eventListeners[_eventListeners.length - 1]._mask & (1 << [anEvent type])) + _eventListeners.pop()._callback(anEvent); + + return; + } + + [[anEvent window] sendEvent:anEvent]; +} + +- (void)doCommandBySelector:(SEL)aSelector +{ + if ([_delegate respondsToSelector:aSelector]) + [_delegate performSelector:aSelector]; + else + [super doCommandBySelector:aSelector]; +} + +- (CPWindow)keyWindow +{ + return _keyWindow; +} + +- (CPWindow)mainWindow +{ + return _mainWindow; +} + +- (CPWindow)windowWithWindowNumber:(int)aWindowNumber +{ + return _windows[aWindowNumber]; +} + +- (CPArray)windows +{ + return _windows; +} + +// Accessing the Main Menu + +- (CPMenu)mainMenu +{ + return _mainMenu; +} + +- (void)setMainMenu:(CPMenu)aMenu +{ + _mainMenu = aMenu; +} + +// Posting Actions + +- (BOOL)tryToPerform:(SEL)anAction with:(id)anObject +{ + if (!anAction) + return NO; + + if ([self tryToPerform:anAction with:anObject]) + return YES; + + if([_delegate respondsToSelector:aSelector]) + { + [_delegate performSelector:aSelector withObject:anObject]; + + return YES; + } + + return NO; +} + +- (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender +{ + var target = [self targetForAction:anAction to:aTarget from:aSender]; + + if (!target) + return NO; + + [target performSelector:anAction withObject:aSender]; + + return YES; +} + +- (id)targetForAction:(SEL)anAction to:(id)aTarget from:(id)aSender +{ + if (!anAction) + return nil; + + if (aTarget) + return aTarget; + + return [self targetForAction:anAction]; +} + +- (id)_targetForWindow:(CPWindow)aWindow action:(SEL)anAction +{ + var responder = [aWindow firstResponder], + checkWindow = YES; + + while (responder) + { + if ([responder respondsToSelector:anAction]) + return responder; + + if (responder == aWindow) + checkWindow = NO; + + responder = [responder nextResponder]; + } + + if (checkWindow && [aWindow respondsToSelector:anAction]) + return aWindow; + + var delegate = [aWindow delegate]; + + if ([aWindow respondsToSelector:anAction]) + return delegate; + + var windowController = [aWindow windowController]; + + if ([windowController respondsToSelector:anAction]) + return windowController; + + var theDocument = [windowController document]; + + if (theDocument != delegate && [theDocument respondsToSelector:anAction]) + return theDocument; + + return nil; +} + +- (id)targetForAction:(SEL)anAction +{ + if (!anAction) + return nil; + + var target = [self _targetForWindow:[self keyWindow] action:anAction]; + + if (target) + return target; + + target = [self _targetForWindow:[self mainWindow] action:anAction]; + + if (target) + return target; + + if ([self respondsToSelector:anAction]) + return self; + + if ([_delegate respondsToSelector:anAction]) + return _delegate; + + if ([_documentController respondsToSelector:anAction]) + return _documentController; + + return nil; +} + +- (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue +{ + _eventListeners.push(_CPEventListenerMake(aMask, aCallback)); + + if (_eventListeners.length == 3) objj_debug_print_backtrace(); +} + +- (CPEvent)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue +{ + _eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); })); +} + +// Managing Sheets + +- (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo +{ + [aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo]; +} + +- (CPArray)arguments +{ + return _args; +} + +- (CPDictionary)namedArguments +{ + return _namedArgs; +} + +@end + +var _CPModalSessionMake = function(aWindow, aStopCode) +{ + return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil }; +} + +var _CPEventListenerMake = function(anEventMask, aCallback) +{ + return { _mask:anEventMask, _callback:aCallback }; +} + +var _CPRunModalLoop = function(anEvent) +{ + [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO]; + + // FIXME: abortModal from event loop? + var theWindow = [anEvent window], + modalSession = CPApp._currentSession; + + if (theWindow == modalSession._window || [theWindow worksWhenModal]) + [theWindow sendEvent:anEvent]; + /*else + [[session modalWindow] makeKeyAndOrderFront:]*/ + + if (modalSession._state != CPRunContinuesResponse) + [CPApp _removeRunModalLoop]; +} + +function CPApplicationMain(args, namedArgs) +{ + var mainBundle = [CPBundle mainBundle], + principalClass = [mainBundle principalClass]; + + if (!principalClass) + principalClass = [CPApplication class]; + + [principalClass sharedApplication]; + + //[NSBundle loadNibNamed:@"myMain" owner:NSApp]; + + //FIXME? + CPApp._args = args; + CPApp._namedArgs = namedArgs; + + [CPApp run]; +} diff --git a/AppKit/CPButton.j b/AppKit/CPButton.j new file mode 100644 index 0000000000..5a4f1ed911 --- /dev/null +++ b/AppKit/CPButton.j @@ -0,0 +1,604 @@ +/* + * CPButton.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "_CPImageAndTitleView.j" +import "CGGeometry.j" + +import "CPControl.j" + +#include "CoreGraphics/CGGeometry.h" + + +CPScaleProportionally = 0; +CPScaleToFit = 1; +CPScaleNone = 2; + +CPNoImage = 0; +CPImageOnly = 1; +CPImageLeft = 2; +CPImageRight = 3; +CPImageBelow = 4; +CPImageAbove = 5; +CPImageOverlaps = 6; + +CPOnState = 1; +CPOffState = 0; +CPMixedState = -1; + +CPRoundedBezelStyle = 1; +CPRegularSquareBezelStyle = 2; +CPThickSquareBezelStyle = 3; +CPThickerSquareBezelStyle = 4; +CPDisclosureBezelStyle = 5; +CPShadowlessSquareBezelStyle = 6; +CPCircularBezelStyle = 7; +CPTexturedSquareBezelStyle = 8; +CPHelpButtonBezelStyle = 9; +CPSmallSquareBezelStyle = 10; +CPTexturedRoundedBezelStyle = 11; +CPRoundRectBezelStyle = 12; +CPRecessedBezelStyle = 13; +CPRoundedDisclosureBezelStyle = 14; +CPHUDBezelStyle = -1; + +CPMomentaryLightButton = 0; +CPPushOnPushOffButton = 1; +CPToggleButton = 2; +CPSwitchButton = 3; +CPRadioButton = 4; +CPMomentaryChangeButton = 5; +CPOnOffButton = 6; +CPMomentaryPushInButton = 7; +CPMomentaryPushButton = 0; +CPMomentaryLight = 7; + + +var CPHUDBezelStyleTextColor = nil; + +var _CPButtonClassName = nil, + _CPButtonBezelStyleSizes = {}, + _CPButtonBezelStyleIdentifiers = {}, + _CPButtonBezelStyleHighlightedIdentifier = @"Highlighted"; + +@implementation CPButton : CPControl +{ + int _tag; + int _state; + BOOL _allowsMixedState; + BOOL _isHighlighted; + + CPImage _image; + CPImage _alternateImage; + + CPCellImagePosition _imagePosition; + CPImageScaling _imageScalng; + + CPString _title; + CPString _alternateTitle; + + CPBezelStyle _bezelStyle; + BOOL _isBordered; + CPControlSize _controlSize; + + BOOL _bezelBorderNeedsUpdate; + + _CPImageAndTitleView _imageAndTitleView; +} + ++ (void)initialize +{ + if (self != [CPButton class]) + return; + + _CPButtonClassName = [CPButton className]; + + // Textured Rounded + _CPButtonBezelStyleIdentifiers[CPRoundedBezelStyle] = @"Rounded"; + _CPButtonBezelStyleIdentifiers[CPRegularSquareBezelStyle] = @"RegularSquare"; + _CPButtonBezelStyleIdentifiers[CPThickSquareBezelStyle] = @"ThickSquare"; + _CPButtonBezelStyleIdentifiers[CPThickerSquareBezelStyle] = @"ThickerSquare"; + _CPButtonBezelStyleIdentifiers[CPDisclosureBezelStyle] = @"Disclosure"; + _CPButtonBezelStyleIdentifiers[CPShadowlessSquareBezelStyle] = @"ShadowlessSquare"; + _CPButtonBezelStyleIdentifiers[CPCircularBezelStyle] = @"Circular"; + _CPButtonBezelStyleIdentifiers[CPTexturedSquareBezelStyle] = @"TexturedSquare"; + _CPButtonBezelStyleIdentifiers[CPHelpButtonBezelStyle] = @"HelpButton"; + _CPButtonBezelStyleIdentifiers[CPSmallSquareBezelStyle] = @"SmallSquare"; + _CPButtonBezelStyleIdentifiers[CPTexturedRoundedBezelStyle] = @"TexturedRounded"; + _CPButtonBezelStyleIdentifiers[CPRoundRectBezelStyle] = @"RoundRect"; + _CPButtonBezelStyleIdentifiers[CPRecessedBezelStyle] = @"Recessed"; + _CPButtonBezelStyleIdentifiers[CPRoundedDisclosureBezelStyle] = @"RoundedDisclosure"; + _CPButtonBezelStyleIdentifiers[CPHUDBezelStyle] = @"HUD"; + + var regularIdentifier = _CPControlIdentifierForControlSize(CPRegularControlSize), + smallIdentifier = _CPControlIdentifierForControlSize(CPSmallControlSize), + miniIdentifier = _CPControlIdentifierForControlSize(CPMiniControlSize); + + // Rounded Rect + var prefix = _CPButtonClassName + _CPButtonBezelStyleIdentifiers[CPRoundRectBezelStyle]; + + _CPButtonBezelStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(10.0, 18.0), _CGSizeMake(1.0, 18.0), _CGSizeMake(10.0, 18.0)]; + _CPButtonBezelStyleSizes[prefix + regularIdentifier + _CPButtonBezelStyleHighlightedIdentifier] = [_CGSizeMake(10.0, 18.0), _CGSizeMake(1.0, 18.0), _CGSizeMake(10.0, 18.0)]; + + // HUD + var prefix = _CPButtonClassName + _CPButtonBezelStyleIdentifiers[CPHUDBezelStyle]; + + _CPButtonBezelStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(13.0, 20.0), _CGSizeMake(1.0, 20.0), _CGSizeMake(13.0, 20.0)]; + _CPButtonBezelStyleSizes[prefix + regularIdentifier + _CPButtonBezelStyleHighlightedIdentifier] = [_CGSizeMake(13.0, 20.0), _CGSizeMake(1.0, 20.0), _CGSizeMake(13.0, 20.0)]; + + CPHUDBezelStyleTextColor = [CPColor whiteColor]; + + // Textured Rounded + var prefix = _CPButtonClassName + _CPButtonBezelStyleIdentifiers[CPTexturedRoundedBezelStyle]; + + _CPButtonBezelStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(7.0, 20.0), _CGSizeMake(1.0, 20.0), _CGSizeMake(7.0, 20.0)]; + _CPButtonBezelStyleSizes[prefix + regularIdentifier + _CPButtonBezelStyleHighlightedIdentifier] = [_CGSizeMake(7.0, 20.0), _CGSizeMake(1.0, 20.0), _CGSizeMake(7.0, 20.0)]; +} + +// Configuring Buttons + +- (void)setButtonType:(CPButtonType)aButtonType +{ + if (aButtonType == CPSwitchButton) + { + [self setBordered:NO]; + [self setImage:nil]; + [self setAlternateImage:nil]; + [self setAlignment:CPLeftTextAlignment]; + } +} + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _imagePosition = CPNoImage; + _imageScaling = CPScaleNone; + + _controlSize = CPRegularControlSize; + + [self setBezelStyle:CPRoundRectBezelStyle]; + [self setBordered:YES]; + + [self setAlignment:CPCenterTextAlignment]; + } + + return self; +} + +- (void)setImagePosition:(CPCellImagePosition)anImagePosition +{ + if (_imagePosition == anImagePosition) + return; + + _imagePosition = anImagePosition; + + [self drawContentsWithHighlight:_isHighlighted]; +} + +- (CPCellImagePosition)imagePosition +{ + return _imagePosition; +} + +- (void)setImageScaling:(CPImageScaling)anImageScaling +{ + if (_imageScaling == anImageScaling) + return; + + _imageScaling = anImageScaling; + + [self drawContentsWithHighlight:_isHighlighted]; +} + +- (CPImageScaling)imageScaling +{ + return _imageScaling; +} + +- (void)setTextColor:(CPColor)aColor +{ + [super setTextColor:aColor]; + + [self drawContentsWithHighlight:_isHighlighted]; +} + +- (void)setFont:(CPFont)aFont +{ + [super setFont:aFont]; + + [self drawContentsWithHighlight:_isHighlighted]; +} + +// Setting the state + +- (BOOL)allowsMixedState +{ + return _allowsMixedState; +} + +- (void)setAllowsMixedState:(BOOL)aFlag +{ + _allowsMixedState = aFlag; +} + +- (void)setNextState +{ + if (_state == CPOffState) + _state = CPOnState; + else + _state = (_state >= CPOnState && _allowsMixedState) ? CPMixedState : CPOffState; +} + +- (void)setState:(int)aState +{ + _state = aState; +} + +- (int)state +{ + return _state; +} + +- (void)setAlignment:(CPTextAlignment)anAlignment +{ + [super setAlignment:anAlignment]; + + [self drawContentsWithHighlight:_isHighlighted]; +} + +- (void)setImage:(CPImage)anImage +{ + if (_image == anImage) + return; + + _image = anImage; + + [self drawContentsWithHighlight:_isHighlighted]; +} + +- (CPImage)image +{ + return _image; +} + +- (void)setAlternateImage:(CPImage)anImage +{ + _alternateImage = anImage; +} + +- (CPImage)alternateImage +{ + return _alternateImage; +} + +- (void)setTitle:(CPString)aTitle +{ + if (_title == aTitle) + return; + + _title = aTitle; + + [self drawContentsWithHighlight:_isHighlighted]; +} + +- (CPString)title +{ + return _title; +} + +- (void)tile +{ + var size = [self bounds].size; + + if (_isBordered) + { + var imageAndTitleSize = CGSizeMakeCopy(size); + + if (_bezelStyle == CPHUDBezelStyle) + imageAndTitleSize.height -= 4.0; + else if (_bezelStyle == CPRoundRectBezelStyle) + imageAndTitleSize.height -= 2.0; + else if (_bezelStyle == CPTexturedRoundedBezelStyle) + imageAndTitleSize.height -= 2.0; + + [_imageAndTitleView setFrameSize:imageAndTitleSize]; + } + else + [_imageAndTitleView setFrameSize:size]; +} + +- (void)sizeToFit +{ + [_imageAndTitleView sizeToFit]; + + var frame = [_imageAndTitleView frame], + height = CGRectGetHeight(frame); + /* + if (_isBordered) + if (_bezelStyle == CPHUDBezelStyle) + height += 2.0; + else if (_bezelStyle == CPRoundRectBezelStyle) + height += 1.0; + else if (_bezelStyle == CPTexturedRoundedBezelStyle) + height += 2.0; + */ + [self setFrameSize:CGSizeMake(CGRectGetWidth(frame), height)]; +} + +- (void)setFrameSize:(CPSize)aSize +{ + [super setFrameSize:aSize]; + + [self tile]; +} + +- (void)highlight:(BOOL)aFlag +{ + [self drawBezelWithHighlight:aFlag]; + [self drawContentsWithHighlight:aFlag]; +} + +- (void)setTag:(int)aTag +{ + _tag = aTag; +} + +- (int)tag +{ + return _tag; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + _isHighlighted = YES; + + [self highlight:_isHighlighted]; +} + +- (void)mouseDragged:(CPEvent)anEvent +{ + _isHighlighted = CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]); + + [self highlight:_isHighlighted]; +} + +- (void)mouseUp:(CPEvent)anEvent +{ + _isHighlighted = NO; + + [self highlight:_isHighlighted]; + + [super mouseUp:anEvent]; +} + +// FIXME: This probably belongs in CPControl. +- (void)setControlSize:(CPControlSize)aControlSize +{ + if (_controlSize == aControlSize) + return; + + _controlSize = aControlSize; + + [self drawBezelWithHighlight:_isHighlighted]; + [self _updateTextAttributes]; +} + +- (CPControlSize)controlSize +{ + return _controlSize; +} + +- (void)setBordered:(BOOL)isBordered +{ + if (_isBordered == isBordered) + return; + + _isBordered = isBordered; + + [self updateBackgroundColors]; + [self drawBezelWithHighlight:_isHighlighted]; + + [self tile]; +} + +- (BOOL)isBordered +{ + return _isBordered; +} + +- (void)setBezelStyle:(CPBezelStyle)aBezelStyle +{ + // FIXME: We need real support for these: + if (aBezelStyle == CPRoundedBezelStyle || + aBezelStyle == CPRoundedBezelStyle || + aBezelStyle == CPRegularSquareBezelStyle || + aBezelStyle == CPThickSquareBezelStyle || + aBezelStyle == CPThickerSquareBezelStyle || + aBezelStyle == CPDisclosureBezelStyle || + aBezelStyle == CPShadowlessSquareBezelStyle || + aBezelStyle == CPCircularBezelStyle || + aBezelStyle == CPTexturedSquareBezelStyle || + aBezelStyle == CPHelpButtonBezelStyle || + aBezelStyle == CPSmallSquareBezelStyle || + aBezelStyle == CPRecessedBezelStyle || + aBezelStyle == CPRoundedDisclosureBezelStyle) + aBezelStyle = CPRoundRectBezelStyle; + + if (_bezelStyle == aBezelStyle) + return; + + _bezelStyle = aBezelStyle; + + [self updateBackgroundColors]; + [self drawBezelWithHighlight:_isHighlighted]; + + [self _updateTextAttributes]; + [self tile]; +} + +- (int)bezelStyle +{ + return _bezelStyle; +} + +- (void)updateBackgroundColors +{ + if (_isBordered) + { + [self setBackgroundColor:_CPControlThreePartImagePattern( + NO, + _CPButtonBezelStyleSizes, + _CPButtonClassName, + _CPButtonBezelStyleIdentifiers[_bezelStyle], + _CPControlIdentifierForControlSize(_controlSize)) forName:CPControlNormalBackgroundColor]; + + [self setBackgroundColor:_CPControlThreePartImagePattern( + NO, + _CPButtonBezelStyleSizes, + _CPButtonClassName, + _CPButtonBezelStyleIdentifiers[_bezelStyle], + _CPControlIdentifierForControlSize(_controlSize), + _CPButtonBezelStyleHighlightedIdentifier) forName:CPControlHighlightedBackgroundColor]; + } + else + { + [self setBackgroundColor:nil forName:CPControlNormalBackgroundColor]; + [self setBackgroundColor:nil forName:CPControlHighlightedBackgroundColor]; + } +} + +- (void)drawBezelWithHighlight:(BOOL)shouldHighlight +{ + _bezelBorderNeedsUpdate = ![self window]; + + if (_bezelBorderNeedsUpdate) + return; + + [self setBackgroundColorWithName:shouldHighlight ? CPControlHighlightedBackgroundColor : CPControlNormalBackgroundColor]; +} + +- (void)drawContentsWithHighlight:(BOOL)isHighlighted +{ + if (!_title && !_image && !_alternateTitle && !_alternateImage && !_imageAndTitleView) + return; + + if (!_imageAndTitleView) + { + _imageAndTitleView = [[_CPImageAndTitleView alloc] initWithFrame:[self bounds]]; + + [self addSubview:_imageAndTitleView]; + + [self tile]; + } + + [_imageAndTitleView setFont:[self font]]; + [_imageAndTitleView setTextColor:[self textColor]]; + [_imageAndTitleView setAlignment:[self alignment]]; + [_imageAndTitleView setImagePosition:_imagePosition]; + [_imageAndTitleView setImageScaling:_imageScaling]; + + [_imageAndTitleView setTitle:isHighlighted && _alternateTitle ? _alternateTitle : _title]; + [_imageAndTitleView setImage:isHighlighted && _alternateImage ? _alternateImage : _image]; +} + +- (void)viewDidMoveToWindow +{ + if (_bezelBorderNeedsUpdate) + [self drawBezelWithHighlight:_isHighlighted]; +} + +- (void)_updateTextAttributes +{ + if (_bezelStyle == CPHUDBezelStyle) + [self setTextColor:CPHUDBezelStyleTextColor]; + + if (_controlSize == CPRegularControlSize) + [self setFont:[CPFont systemFontOfSize:11.0]]; +} + +@end + + +var CPButtonImageKey = @"CPButtonImageKey", + CPButtonAlternateImageKey = @"CPButtonAlternateImageKey", + CPButtonTitleKey = @"CPButtonTitleKey", + CPButtonAlteranteTitleKey = @"CPButtonAlternateTitleKey", + CPButtonImageAndTitleViewKey = @"CPButtonImageAndTitleViewKey", + CPButtonImagePositionKey = @"CPButtonImagePositionKey", + CPButtonImageScalingKey = @"CPButtonImageScalingKey", + CPButtonIsBorderedKey = @"CPButtonIsBorderedKey", + CPButtonBezelStyleKey = @"CPButtonBezelStyleKey", + CPButtonImageAndTitleViewKey = @"CPButtonImageAndTitleViewKey"; + +@implementation CPButton (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + _controlSize = CPRegularControlSize; + + [self setImage:[aCoder decodeObjectForKey:CPButtonImageKey]]; + [self setAlternateImage:[aCoder decodeObjectForKey:CPButtonAlternateImageKey]]; + + [self setTitle:[aCoder decodeObjectForKey:CPButtonTitleKey]]; + + [self setImagePosition:[aCoder decodeIntForKey:CPButtonImagePositionKey]]; + [self setImageScaling:[aCoder decodeIntForKey:CPButtonImageScalingKey]]; + + [self setBezelStyle:[aCoder decodeIntForKey:CPButtonBezelStyleKey]]; + [self setBordered:[aCoder decodeBoolForKey:CPButtonIsBorderedKey]]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + // We do this in order to avoid encoding the _imageAndTitleView, which + // should just automatically be created programmatically as needed. + var actualSubviews = _subviews; + + _subviews = [_subviews copy]; + [_subviews removeObjectIdenticalTo:_imageAndTitleView]; + + [super encodeWithCoder:aCoder]; + + _subviews = actualSubviews; + + [aCoder encodeObject:_image forKey:CPButtonImageKey]; + [aCoder encodeObject:_alternateImage forKey:CPButtonAlternateImageKey]; + + [aCoder encodeObject:_title forKey:CPButtonTitleKey]; + + [aCoder encodeInt:_imagePosition forKey:CPButtonImagePositionKey]; + [aCoder encodeInt:_imageScaling forKey:CPButtonImageScalingKey]; + + [aCoder encodeBool:_isBordered forKey:CPButtonIsBorderedKey]; + [aCoder encodeInt:_bezelStyle forKey:CPButtonBezelStyleKey]; +} + +@end diff --git a/AppKit/CPClipView.j b/AppKit/CPClipView.j new file mode 100644 index 0000000000..f32acecd8b --- /dev/null +++ b/AppKit/CPClipView.j @@ -0,0 +1,130 @@ +/* + * CPClipView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPView.j" + +#include "CoreGraphics/CGGeometry.h" + + +@implementation CPClipView : CPView +{ + CPView _documentView; +} + +- (void)setDocumentView:(CPView)aView +{ + if (_documentView == aView) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + if (_documentView) + { + [defaultCenter + removeObserver:self + name:CPViewFrameDidChangeNotification + object:_documentView]; + + [defaultCenter + removeObserver:self + name:CPViewBoundsDidChangeNotification + object:_documentView]; + + [_documentView removeFromSuperview]; + } + + _documentView = aView; + + if (_documentView) + { + // FIXME: remove when bounds. + [_documentView setFrameOrigin:CGPointMake(0.0, 0.0)]; + + [self addSubview:_documentView]; + + [_documentView setPostsFrameChangedNotifications:YES]; + [_documentView setPostsBoundsChangedNotifications:YES]; + + [defaultCenter + addObserver:self + selector:@selector(viewFrameChanged:) + name:CPViewFrameDidChangeNotification + object:_documentView]; + + [defaultCenter + addObserver:self + selector:@selector(viewBoundsChanged:) + name:CPViewBoundsDidChangeNotification + object:_documentView]; + } +} + +- (id)documentView +{ + return _documentView; +} + +- (CGPoint)constrainScrollPoint:(CGPoint)aPoint +{ + var documentFrame = [_documentView frame]; + + aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(_bounds), 0.0))); + aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(_CGRectGetHeight(documentFrame) - _CGRectGetHeight(_bounds), 0.0))); + + return aPoint; +} + +- (void)setBoundsOrigin:(CGPoint)aPoint +{ + if (_CGPointEqualToPoint(_bounds.origin, aPoint)) + return; + + [super setBoundsOrigin:aPoint]; + + var superview = [self superview]; + + if([superview isKindOfClass:[CPScrollView class]]) + [superview reflectScrolledClipView:self]; +} + +- (void)scrollToPoint:(CPPoint)aPoint +{ + [self setBoundsOrigin:[self constrainScrollPoint:aPoint]]; +} + +- (void)viewBoundsChanged:(CPNotification)aNotification +{ + var superview = [self superview]; + + if([superview isKindOfClass:[CPScrollView class]]) + [superview reflectScrolledClipView:self]; +} + +- (void)viewFrameChanged:(CPNotification)aNotification +{ + var superview = [self superview]; + + if([superview isKindOfClass:[CPScrollView class]]) + [superview reflectScrolledClipView:self]; +} + +@end diff --git a/AppKit/CPCollectionView.j b/AppKit/CPCollectionView.j new file mode 100644 index 0000000000..111369a754 --- /dev/null +++ b/AppKit/CPCollectionView.j @@ -0,0 +1,546 @@ +/* + * CPCollectionView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import +import +import + +import + + +@implementation CPCollectionView : CPView +{ + CPArray _content; + CPArray _items; + + CPData _itemData; + CPCollectionViewItem _itemPrototype; + CPCollectionViewItem _itemForDragging; + CPMutableArray _cachedItems; + + unsigned _maxNumberOfRows; + unsigned _maxNumberOfColumns; + + CGSize _minItemSize; + CGSize _maxItemSize; + + float _tileWidth; + + BOOL _isSelectable; + BOOL _allowsMultipleSelection; + CPIndexSet _selectionIndexes; + + CGSize _itemSize; + + float _horizontalMargin; + float _verticalMargin; + + unsigned _numberOfRows; + unsigned _numberOfColumns; + + id _delegate; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _items = []; + _content = []; + + _cachedItems = []; + + _itemSize = CGSizeMakeZero(); + _minItemSize = CGSizeMakeZero(); + _maxItemSize = CGSizeMakeZero(); + + _verticalMargin = 5.0; + _tileWidth = -1.0; + + _selectionIndexes = [CPIndexSet indexSet]; + } + + return self; +} + +- (void)setItemPrototype:(CPCollectionViewItem)anItem +{ + _itemData = [CPKeyedArchiver archivedDataWithRootObject:anItem]; + _itemForDragging = anItem//[CPKeyedUnarchiver unarchiveObjectWithData:_itemData]; + + [self reloadContent]; +} + +- (CPCollectionViewItem)itemPrototype +{ + return _itemPrototype; +} + +- (CPCollectionViewItem)newItemForRepresentedObject:(id)anObject +{ + var item = nil; + + if (_cachedItems.length) + item = _cachedItems.pop(); + else + item = [CPKeyedUnarchiver unarchiveObjectWithData:_itemData]; + + [item setRepresentedObject:anObject]; + [[item view] setFrameSize:_itemSize]; + + return item; +} + +// Working with the Responder Chain + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)isFirstResponder +{ + return [[self window] firstResponder] == self; +} + +// Setting the Content + +- (void)setContent:(CPArray)anArray +{ + if (_content == anArray) + return; + + _content = anArray; + + [self reloadContent]; +} + +- (CPArray)content +{ + return _content; +} + +- (CPArray)items +{ + return _items; +} + +// Setting the Selection Mode + +- (void)setSelectable:(BOOL)isSelectable +{ + if (_isSelectable == isSelectable) + return; + + _isSelectable = isSelectable; + + if (!_isSelectable) + { + var index = CPNotFound; + + while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound) + [_items[index] setSelected:NO]; + } +} + +- (BOOL)isSelected +{ + return _isSelected; +} + +- (void)setAllowsMultipleSelection:(BOOL)shouldAllowMultipleSelection +{ + _allowsMultipleSelection = shouldAllowsMultipleSelection; +} + +- (BOOL)allowsMultipleSelection +{ + return _allowsMultipleSelection; +} + +- (void)setSelectionIndexes:(CPIndexSet)anIndexSet +{ + if (_selectionIndexes == anIndexSet) + return; + + var index = CPNotFound; + + while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound) + [_items[index] setSelected:NO]; + + _selectionIndexes = anIndexSet; + + var index = CPNotFound; + + while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound) + [_items[index] setSelected:YES]; + + if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)]) + [_delegate collectionViewDidChangeSelection:self] +} + +- (CPIndexSet)selectionIndexes +{ + return _selectionIndexes; +} + +- (void)reloadContent +{ + // Remove current views + var count = _items.length; + + while (count--) + { + [[_items[count] view] removeFromSuperview]; + _cachedItems.push(_items[count]); + } + + _items = []; + + if (!_itemData || !_content) + return; + + var index = 0; + + count = _content.length; + + for (; index < count; ++index) + { + _items.push([self newItemForRepresentedObject:_content[index]]); + + [self addSubview:[_items[index] view]]; + } + + [self tile]; +} + +- (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, + itemsNeedSizeUpdate = NO; + + if (remaining > 0 && itemSize.width < _maxItemSize.width) + itemSize.width = MIN(_maxItemSize.width, itemSize.width + FLOOR(remaining / _numberOfColumns)); + + if (!CGSizeEqualToSize(_itemSize, itemSize)) + { + _itemSize = itemSize; + itemsNeedSizeUpdate = YES; + } + + var index = 0, + count = _items.length; + + if (_maxNumberOfColumns > 0 && _maxNumberOfRows > 0) + count = MIN(count, _maxNumberOfColumns * _maxNumberOfRows); + + _numberOfRows = CEIL(count / _numberOfColumns); + + _horizontalMargin = FLOOR((width - _numberOfColumns * itemSize.width) / (_numberOfColumns + 1)); + + var x = _horizontalMargin, + y = -itemSize.height; + + for (; index < count; ++index) + { + if (index % _numberOfColumns == 0) + { + x = _horizontalMargin; + y += _verticalMargin + itemSize.height; + } + + var view = [_items[index] view]; + + [view setFrameOrigin:CGPointMake(x, y)]; + + if (itemsNeedSizeUpdate) + [view setFrameSize:_itemSize]; + + x += itemSize.width + _horizontalMargin; + } + + _tileWidth = width; + [self setFrameSize:CGSizeMake(width, y + itemSize.height + _verticalMargin)]; + _tileWidth = -1.0; +} + +- (void)resizeSubviewsWithOldSize:(CGSize)aSize +{ + [self tile]; +} + +// Laying Out the Collection View + +- (void)setMaxNumberOfRows:(unsigned)aMaxNumberOfRows +{ + if (_maxNumberOfRows == aMaxNumberOfRows) + return; + + _maxNumberOfRows = aMaxNumberOfRows; + + [self tile]; +} + +- (unsigned)maxNumberOfRows +{ + return _maxNumberOfRows; +} + +- (void)setMaxNumberOfColumns:(unsigned)aMaxNumberOfColumns +{ + if (_maxNumberOfColumns == aMaxNumberOfColumns) + return; + + _maxNumberOfColumns = aMaxNumberOfColumns; + + [self tile]; +} + +- (unsigned)maxNumberOfColumns +{ + return _maxNumberOfColumns; +} + +- (void)setMinItemSize:(CGSize)aSize +{ + if (CGSizeEqualToSize(_minItemSize, aSize)) + return; + + _minItemSize = CGSizeMakeCopy(aSize); + + [self tile]; +} + +- (CGSize)minItemSize +{ + return _minItemSize; +} + +- (void)setMaxItemSize:(CGSize)aSize +{ + if (CGSizeEqualToSize(_maxItemSize, aSize)) + return; + + _maxItemSize = CGSizeMakeCopy(aSize); + + [self tile]; +} + +- (CGSize)maxItemSize +{ + return _maxItemSize; +} + +- (void)mouseUp:(CPEvent)anEvent +{ + if ([_selectionIndexes count] && [anEvent clickCount] == 2 && [_delegate respondsToSelector:@selector(collectionView:didDoubleClickOnItemAtIndex:)]) + [_delegate collectionView:self didDoubleClickOnItemAtIndex:[_selectionIndexes firstIndex]]; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + var location = [self convertPoint:[anEvent locationInWindow] fromView:nil], + row = FLOOR(location.y / (_itemSize.height + _verticalMargin)), + column = FLOOR(location.x / (_itemSize.width + _horizontalMargin)), + index = row * _numberOfColumns + column; + + if (index >= 0 && index < _items.length) + [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]]; +} + +- (void)mouseDragged:(CPEvent)anEvent +{ + if (![_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)]) + return; + + // If we don't have any selected items, we've clicked away, and thus the drag is meaningless. + if (![_selectionIndexes count]) + return; + + // Set up the pasteboard + var dragTypes = [_delegate collectionView:self dragTypesForItemsAtIndexes:_selectionIndexes]; + + [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:dragTypes owner:self]; + + var point = [self convertPoint:[anEvent locationInWindow] fromView:nil]; + + [_itemForDragging setRepresentedObject:_content[[_selectionIndexes firstIndex]]]; + + var view = [_itemForDragging view], + frame = [view frame]; + + [view setFrameSize:_itemSize]; + + [self dragView:view + at:[[_items[[_selectionIndexes firstIndex]] view] frame].origin + offset:CGPointMakeZero() + event:anEvent + pasteboard:nil + source:self + slideBack:YES]; +} + +- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType +{ + [aPasteboard setData:[_delegate collectionView:self dataForItemsAtIndexes:_selectionIndexes forType:aType] forType:aType]; +} + +// Cappuccino Additions + +- (void)setVerticalMargin:(float)aVerticalMargin +{ + if (_verticalMargin == aVerticalMargin) + return; + + _verticalMargin = aVerticalMargin; + + [self tile]; +} + +- (void)setDelegate:(id)aDelegate +{ + _delegate = aDelegate; +} + +- (id)delegate +{ + return _delegate; +} + +@end + +@implementation CPCollectionViewItem : CPObject +{ + id _representedObject; + + CPView _view; + + BOOL _isSelected; +} + +// Setting the Represented Object + +- (void)setRepresentedObject:(id)anObject +{ + if (_representedObject == anObject) + return; + + _representedObject = anObject; + + // FIXME: This should be set up by bindings + [_view setRepresentedObject:anObject]; +} + +- (id)representedObject +{ + return _representedObject; +} + +// Modifying the View + +- (void)setView:(CPView)aView +{ + _view = aView; +} + +- (CPView)view +{ + return _view; +} + +// Modifying the Selection + +- (void)setSelected:(BOOL)shouldBeSelected +{ + if (_isSelected == shouldBeSelected) + return; + + _isSelected = shouldBeSelected; + + // FIXME: This should be set up by bindings + [_view setSelected:_isSelected]; +} + +- (BOOL)isSelected +{ + return _isSelected; +} + +// Parent Collection View + +- (CPCollectionView)collectionView +{ + return [_view superview]; +} + +@end + +var CPCollectionViewItemViewKey = @"CPCollectionViewItemViewKey"; + +@implementation CPCollectionViewItem (CPCoding) + +- (id)copy +{ + +} + +@end + +var CPCollectionViewItemViewKey = @"CPCollectionViewItemViewKey"; + +@implementation CPCollectionViewItem (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + _view = [aCoder decodeObjectForKey:CPCollectionViewItemViewKey]; + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_view forKey:CPCollectionViewItemViewKey]; +} + +@end diff --git a/AppKit/CPColor.j b/AppKit/CPColor.j new file mode 100644 index 0000000000..dd88941924 --- /dev/null +++ b/AppKit/CPColor.j @@ -0,0 +1,394 @@ +/* + * CPColor.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import "CGColor.j" + +import "CPCompatibility.j" +import "CPImage.j" + +var _redComponent = 0, + _greenComponent = 1, + _blueComponent = 2, + _alphaCompnent = 3; + +var _hueComponent = 0, + _saturationComponent = 1, + _brightnessComponent = 2; + +@implementation CPColor : CPObject +{ + CPArray _components; + + CPImage _patternImage; + CPString _cssString; +} + ++ (CPColor)colorWithCalibratedRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha +{ + return [[CPColor alloc] _initWithRGBA:[red, green, blue, alpha]]; +} + ++ (CPColor)colorWithCalibratedWhite:(float)white alpha:(float)alpha +{ + return [[CPColor alloc] _initWithRGBA:[white, white, white, alpha]]; +} + ++ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness +{ + if(saturation == 0.0) + return [CPColor colorWithCalibratedWhite: brightness/100.0 alpha: 1.0]; + + var f = hue % 60, + p = (brightness * (100 - saturation)) / 10000, + q = (brightness * (6000 - saturation * f)) / 600000, + t = (brightness * (6000 - saturation * (60 -f))) / 600000, + b = brightness / 100.0; + + switch(FLOOR(hue / 60)) + { + case 0: return [CPColor colorWithCalibratedRed: b green: t blue: p alpha: 1.0]; + case 1: return [CPColor colorWithCalibratedRed: q green: b blue: p alpha: 1.0]; + case 2: return [CPColor colorWithCalibratedRed: p green: b blue: t alpha: 1.0]; + case 3: return [CPColor colorWithCalibratedRed: p green: q blue: b alpha: 1.0]; + case 4: return [CPColor colorWithCalibratedRed: t green: p blue: b alpha: 1.0]; + case 5: return [CPColor colorWithCalibratedRed: b green: p blue: q alpha: 1.0]; + } +} + ++ (CPColor)colorWithHexString:(string)hex +{ + return [[CPColor alloc] _initWithRGBA: hexToRGB(hex)]; +} + ++ (CPColor)blackColor +{ + return [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0]]; +} + ++ (CPColor)blueColor +{ + return [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 1.0, 1.0]]; +} + ++ (CPColor)darkGrayColor +{ + return [CPColor colorWithCalibratedWhite:1.0 / 3.0 alpha:1.0]; +} + ++ (CPColor)grayColor +{ + return [CPColor colorWithCalibratedWhite:0.5 alpha: 1.0]; +} + ++ (CPColor)greenColor +{ + return [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 0.0, 1.0]]; +} + ++ (CPColor)lightGrayColor +{ + return [CPColor colorWithCalibratedWhite:2.0 / 3.0 alpha:1.0]; +} + ++ (CPColor)redColor +{ + return [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 0.0, 1.0]]; +} + ++ (CPColor)whiteColor +{ + return [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 1.0, 1.0]]; +} + ++ (CPColor)yellowColor +{ + return [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 0.0, 1.0]]; +} + ++ (CPColor)shadowColor +{ + return [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0 / 3.0]]; +} + ++ (CPColor)colorWithPatternImage:(CPImage)anImage +{ + return [[CPColor alloc] _initWithPatternImage:anImage]; +} + ++ (CPColor)colorWithCSSString:(CPString)aString +{ + return [[CPColor alloc] _initWithCSSString: aString]; +} + +- (id)_initWithCSSString:(CPString)aString +{ + if(aString.indexOf("rgb") == CPNotFound) + return nil; + + self = [super init]; + + var startingIndex = aString.indexOf("("); + var parts = aString.substring(startingIndex+1).split(','); + + _components = [ + parseInt(parts[0], 10) / 255.0, + parseInt(parts[1], 10) / 255.0, + parseInt(parts[2], 10) / 255.0, + parts[3] ? parseInt(parts[3], 10) / 255.0 : 1.0 + ] + + _cssString = aString; + + return self; +} + +- (id)_initWithRGBA:(CPArray)components +{ + self = [super init]; + + if (self) + { + _components = components; + + if (!CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0 && window.Base64 && window.CRC32) + { + var bytes = [0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x8,0x3,0x0,0x0,0x0,0x28,0xcb,0x34,0xbb,0x0,0x0,0x3,0x0,0x50,0x4c,0x54,0x45,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x89,0x99,0x55,0x0,0x0,0x0,0x1,0x74,0x52,0x4e,0x53,0x0,0x40,0xe6,0xd8,0x66,0x0,0x0,0x0,0x10,0x49,0x44,0x41,0x54,0x78,0xda,0x62,0x60,0x0,0x0,0x0,0x0,0xff,0xff,0x3,0x0,0x0,0x2,0x0,0x1,0x24,0x7f,0x24,0xf1,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,0xff]; + var r_off = 41; + var g_off = 42; + var b_off = 43; + var a_off = 821; + var plte_crc_off = 809; + var trns_crc_off = 822; + var plte_type_off = 37; + var trns_type_off = 817; + + bytes[r_off] = Math.round(_components[0]*255); + bytes[g_off] = Math.round(_components[1]*255); + bytes[b_off] = Math.round(_components[2]*255); + bytes[a_off] = Math.round(_components[3]*255); + + // calculate new CRCs + var new_plte_crc = integerToBytes(CRC32.getCRC(bytes, plte_type_off, 4+768), 4); + var new_trns_crc = integerToBytes(CRC32.getCRC(bytes, trns_type_off, 4+1), 4); + + // overwrite old CRCs with new ones + for (var i = 0; i < 4; i++) + { + bytes[plte_crc_off+i] = new_plte_crc[i]; + bytes[trns_crc_off+i] = new_trns_crc[i]; + } + + // Base64 encode, strip whitespace and build data URL + var base64image = Base64.encode(bytes); //.replace(/[\s]/g, ""); + + _cssString = "url(\"data:image/png;base64," + base64image + "\")"; + } + else + { + var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0; + + _cssString = (hasAlpha ? "rgba(" : "rgb(") + + parseInt(_components[0] * 255.0) + ", " + + parseInt(_components[1] * 255.0) + ", " + + parseInt(_components[2] * 255.0) + + (hasAlpha ? (", " + _components[3]) : "") + ")"; + } + } + return self; +} + +- (id)_initWithPatternImage:(CPImage)anImage +{ + self = [super init]; + + if (self) + { + _patternImage = anImage; + _cssString = "url(\"" + _patternImage._filename + "\")"; + } + + return self; +} + +- (CPImage)patternImage +{ + return _patternImage; +} + +- (float)alphaComponent +{ + return _components[3]; +} + +- (float)blueComponent +{ + return _components[2]; +} + +- (float)greenComponent +{ + return _components[1]; +} + +- (float)redComponent +{ + return _components[0]; +} + +- (CPArray)components +{ + return _components; +} + +- (CPColor)colorWithAlphaComponent:(float)anAlphaComponent +{ + var components = _components.slice(); + + components[components.length - 1] = anAlphaComponent; + + return [[[self class] alloc] _initWithRGBA:components]; +} + +- (CPArray)hsbComponents +{ + var red = ROUND(_components[_redComponent] * 255.0), + green = ROUND(_components[_greenComponent] * 255.0), + blue = ROUND(_components[_blueComponent] * 255.0); + + var max = MAX(red, green, blue), + min = MIN(red, green, blue), + delta = max - min; + + var brightness = max / 255.0, + saturation = (max != 0) ? delta / max : 0; + + var hue; + if(saturation == 0) + hue = 0; + else + { + var rr = (max - red) / delta; + var gr = (max - green) / delta; + var br = (max - blue) / delta; + + if (red == max) + hue = br - gr; + else if (green == max) + hue = 2 + rr - br; + else + hue = 4 + gr - rr; + + hue /= 6; + if (hue < 0) + hue++; + } + + return [ + ROUND(hue * 360.0), + ROUND(saturation * 100.0), + ROUND(brightness * 100.0) + ]; +} + +- (CPString)cssString +{ + return _cssString; +} + +- (CPString)hexString +{ + return rgbToHex([self redComponent], [self greenComponent], [self blueComponent]) +} + +@end + +var CPColorComponentsKey = @"CPColorComponentsKey", + CPColorPatternImageKey = @"CPColorPatternImageKey"; + +@implementation CPColor (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + if ([aCoder containsValueForKey:CPColorPatternImageKey]) + return [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]]; + + return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]]; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + if (_patternImage) + [aCoder encodeObject:_patternImage forKey:CPColorPatternImageKey]; + else + [aCoder encodeObject:_components forKey:CPColorComponentsKey]; +} + +@end + +var hexCharacters = "0123456789ABCDEF"; + +function hexToRGB(hex) +{ + if(hex.length != 6) + return null; + + for(var i=0; i= 0; i--) { + bytes[i] = integer & 255; + integer = integer >> 8 + } + return bytes; +} + +function rgbToHex(r,g,b) { + return byteToHex(r) + byteToHex(g) + byteToHex(b); +} + +function byteToHex(n) { + if (!n || isNaN(n)) return "00"; + n = ROUND(MIN(255,MAX(0,256*n))); + return hexCharacters.charAt((n - n % 16) / 16) + + hexCharacters.charAt(n % 16); +} + +// Toll-Free bridge CPColor to CGColor. +//CGColor.prototype.isa = CPColor; +//[CPColor initialize]; + +//http://dev.mootools.net/browser/trunk/Source/Utilities/Color.js?rev=1184 diff --git a/AppKit/CPColorPanel.j b/AppKit/CPColorPanel.j new file mode 100644 index 0000000000..7d088aa959 --- /dev/null +++ b/AppKit/CPColorPanel.j @@ -0,0 +1,614 @@ +/* + * CPColorPanel.j + * AppKit + * + * Created by Ross Boucher. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPButton.j" +import "CPColorPicker.j" +import "CPCookie.j" +import "CPKulerColorPicker.j" +import "CPPanel.j" +import "CPSliderColorPicker.j" +import "CPView.j" + + +CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification"; + +var PREVIEW_HEIGHT = 20.0, + TOOLBAR_HEIGHT = 32.0, + SWATCH_HEIGHT = 14.0; + +var SharedColorPanel = nil; + +CPWheelColorPickerMode = 1, +CPKulerColorPickerMode = 2, +CPSliderColorPickerMode = 3; + +CPColorPickerViewWidth = 265, +CPColorPickerViewHeight = 370; + +@implementation CPColorPanel : CPPanel +{ + _CPColorPanelToolbar _toolbar; + _CPColorPanelSwatches _swatchView; + _CPColorPanelPreview _previewView; + + CPTextField _previewLabel; + CPTextField _swatchLabel; + + CPView _activeView; + + CPColorPicker _activePicker; + CPColorPicker _wheelPicker; + CPColorPicker _kulerPicker; + CPColorPicker _sliderPicker; + + CPColor _color; + + id _target; + SEL _action; + + int _mode; +} + ++ (CPColorPanel)sharedColorPanel +{ + if (!SharedColorPanel) + SharedColorPanel = [[CPColorPanel alloc] init]; + + return SharedColorPanel; +} + ++ (void)setPickerMode:(int)mode +{ + var panel = [CPColorPanel sharedColorPanel]; + [panel setMode: mode]; +} + +- (id)init +{ + self = [super initWithContentRect:CGRectMake(500.0, 50.0, 218.0, 360.0) + styleMask:(CPHUDBackgroundWindowMask | CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask)]; + + if (self) + { + [self setTitle:@"Color Panel"]; + [self setLevel:CPFloatingWindowLevel]; + + [self setBecomesKeyOnlyIfNeeded:YES]; + + [self setMinSize:CGSizeMake(218.0, 360.0)]; + [self setMaxSize:CGSizeMake(327.0, 540.0)]; + } + + return self; +} + +- (void)setColor:(CPColor)aColor +{ + _color = aColor; + [_previewView setBackgroundColor: _color]; + + [CPApp sendAction:@selector(changeColor:) to:nil from:self]; + + if (_target && _action) + objj_msgSend(_target, _action, self); + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPColorPanelColorDidChangeNotification + object:self]; +} + + -(void)setColor:(CPColor)aColor updatePicker:(BOOL)bool + { + [self setColor: aColor]; + + if(bool) + [_activePicker setColor: _color]; + } + + +- (CPColor)color +{ + return _color; +} + +- (void)setTarget:(id)aTarget +{ + _target = aTarget; +} + +- (id)target +{ + return _target; +} + +- (void)setAction:(selector)anAction +{ + _action = anAction; +} + +- (selector)action +{ + return _action; +} + +- (void)setMode:(int)mode +{ + if(mode == _mode) + return; + + var frame = CPRectCreateCopy([_currentView frame]); + [_currentView removeFromSuperview]; + + switch(mode) + { + case CPWheelColorPickerMode: _activePicker = _wheelPicker; break; + case CPKulerColorPickerMode: _activePicker = _kulerPicker; break; + case CPSliderColorPickerMode: _activePicker = _sliderPicker; break; + } + + _currentView = [_activePicker provideNewView: NO]; + [_activePicker setColor: _color]; + + _mode = mode; + + [_currentView setFrame: frame]; + [_currentView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)]; + [[self contentView] addSubview: _currentView]; +} + +- (int)mode +{ + return _mode; +} + +- (void)orderFront:(id)aSender +{ + [self _loadContentsIfNecessary]; + + [super orderFront:aSender]; +} + +- (void)_loadContentsIfNecessary +{ + if (_toolbar) + return; + + var contentView = [self contentView], + bounds = [contentView bounds]; + + _toolbar = [[_CPColorPanelToolbar alloc] initWithFrame: CPRectMake(0, 0, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)]; + [_toolbar setAutoresizingMask: CPViewWidthSizable]; + + // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox + var previewBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10, CGRectGetWidth(bounds) - 86, PREVIEW_HEIGHT)]; + + _previewView = [[_CPColorPanelPreview alloc] initWithFrame:CGRectInset([previewBox bounds], 2.0, 2.0)]; + + [_previewView setColorPanel:self]; + [_previewView setAutoresizingMask:CPViewWidthSizable]; + + [previewBox setBackgroundColor:[CPColor grayColor]]; + [previewBox setAutoresizingMask:CPViewWidthSizable]; + + [previewBox addSubview:_previewView]; + + _previewLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 14, 60, 15)]; + [_previewLabel setStringValue: "Preview:"]; + [_previewLabel setTextColor:[CPColor whiteColor]]; + [_previewLabel setAlignment:CPRightTextAlignment]; + + // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox + var swatchBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5, CGRectGetWidth(bounds) - 86, SWATCH_HEIGHT + 2.0)]; + + [swatchBox setBackgroundColor:[CPColor grayColor]]; + [swatchBox setAutoresizingMask:CPViewWidthSizable]; + + _swatchView = [[_CPColorPanelSwatches alloc] initWithFrame:CGRectInset([swatchBox bounds], 1.0, 1.0)]; + + [_swatchView setColorPanel: self]; + [_swatchView setAutoresizingMask: CPViewWidthSizable]; + + [swatchBox addSubview:_swatchView]; + + _swatchLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 8 + PREVIEW_HEIGHT + 5, 60, 15)]; + [_swatchLabel setStringValue: "Swatches:"]; + [_swatchLabel setTextColor:[CPColor whiteColor]]; + [_swatchLabel setAlignment:CPRightTextAlignment]; + + _wheelPicker = [[CPColorWheelColorPicker alloc] initWithPickerMask: 1|2|3 colorPanel: self]; + _currentView = [_wheelPicker provideNewView: YES]; + + var height = (TOOLBAR_HEIGHT+10+PREVIEW_HEIGHT+5+SWATCH_HEIGHT+10); + [_currentView setFrameSize: CPSizeMake(bounds.size.width - 10, bounds.size.height - height)]; + [_currentView setFrameOrigin: CPPointMake(5, TOOLBAR_HEIGHT+10+PREVIEW_HEIGHT+5+SWATCH_HEIGHT+10)]; + [_currentView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)]; + + _kulerPicker = [[CPKulerColorPicker alloc] initWithPickerMask: 1|2|3 colorPanel: self]; + [_kulerPicker provideNewView: YES]; + + _sliderPicker = [[CPSliderColorPicker alloc] initWithPickerMask: 1|2|3 colorPanel: self]; + [_sliderPicker provideNewView: YES]; + + [contentView addSubview: _toolbar]; + [contentView addSubview: previewBox]; + [contentView addSubview: _previewLabel]; + [contentView addSubview: swatchBox]; + [contentView addSubview: _swatchLabel]; + [contentView addSubview: _currentView]; + + _target = nil; + _action = nil; + + _activePicker = _wheelPicker; + + [self setColor:[CPColor whiteColor]]; + [_activePicker setColor:[CPColor whiteColor]]; +} + +@end + +var iconSize = 32, + totalIcons = 3; + +@implementation _CPColorPanelToolbar : CPView +{ + CPImage _wheelImage; + CPImage _wheelAlternateImage; + CPButton _wheelButton; + + CPImage _sliderImage; + CPImage _sliderAlternateImage; + CPButton _sliderButton; + + CPImage _kulerImage; + CPImage _kulerAlternateImage; + CPButton _kulerButton; +} + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + var width = aFrame.size.width; + var center = width / 2.0; + var start = center - ((totalIcons * iconSize) + (totalIcons - 1) * 8.0) / 2.0; + + _wheelButton = [[CPButton alloc] initWithFrame:CPRectMake(start, 0, iconSize, iconSize)]; + + start += iconSize + 8; + + var path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"wheel_button.png"]; + _wheelImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)]; + + path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"wheel_button_h.png"]; + _wheelAlternateImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)]; + + [_wheelButton setBordered:NO]; + [_wheelButton setImage: _wheelImage]; + [_wheelButton setAlternateImage: _wheelAlternateImage]; + [_wheelButton setTarget: self]; + [_wheelButton setAction: @selector(setMode:)]; + [_wheelButton setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin]; + + [self addSubview: _wheelButton]; + + _sliderButton = [[CPButton alloc] initWithFrame:CPRectMake(start, 0, iconSize, iconSize)]; + + start += iconSize + 8; + + path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"slider_button.png"]; + _sliderImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)]; + + path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"slider_button_h.png"]; + _sliderAlternateImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)]; + + [_sliderButton setBordered:NO]; + [_sliderButton setImage: _sliderImage]; + [_sliderButton setAlternateImage: _sliderAlternateImage]; + [_sliderButton setTarget: self]; + [_sliderButton setAction: @selector(setMode:)]; + [_sliderButton setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin]; + + [self addSubview: _sliderButton]; + + _kulerButton = [[CPButton alloc] initWithFrame:CPRectMake(start, 0, iconSize, iconSize)]; + start += iconSize + 8; + + path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"kuler_button.png"]; + _kulerImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)]; + + path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"kuler_button_h.png"]; + _kulerAlternateImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)]; + + [_kulerButton setBordered:NO]; + [_kulerButton setImage: _kulerImage]; + [_kulerButton setAlternateImage: _kulerAlternateImage]; + [_kulerButton setTarget: self]; + [_kulerButton setAction: @selector(setMode:)]; + [_kulerButton setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin]; + + [self addSubview: _kulerButton]; + + return self; +} + +- (void)setMode:(id)sender +{ + if(sender == _kulerButton) + [[CPColorPanel sharedColorPanel] setMode: CPKulerColorPickerMode]; + else if(sender == _wheelButton) + [[CPColorPanel sharedColorPanel] setMode: CPWheelColorPickerMode]; + else + [[CPColorPanel sharedColorPanel] setMode: CPSliderColorPickerMode]; +} + +@end + +CPColorDragType = "CPColorDragType"; +var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie"; + +@implementation _CPColorPanelSwatches : CPView +{ + CPView[] _swatches; + CPColor _dragColor; + CPColorPanel _colorPanel; + CPCookie _swatchCookie; +} + +-(id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + [self setBackgroundColor: [CPColor grayColor]]; + + [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]]; + + var whiteColor = [CPColor whiteColor]; + + _swatchCookie = [[CPCookie alloc] initWithName: CPColorPanelSwatchesCookie]; + var colorList = [self startingColorList]; + + _swatches = []; + + for(var i=0; i < 50; i++) + { + // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox + var view = [[CPView alloc] initWithFrame: CPRectMake(13*i+1, 1, 12, 12)], + fillView = [[CPView alloc] initWithFrame:CGRectInset([view bounds], 1.0, 1.0)]; + + [view setBackgroundColor:whiteColor]; + [fillView setBackgroundColor: (i < colorList.length) ? colorList[i] : whiteColor]; + + [view addSubview:fillView]; + + [self addSubview: view]; + + _swatches.push(view); + } + + return self; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (CPArray)startingColorList +{ + var cookieValue = [_swatchCookie value]; + if(cookieValue == "") + { + return [ + [CPColor blackColor], + [CPColor darkGrayColor], + [CPColor grayColor], + [CPColor lightGrayColor], + [CPColor whiteColor], + [CPColor redColor], + [CPColor greenColor], + [CPColor blueColor], + [CPColor yellowColor] + ]; + } + + var cookieValue = eval(cookieValue); + var result = []; + + for(var i=0; i [self bounds].size.width - 1 || point.x < 1) + return NO; + + [_colorPanel setColor: [self colorAtIndex:FLOOR(point.x / 13)] updatePicker: YES]; +} + +- (void)mouseDragged:(CPEvent)anEvent +{ + var point = [self convertPoint:[anEvent locationInWindow] fromView:nil]; + + if(point.x > [self bounds].size.width - 1 || point.x < 1) + return NO; + + [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self]; + + var swatch = _swatches[FLOOR(point.x / 13)]; + + // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox + _dragColor = [[swatch subviews][0] backgroundColor]; + + var bounds = CPRectCreateCopy([swatch bounds]); + + // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox + var dragView = [[CPView alloc] initWithFrame: bounds]; + dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)]; + + [dragView setBackgroundColor:[CPColor blackColor]]; + [dragFillView setBackgroundColor:_dragColor]; + + [dragView addSubview:dragFillView]; + + [self dragView: dragView + at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) + offset: CPPointMake(0.0, 0.0) + event: anEvent + pasteboard: nil + source: self + slideBack: YES]; +} + +- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType +{ + if(aType == CPColorDragType) + [aPasteboard setData:_dragColor forType:aType]; +} + +- (void)performDragOperation:(id )aSender +{ + var location = [self convertPoint:[aSender draggingLocation] fromView:nil], + pasteboard = [aSender draggingPasteboard], + swatch = nil; + + if(![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1) + return NO; + + [self setColor:[pasteboard dataForType:CPColorDragType] atIndex: FLOOR(location.x / 13)]; +} + +@end + +@implementation _CPColorPanelPreview : CPView +{ + CPColorPanel _colorPanel; +} + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]]; + + return self; +} + +- (void)setColorPanel:(CPColorPanel)aPanel +{ + _colorPanel = aPanel; +} + +- (CPColorPanel)colorPanel +{ + return _colorPanel; +} + +- (void)performDragOperation:(id )aSender +{ + var pasteboard = [aSender draggingPasteboard]; + + if(![pasteboard availableTypeFromArray:[CPColorDragType]]) + return NO; + + var color = [pasteboard dataForType:CPColorDragType]; + [_colorPanel setColor: color updatePicker: YES]; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (void)mouseDragged:(CPEvent)anEvent +{ + var point = [self convertPoint:[anEvent locationInWindow] fromView:nil]; + + [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self]; + + var bounds = CPRectMake(0, 0, 15, 15); + + // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox + var dragView = [[CPView alloc] initWithFrame: bounds]; + dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)]; + + [dragView setBackgroundColor:[CPColor blackColor]]; + [dragFillView setBackgroundColor:[self backgroundColor]]; + + [dragView addSubview:dragFillView]; + + [self dragView: dragView + at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) + offset: CPPointMake(0.0, 0.0) + event: anEvent + pasteboard: nil + source: self + slideBack: YES]; +} + +- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType +{ + if(aType == CPColorDragType) + [aPasteboard setData:[self backgroundColor] forType:aType]; +} + +@end diff --git a/AppKit/CPColorPicker.j b/AppKit/CPColorPicker.j new file mode 100644 index 0000000000..343df45c9e --- /dev/null +++ b/AppKit/CPColorPicker.j @@ -0,0 +1,336 @@ +/* + * CPColorPicker.j + * AppKit + * + * Created by Ross Boucher. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import +import + + +@implementation CPColorPicker : CPObject +{ + CPColorPanel _panel; + int _mask; +} + +- (id)initWithPickerMask:(int)aMask colorPanel:(CPColorPanel)aPanel +{ + self = [super init]; + + _panel = aPanel; + _mask = aMask; + + return self; +} + +- (CPColorPanel)colorPanel +{ + return _panel; +} + +- (CPImage)provideNewButtonImage +{ + return nil; +} + +- (void)setMode:(int)mode +{ + return; +} + +- (void)setColor:(CPColor)aColor +{ + return; +} + +@end + +@implementation CPColorWheelColorPicker : CPColorPicker +{ + CPView _pickerView; + CPView _brightnessSlider; + DOMElement _brightnessBarImage; + __CPColorWheel _hueSaturationView; +} + +- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel +{ + return [super initWithPickerMask:mask colorPanel: owningColorPanel]; +} + +-(id)initView +{ + aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight); + _pickerView = [[CPView alloc] initWithFrame:aFrame]; + + var path = [[CPBundle bundleForClass: CPColorPicker] pathForResource:@"brightness_bar.png"]; + + _brightnessBarImage = new Image(); + _brightnessBarImage.src = path; + _brightnessBarImage.style.width = "100%"; + _brightnessBarImage.style.height = "100%"; + _brightnessBarImage.style.position = "absolute"; + _brightnessBarImage.style.top = "0px"; + _brightnessBarImage.style.left = "0px"; + + var brightnessBarView = [[CPView alloc] initWithFrame: CPRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)]; + [brightnessBarView setAutoresizingMask: (CPViewWidthSizable | CPViewMinYMargin)]; + brightnessBarView._DOMElement.appendChild(_brightnessBarImage); + + _brightnessSlider = [[CPSlider alloc] initWithFrame: CPRectMake(0, aFrame.size.height - 22, aFrame.size.width, 12)]; + [_brightnessSlider setMaxValue: 100.0]; + [_brightnessSlider setMinValue: 0.0]; + [_brightnessSlider setValue: 100.0]; + + [_brightnessSlider setTarget: self]; + [_brightnessSlider setAction: @selector(brightnessSliderDidChange:)]; + [_brightnessSlider setAutoresizingMask: (CPViewWidthSizable | CPViewMinYMargin)]; + + _hueSaturationView = [[__CPColorWheel alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, aFrame.size.height - 38)]; + [_hueSaturationView setDelegate: self]; + [_hueSaturationView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)]; + + [_pickerView addSubview: brightnessBarView]; + [_pickerView addSubview: _hueSaturationView]; + [_pickerView addSubview: _brightnessSlider]; +} + +-(void)brightnessSliderDidChange:(id)sender +{ + [self updateColor]; +} + +-(void)colorWheelDidChange:(id)sender +{ + [self updateColor]; +} + +-(void)updateColor +{ + var hue = [_hueSaturationView angle], + saturation = [_hueSaturationView distance], + brightness = [_brightnessSlider value]; + + [_hueSaturationView setWheelBrightness: brightness / 100.0]; + _brightnessBarImage.style.backgroundColor = "#"+[[CPColor colorWithHue: hue saturation: saturation brightness: 100] hexString]; + + [[self colorPanel] setColor:[CPColor colorWithHue: hue saturation: saturation brightness: brightness]]; +} + +- (BOOL)supportsMode:(int)mode +{ + return (mode == CPWheelColorPickerMode) ? YES : NO; +} + +- (int)currentMode +{ + return CPWheelColorPickerMode; +} + +- (CPView)provideNewView:(BOOL)initialRequest +{ + if (initialRequest) + [self initView]; + + return _pickerView; +} + +- (void)setColor:(CPColor)newColor +{ + var hsb = [newColor hsbComponents]; + + [_hueSaturationView setPositionToColor: newColor]; + [_brightnessSlider setValue: hsb[2]]; + [_hueSaturationView setWheelBrightness: hsb[2] / 100.0]; + + _brightnessBarImage.style.backgroundColor = "#"+[[CPColor colorWithHue: hsb[0] saturation: hsb[1] brightness: 100] hexString]; +} + +@end + +@implementation __CPColorWheel : CPView +{ + DOMElement _wheelImage; + DOMElement _blackWheelImage; + + CPView _crosshair; + + id _delegate; + + float _angle; + float _distance; + + float _radius; +} + +-(id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame: aFrame]; + + var path = [[CPBundle bundleForClass: CPColorPicker] pathForResource:@"wheel.png"]; + + _wheelImage = new Image(); + _wheelImage.src = path; + _wheelImage.style.position = "absolute"; + + path = [[CPBundle bundleForClass: CPColorPicker] pathForResource:@"wheel_black.png"]; + + _blackWheelImage = new Image(); + _blackWheelImage.src = path; + _blackWheelImage.style.opacity = "0"; + _blackWheelImage.style.filter = "alpha(opacity=0)" + _blackWheelImage.style.position = "absolute"; + + _DOMElement.appendChild(_wheelImage); + _DOMElement.appendChild(_blackWheelImage); + + [self setWheelSize: aFrame.size]; + + _crosshair = [[CPView alloc] initWithFrame:CPRectMake(_radius - 2, _radius - 2, 4, 4)]; + [_crosshair setBackgroundColor:[CPColor blackColor]]; + + var view = [[CPView alloc] initWithFrame:CGRectInset([_crosshair bounds], 1.0, 1.0)]; + [view setBackgroundColor:[CPColor whiteColor]]; + + [_crosshair addSubview:view]; + + [self addSubview: _crosshair]; + + return self; +} + +-(void)setWheelBrightness:(float)brightness +{ + _blackWheelImage.style.opacity = 1.0 - brightness; + _blackWheelImage.style.filter = "alpha(opacity=" + (1.0 - brightness)*100 + ")" +} + +-(void)setFrameSize:(CPSize)aSize +{ + [super setFrameSize: aSize]; + [self setWheelSize: aSize]; +} + +-(void)setWheelSize:(CPSize)aSize +{ + var min = MIN(aSize.width, aSize.height); + + _blackWheelImage.style.width = min; + _blackWheelImage.style.height = min; + _blackWheelImage.width = min; + _blackWheelImage.height = min; + _blackWheelImage.style.top = (aSize.height - min) / 2.0 + "px"; + _blackWheelImage.style.left = (aSize.width - min) / 2.0 + "px"; + + _wheelImage.style.width = min; + _wheelImage.style.height = min; + _wheelImage.width = min; + _wheelImage.height = min; + _wheelImage.style.top = (aSize.height - min) / 2.0 + "px"; + _wheelImage.style.left = (aSize.width - min) / 2.0 + "px"; + + _radius = min / 2.0; + + [self setAngle: [self degreesToRadians: _angle] distance: (_distance / 100.0) * _radius]; +} + +-(void)setDelegate:(id)aDelegate +{ + _delegate = aDelegate; +} + +-(id)delegate +{ + return _delegate; +} + +-(float)angle +{ + return _angle; +} + +-(float)distance +{ + return _distance; +} + +-(void)mouseDown:(CPEvent)anEvent +{ + [self reposition: anEvent]; +} + +-(void)mouseDragged:(CPEvent)anEvent +{ + [self reposition: anEvent]; +} + +-(void)reposition:(CPEvent)anEvent +{ + var bounds = [self bounds], + location = [self convertPoint: [anEvent locationInWindow] fromView: nil]; + + var midX = CGRectGetMidX(bounds); + var midY = CGRectGetMidY(bounds); + + var distance = MIN(SQRT((location.x - midX)*(location.x - midX) + (location.y - midY)*(location.y - midY)), _radius); + var angle = ATAN2(location.y - midY, location.x - midX); + + [self setAngle: angle distance: distance]; + + if(_delegate) + [_delegate colorWheelDidChange: self]; +} + +-(void)setAngle:(int)angle distance:(float)distance +{ + var bounds = [self bounds]; + var midX = CGRectGetMidX(bounds); + var midY = CGRectGetMidY(bounds); + + _angle = [self radiansToDegrees: angle]; + _distance = (distance / _radius) * 100.0; + + [_crosshair setFrameOrigin:CPPointMake(COS(angle) * distance + midX - 2.0, SIN(angle) * distance + midY - 2.0)]; +} + +-(void)setPositionToColor:(CPColor)aColor +{ + var hsb = [aColor hsbComponents], + bounds = [self bounds]; + + var angle = [self degreesToRadians: hsb[0]], + distance = (hsb[1] / 100.0) * _radius; + + [self setAngle: angle distance: distance]; +} + +-(int)radiansToDegrees:(float)radians +{ + return ((-radians / PI) * 180 + 360) % 360; +} + +-(float)degreesToRadians:(float)degrees +{ + return -(((degrees - 360) / 180) * PI); +} + +@end \ No newline at end of file diff --git a/AppKit/CPColorWell.j b/AppKit/CPColorWell.j new file mode 100644 index 0000000000..bcca91233b --- /dev/null +++ b/AppKit/CPColorWell.j @@ -0,0 +1,198 @@ +/* + * CPColorWell.j + * AppKit + * + * Created by Ross Boucher. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import "CPView.j" +import "CPColor.j" +import "CPColorPanel.j" + + +var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiveNotification"; + +@implementation CPColorWell : CPControl +{ + BOOL _active; + + CPColor _color; + CPView _wellView; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _active = NO; + + _color = [CPColor whiteColor]; + + [self drawBezelWithHighlight:NO]; + [self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)]; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + [defaultCenter + addObserver:self + selector:@selector(colorWellDidBecomeExclusive:) + name:_CPColorWellDidBecomeExclusiveNotification + object:nil]; + + [defaultCenter + addObserver:self + selector:@selector(colorPanelWillClose:) + name:CPWindowWillCloseNotification + object:[CPColorPanel sharedColorPanel]]; + } + + return self; +} + +// Managing Color From Color Wells + +- (CPColor)color +{ + return _color; +} + +- (void)setColor:(CPColor)aColor +{ + if (_color == aColor) + return; + + _color = aColor; + + [self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)]; +} + +- (void)takeColorFrom:(id)aSender +{ + [self setColor:[aSender color]]; +} + +// Activating and Deactivating Color Wells + +- (void)activate:(BOOL)shouldBeExclusive +{ + if (shouldBeExclusive) + // FIXME: make this queue! + [[CPNotificationCenter defaultCenter] + postNotificationName:_CPColorWellDidBecomeExclusiveNotification + object:self]; + + + if ([self isActive]) + return; + + _active = YES; + + [[CPNotificationCenter defaultCenter] + addObserver:self + selector:@selector(colorPanelDidChangeColor:) + name:CPColorPanelColorDidChangeNotification + object:[CPColorPanel sharedColorPanel]]; +} + +- (void)deactivate +{ + if (![self isActive]) + return; + + _active = NO; + + [[CPNotificationCenter defaultCenter] + removeObserver:self + name:CPColorPanelColorDidChangeNotification + object:[CPColorPanel sharedColorPanel]]; +} + +- (BOOL)isActive +{ + return _active; +} + +// Drawing a Color Well + +- (void)drawBezelWithHighlight:(BOOL)shouldHighlight +{ +} + +- (void)drawWellInside:(CGRect)aRect +{ + if (!_wellView) + { + _wellView = [[CPView alloc] initWithFrame:aRect]; + + [self addSubview:_wellView]; + } + else + [_wellView setFrame:aRect]; + + [_wellView setBackgroundColor:_color]; +} + +- (void)colorPanelDidChangeColor:(CPNotification)aNotification +{ + [self takeColorFrom:[aNotification object]]; + + [self sendAction:[self action] to:[self target]]; +} + +- (void)colorWellDidBecomeExclusive:(CPNotification)aNotification +{ + if (self != [aNotification object]) + [self deactivate]; +} + +- (void)colorPanelWillClose:(CPNotification)aNotification +{ + [self deactivate]; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + [self drawBezelWithHighlight:YES]; +} + +- (void)mouseDragged:(CPEvent)anEvent +{ + [self drawBezelWithHighlight:CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil])]; +} + +-(void)mouseUp:(CPEvent)anEvent +{ + [self drawBezelWithHighlight:NO]; + + if (!CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil])) + return; + + [self activate:YES]; + + var colorPanel = [CPColorPanel sharedColorPanel]; + + [colorPanel setColor:_color]; + + [colorPanel orderFront:self]; +} + +@end diff --git a/AppKit/CPCompatibility.j b/AppKit/CPCompatibility.j new file mode 100644 index 0000000000..981769fccd --- /dev/null +++ b/AppKit/CPCompatibility.j @@ -0,0 +1,162 @@ +/* + * CPCompatibility.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPEvent.j" + +// Browser Engines +CPUnknownBrowserEngine = 0; +CPGeckoBrowserEngine = 1; +CPInternetExplorerBrowserEngine = 2; +CPKHTMLBrowserEngine = 3; +CPOperaBrowserEngine = 4; +CPWebKitBrowserEngine = 5; + +// Features +CPCSSRGBAFeature = 1 << 5; + +CPHTMLCanvasFeature = 1 << 6; +CPHTMLContentEditableFeature = 1 << 7; + +CPJavascriptInnerTextFeature = 1 << 8; +CPJavascriptTextContentFeature = 1 << 9; +CPJavascriptClipboardEventsFeature = 1 << 10; +CPJavascriptClipboardAccessFeature = 1 << 11; +CPJavaScriptCanvasDrawFeature = 1 << 12; + +CPVMLFeature = 1 << 14; + +CPJavascriptRemedialKeySupport = 1 << 15; +CPJavaScriptShadowFeature = 1 << 20; + +CPJavaScriptNegativeMouseWheelValues = 1 << 22; + +var USER_AGENT = ""; + + PLATFORM_ENGINE = CPUnknownBrowserEngine, + PLATFORM_FEATURES = 0; + +if (typeof window != "undfined" && typeof window.navigator != "undefined") + USER_AGENT = window.navigator.userAgent; + +// Opera +if (window.opera) +{ + PLATFORM_ENGINE = CPOperaBrowserEngine; + + PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature; +} + +// Internet Explorer +else if (window.attachEvent) // Must follow Opera check. +{ + PLATFORM_ENGINE = CPInternetExplorerBrowserEngine; + + // Features we can only be sure of with IE (no known independent tests) + PLATFORM_FEATURES |= CPVMLFeature; + PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport; + PLATFORM_FEATURES |= CPJavaScriptShadowFeature; +} + +// WebKit +else if (USER_AGENT.indexOf("AppleWebKit/") != -1) +{ + PLATFORM_ENGINE = CPWebKitBrowserEngine; + + // Features we can only be sure of with WebKit (no known independent tests) + PLATFORM_FEATURES |= CPCSSRGBAFeature; + PLATFORM_FEATURES |= CPHTMLContentEditableFeature; + PLATFORM_FEATURES |= CPJavascriptClipboardEventsFeature; + PLATFORM_FEATURES |= CPJavascriptClipboardAccessFeature; + PLATFORM_FEATURES |= CPJavaScriptShadowFeature; + + if(USER_AGENT.indexOf("AppleWebKit/") != -1) + { + var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length, + versionEnd = USER_AGENT.indexOf(" ", versionStart), + version = parseFloat(USER_AGENT.substring(versionStart, versionEnd), 10); + + if(version >= 525.13) + PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport; + } +} + +// KHTML +else if (USER_AGENT.indexOf('KHTML') != -1) // Must follow WebKit check. +{ + PLATFORM_ENGINE = CPKHTMLBrowserEngine; +} + +// Gecko +else if (USER_AGENT.indexOf('Gecko') != -1) // Must follow KHTML check. +{ + PLATFORM_ENGINE = CPGeckoBrowserEngine; + + PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature; + + var index = USER_AGENT.indexOf("Firefox"), + version = (index == -1) ? 2.0 : parseFloat(USER_AGENT.substring(index+"Firefox".length+1)); + + if (version >= 3.0) + PLATFORM_FEATURES |= CPCSSRGBAFeature; +} + +// Feature Specific Checks +if (typeof document != "undefined") +{ + // Detect Canvas Support + if (document.createElement("canvas").getContext) + PLATFORM_FEATURES |= CPHTMLCanvasFeature; + + var DOMElement = document.createElement("div"); + + // Detect whether we have innerText or textContent (or neither) + if (DOMElement.innerText != undefined) + PLATFORM_FEATURES |= CPJavascriptInnerTextFeature; + else if (DOMElement.textContent != undefined) + PLATFORM_FEATURES |= CPJavascriptTextContentFeature; +} + +function CPFeatureIsCompatible(aFeature) +{ + return PLATFORM_FEATURES & aFeature; +} + +if (USER_AGENT.indexOf("Mac") != -1) +{ + CPPlatformActionKeyMask = CPCommandKeyMask; + + CPUndoKeyEquivalent = @"Z"; + CPRedoKeyEquivalent = @"Z"; + + CPUndoKeyEquivalentModifierMask = CPCommandKeyMask; + CPRedoKeyEquivalentModifierMask = CPCommandKeyMask | CPShiftKeyMask; +} +else +{ + CPPlatformActionKeyMask = CPControlKeyMask; + + CPUndoKeyEquivalent = @"Z"; + CPRedoKeyEquivalent = @"Y"; + + CPUndoKeyEquivalentModifierMask = CPControlKeyMask; + CPRedoKeyEquivalentModifierMask = CPControlKeyMask; +} diff --git a/AppKit/CPControl.j b/AppKit/CPControl.j new file mode 100644 index 0000000000..29b87774ac --- /dev/null +++ b/AppKit/CPControl.j @@ -0,0 +1,391 @@ +/* + * CPControl.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPFont.j" +import "CPShadow.j" +import "CPView.j" + +#include "Platform/Platform.h" + + +CPLeftTextAlignment = 0; +CPRightTextAlignment = 1; +CPCenterTextAlignment = 2; +CPJustifiedTextAlignment = 3; +CPNaturalTextAlignment = 4; + +CPRegularControlSize = 0; +CPSmallControlSize = 1; +CPMiniControlSize = 2; + +CPControlNormalBackgroundColor = @"CPControlNormalBackgroundColor"; +CPControlSelectedBackgroundColor = @"CPControlSelectedBackgroundColor"; +CPControlHighlightedBackgroundColor = @"CPControlHighlightedBackgroundColor"; +CPControlDisabledBackgroundColor = @"CPControlDisabledBackgroundColor"; + +var CPControlBlackColor = [CPColor blackColor]; + +@implementation CPControl : CPView +{ + id _value; + + BOOL _isEnabled; + + int _alignment; + CPFont _font; + CPColor _textColor; + CPShadow _textShadow; + + id _target; + SEL _action; + int _sendActionOn; + + CPColor _backgroundColor; + CPColor _highlightedBackgroundColor; + + CPDictionary _backgroundColors; + CPString _currentBackgroundColorName; +} + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _sendActionOn = CPLeftMouseUpMask; + _isEnabled = YES; + + [self setFont:[CPFont systemFontOfSize:12.0]]; + [self setTextColor:CPControlBlackColor]; + + _backgroundColors = [CPDictionary dictionary]; + } + + return self; +} + +- (void)setEnabled:(BOOL)isEnabled +{ + [self setAlphaValue:(_isEnabled = isEnabled) ? 1.0 : 0.3]; +} + +- (BOOL)isEnabled +{ + return _isEnabled; +} + +- (void)setTextColor:(CPColor)aColor +{ + if (_textColor == aColor) + return; + + _textColor = aColor; + +#if PLATFORM(DOM) + _DOMElement.style.color = [aColor cssString]; +#endif +} + +- (CPColor)textColor +{ + return _textColor; +} + +- (int)alignment +{ + return _alignment; +} + +- (void)setAlignment:(int)anAlignment +{ + _alignment = anAlignment; +} + +- (void)setFont:(CPFont)aFont +{ + if (_font == aFont) + return; + + _font = aFont; + +#if PLATFORM(DOM) + _DOMElement.style.font = [_font ? _font : [CPFont systemFontOfSize:12.0] cssString]; +#endif +} + +- (CPFont)font +{ + return _font; +} + +- (void)setTextShadow:(CPShadow)aTextShadow +{ + _DOMElement.style.textShadow = [_textShadow = aTextShadow cssString]; +} + +- (CPShadow)textShadow +{ + return _textShadow; +} + +- (SEL)action +{ + return _action; +} + +- (void)setAction:(SEL)anAction +{ + _action = anAction; +} + +- (id)target +{ + return _target; +} + +- (void)setTarget:(id)aTarget +{ + _target = aTarget; +} + +- (void)mouseUp:(CPEvent)anEvent +{ + if (_sendActionOn & CPLeftMouseUpMask && CPRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil])) + [self sendAction:_action to:_target]; + + [super mouseUp:anEvent]; +} + +- (void)sendAction:(SEL)anAction to:(id)anObject +{ + [CPApp sendAction:anAction to:anObject from:self]; +} + +- (float)floatValue +{ + return _value ? parseFloat(_value) : 0.0; +} + +- (void)setFloatValue:(float)aValue +{ + _value = aValue; +} + +- (void)setBackgroundColor:(CPColor)aColor +{ + _backgroundColors = [CPDictionary dictionary]; + + [self setBackgroundColor:aColor forName:CPControlNormalBackgroundColor]; + + [super setBackgroundColor:aColor]; +} + +- (void)setBackgroundColor:(CPColor)aColor forName:(CPString)aName +{ + if (!aColor) + [_backgroundColors removeObjectForKey:aName]; + else + [_backgroundColors setObject:aColor forKey:aName]; + + if (_currentBackgroundColorName == aName) + [self setBackgroundColorWithName:_currentBackgroundColorName]; +} + +- (CPColor)backgroundColorForName:(CPString)aName +{ + var backgroundColor = [_backgroundColors objectForKey:aName]; + + if (!backgroundColor && aName != CPControlNormalBackgroundColor) + return [_backgroundColors objectForKey:CPControlNormalBackgroundColor]; + + return backgroundColor; +} + +- (void)setBackgroundColorWithName:(CPString)aName +{ + _currentBackgroundColorName = aName; + + [super setBackgroundColor:[self backgroundColorForName:aName]]; +} + +/* +Ð doubleValue +Ð setDoubleValue: +Ð intValue +Ð setIntValue: +Ð objectValue +Ð setObjectValue: +Ð stringValue +Ð setStringValue: +Ð setNeedsDisplay +Ð attributedStringValue +Ð setAttributedStringValue: */ + +@end + +var CPControlIsEnabledKey = @"CPControlIsEnabledKey", + CPControlAlignmentKey = @"CPControlAlignmentKey", + CPControlFontKey = @"CPControlFontKey", + CPControlTextColorKey = @"CPControlTextColorKey", + CPControlTargetKey = @"CPControlTargetKey", + CPControlActionKey = @"CPControlActionKey", + CPControlSendActionOnKey = @"CPControlSendActionOnKey"; + +@implementation CPControl (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + [self setEnabled:[aCoder decodeIntForKey:CPControlIsEnabledKey]]; + + [self setAlignment:[aCoder decodeIntForKey:CPControlAlignmentKey]]; + [self setFont:[aCoder decodeObjectForKey:CPControlFontKey]]; + [self setTextColor:[aCoder decodeObjectForKey:CPControlTextColorKey]]; + + [self setTarget:[aCoder decodeObjectForKey:CPControlTargetKey]]; + [self setAction:[aCoder decodeObjectForKey:CPControlActionKey]]; + + _sendActionOn = [aCoder decodeIntForKey:CPControlSendActionOnKey]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [super encodeWithCoder:aCoder]; + + [aCoder encodeInt:_isEnabled forKey:CPControlIsEnabledKey]; + + [aCoder encodeInt:_alignment forKey:CPControlAlignmentKey]; + [aCoder encodeObject:_font forKey:CPControlFontKey]; + [aCoder encodeObject:_textColor forKey:CPControlTextColorKey]; + + [aCoder encodeConditionalObject:_target forKey:CPControlTargetKey]; + [aCoder encodeObject:_action forKey:CPControlActionKey]; + + [aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey]; +} + +@end + +var _CPControlSizeIdentifiers = [], + _CPControlCachedThreePartImages = {}, + _CPControlCachedColorWithPatternImages = {}, + _CPControlCachedThreePartImagePattern = {}; + +_CPControlSizeIdentifiers[CPRegularControlSize] = @"Regular"; +_CPControlSizeIdentifiers[CPSmallControlSize] = @"Small"; +_CPControlSizeIdentifiers[CPMiniControlSize] = @"Mini"; + +function _CPControlIdentifierForControlSize(aControlSize) +{ + return _CPControlSizeIdentifiers[aControlSize]; +} + +function _CPControlColorWithPatternImage(sizes, aClassName) +{ + var index = 1, + count = arguments.length, + identifier = @""; + + for (; index < count; ++index) + identifier += arguments[index]; + + var color = _CPControlCachedColorWithPatternImages[identifier]; + + if (!color) + { + var bundle = [CPBundle bundleForClass:[CPControl class]]; + + color = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:aClassName + "/" + identifier + @".png"] size:sizes[identifier]]]; + + _CPControlCachedColorWithPatternImages[identifier] = color; + } + + return color; +} + +function _CPControlThreePartImages(sizes, aClassName) +{ + var index = 1, + count = arguments.length, + identifier = @""; + + for (; index < count; ++index) + identifier += arguments[index]; + + var images = _CPControlCachedThreePartImages[identifier]; + + if (!images) + { + var bundle = [CPBundle bundleForClass:[CPControl class]], + path = aClassName + "/" + identifier; + + sizes = sizes[identifier]; + + images = [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"0.png"] size:sizes[0]], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"1.png"] size:sizes[1]], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"2.png"] size:sizes[2]] + ]; + + _CPControlCachedThreePartImages[identifier] = images; + } + + return images; +} + +function _CPControlThreePartImagePattern(isVertical, sizes, aClassName) +{ + var index = 2, + count = arguments.length, + identifier = @""; + + for (; index < count; ++index) + identifier += arguments[index]; + + var color = _CPControlCachedThreePartImagePattern[identifier]; + + if (!color) + { + var bundle = [CPBundle bundleForClass:[CPControl class]], + path = aClassName + "/" + identifier; + + sizes = sizes[identifier]; + + color = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:[ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"0.png"] size:sizes[0]], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"1.png"] size:sizes[1]], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"2.png"] size:sizes[2]] + ] isVertical:isVertical]]; + + _CPControlCachedThreePartImagePattern[identifier] = color; + } + + return color; +} + diff --git a/AppKit/CPCookie.j b/AppKit/CPCookie.j new file mode 100644 index 0000000000..33adb1fcf4 --- /dev/null +++ b/AppKit/CPCookie.j @@ -0,0 +1,89 @@ +/* + * CPCookie.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + + +@implementation CPCookie : CPObject +{ + CPString _cookieName; + CPString _cookieValue; + + CPString _expires; +} + +- (id)initWithName:(CPString)aName +{ + self = [super init]; + + _cookieName = aName; + _cookieValue = [self _readCookieValue]; + + return self; +} + +- (CPString)value +{ + return _cookieValue; +} + +- (CPString)name +{ + return _cookieName; +} + +- (CPString)expires +{ + return _expires; +} + +- (void)setValue:(CPString)value expires:(CPDate)date domain:(CPString)domain +{ + if(date) + var expires = "; expires="+date.toGMTString(); + else + var expires = ""; + + if(domain) + domain = "; domain="+domain; + else + domain = ""; + + document.cookie = _cookieName+"="+value+expires+"; path=/"+domain; +} + +- (CPString)_readCookieValue +{ + var nameEQ = _cookieName + "="; + var ca = document.cookie.split(';'); + for(var i=0;i < ca.length;i++) { + var c = ca[i]; + while (c.charAt(0)==' ') c = c.substring(1,c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); + } + return ""; +} + +@end + +//http://www.quirksmode.org/js/cookies.html diff --git a/AppKit/CPDocument.j b/AppKit/CPDocument.j new file mode 100644 index 0000000000..ccb52e8789 --- /dev/null +++ b/AppKit/CPDocument.j @@ -0,0 +1,514 @@ +/* + * CPDocument.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + +import "CPResponder.j" +import "CPWindowController.j" + + +CPSaveOperation = 0; +CPSaveAsOperation = 1; +CPSaveToOperation = 2; +CPAutosaveOperation = 3; + +CPChangeDone = 0; +CPChangeUndone = 1; +CPChangeCleared = 2; +CPChangeReadOtherContents = 3; +CPChangeAutosaved = 4; + +CPDocumentWillSaveNotification = @"CPDocumentWillSaveNotification"; +CPDocumentDidSaveNotification = @"CPDocumentDidSaveNotification"; +CPDocumentDidFailToSaveNotification = @"CPDocumentDidFailToSaveNotification"; + +var CPDocumentUntitledCount = 0; + +@implementation CPDocument : CPResponder +{ + CPURL _fileURL; + CPString _fileType; + CPArray _windowControllers; + unsigned _untitledDocumentIndex; + + BOOL _hasUndoManager; + CPUndoManager _undoManager; + + int _changeCount; + + CPURLConnection _readConnection; + CPURLRequest _writeRequest; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + _windowControllers = []; + + _hasUndoManager = YES; + _changeCount = 0; + + [self setNextResponder:CPApp]; + } + + return self; +} + +- (id)initWithType:(CPString)aType error:({CPError})anError +{ + self = [self init]; + + if (self) + [self setFileType:aType]; + + return self; +} + +- (id)initWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo +{ + self = [self init]; + + if (self) + { + [self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo]; + + [self setFileURL:anAbsoluteURL]; + [self setFileType:aType]; + } + + return self; +} + +- (id)initForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo +{ + self = [self init]; + + if (self) + { + [self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo]; + + [self setFileURL:anAbsoluteURL]; + [self setFileType:aType]; + } + + return self; +} + +- (CPData)dataOfType:(CPString)aType error:({CPError})anError +{ + // FIXME: Throw Exception. + return nil; +} + +- (void)readFromData:(CPData)aData ofType:(CPString)aType error:(CPError)anError +{ +} + +// Creating and managing window controllers + +- (void)makeWindowControllers +{ + var controller = [[CPWindowController alloc] initWithWindowCibName:nil]; + + [self addWindowController:controller]; +} + +- (CPArray)windowControllers +{ + return _windowControllers; +} + +- (void)addWindowController:(CPWindowController)aWindowController +{ + [_windowControllers addObject:aWindowController]; + + if ([aWindowController document] != self) + { + [aWindowController setNextResponder:self]; + [aWindowController setDocument:self]; + } +} + +// Managing Document Windows + +- (void)showWindows +{ + [_windowControllers makeObjectsPerformSelector:@selector(showWindow:) withObject:self]; +} + +- (CPString)displayName +{ + // FIXME: By default, return last path component of fileURL + if (!_untitledDocumentIndex) + _untitledDocumentIndex = ++CPDocumentUntitledCount; + + if (_untitledDocumentIndex == 1) + return @"Untitled"; + + return @"Untitled " + _untitledDocumentIndex; +} + +- (CPString)windowCibName +{ + return nil; +} + +- (void)windowControllerDidLoadNib:(CPWindowController)aWindowController +{ +} + +- (void)windowControllerWillLoadNib:(CPWindowController)aWindowController +{ +} + +// Reading from and Writing to URLs + +- (void)readFromURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo +{ + [_readConnection cancel]; + + // FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors. + _readConnection = [CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:anAbsoluteURL] delegate:self]; + + _readConnection.session = _CPReadSessionMake(aType, aDelegate, aDidReadSelector, aContextInfo); +} + +- (CPURL)fileURL +{ + return _fileURL; +} + +- (void)setFileURL:(CPURL)aFileURL +{ + if (_fileURL == aFileURL) + return; + + _fileURL = aFileURL; + + [_windowControllers makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)]; +} + +- (void)saveToURL:(CPURL)anAbsoluteURL ofType:(CPString)aTypeName forSaveOperation:(CPSaveOperationType)aSaveOperation delegate:(id)aDelegate didSaveSelector:(SEL)aDidSaveSelector contextInfo:(id)aContextInfo +{ + var data = [self dataOfType:[self fileType] error:nil], + oldChangeCount = _changeCount; + + _writeRequest = [CPURLRequest requestWithURL:anAbsoluteURL]; + + [_writeRequest setHTTPMethod:@"POST"]; + [_writeRequest setHTTPBody:[data string]]; + + [_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"]; + + if (aSaveOperation == CPSaveOperation) + [_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"]; + + if (aSaveOperation != CPSaveToOperation) + [self updateChangeCount:CPChangeCleared]; + + // FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors. + var connection = [CPURLConnection connectionWithRequest:_writeRequest delegate:self]; + + connection.session = _CPSaveSessionMake(anAbsoluteURL, aSaveOperation, oldChangeCount, aDelegate, aDidSaveSelector, aContextInfo, connection); +} + +- (void)connection:(CPURLConnection)aConnection didReceiveResponse:(CPURLResponse)aResponse +{ + var statusCode = [aResponse statusCode]; + + // Nothing to do if everything is hunky dory. + if (statusCode == 200) + return; + + var session = aConnection.session; + + if (aConnection == _readConnection) + { + [aConnection cancel]; + + alert("There was an error retrieving the document."); + + objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo); + } + else + { + // 409: Conflict, in Cappuccino, overwrite protection for documents. + if (statusCode == 409) + { + [aConnection cancel]; + + if (confirm("There already exists a file with that name, would you like to overwrite it?")) + { + [_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"]; + + [aConnection start]; + } + else + { + if (session.saveOperation != CPSaveToOperation) + { + _changeCount += session.changeCount; + [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]]; + } + + _writeRequest = nil; + + objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo); + } + } + } +} + +- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData +{ + var session = aConnection.session; + + // READ + if (aConnection == _readConnection) + { + [self readFromData:[CPData dataWithString:aData] ofType:session.fileType error:nil]; + + objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo); + } + else + { + if (session.saveOperation != CPSaveToOperation) + [self setFileURL:session.absoluteURL]; + + _writeRequest = nil; + + objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo); + } +} + +- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError +{ + var session = aConnection.session; + + if (_readConnection == aConnection) + objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo); + + else + { + if (session.saveOperation != CPSaveToOperation) + { + _changeCount += session.changeCount; + [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]]; + } + + _writeRequest = nil; + + alert("There was an error saving the document."); + + objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo); + } +} + +- (void)connectionDidFinishLoading:(CPURLConnection)aConnection +{ + if (_readConnection == aConnection) + _readConnection = nil; +} + +// Managing Document Status + +- (BOOL)isDocumentEdited +{ + return _changeCount != 0; +} + +- (void)updateChangeCount:(CPDocumentChangeType)aChangeType +{ + if (aChangeType == CPChangeDone) + ++_changeCount; + else if (aChangeType == CPChangeUndone) + --_changeCount; + else if (aChangeType == CPChangeCleared) + _changeCount = 0; + /*else if (aChangeType == CPCHangeReadOtherContents) + + else if (aChangeType == CPChangeAutosaved)*/ + + [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]]; +} + +// Managing File Types + +- (void)setFileType:(CPString)aType +{ + _fileType = aType; +} + +- (CPString)fileType +{ + return _fileType; +} + +// Working with Undo Manager + +- (BOOL)hasUndoManager +{ + return _hasUndoManager; +} + +- (void)setHashUndoManager:(BOOL)aFlag +{ + if (_hasUndoManager == aFlag) + return; + + _hasUndoManager = aFlag; + + if (!_hasUndoManager) + [self setUndoManager:nil]; +} + +- (void)_undoManagerWillCloseGroup:(CPNotification)aNotification +{ + var undoManager = [aNotification object]; + + if ([undoManager isUndoing] || [undoManager isRedoing]) + return; + + [self updateChangeCount:CPChangeDone]; +} + +- (void)_undoManagerDidUndoChange:(CPNotification)aNotification +{ + [self updateChangeCount:CPChangeUndone]; +} + +- (void)_undoManagerDidRedoChange:(CPNotification)aNotification +{ + [self updateChangeCount:CPChangeDone]; +} + +- (void)setUndoManager:(CPUndoManager)anUndoManager +{ + var defaultCenter = [CPNotificationCenter defaultCenter]; + + if (_undoManager) + { + [defaultCenter removeObserver:self + name:CPUndoManagerDidUndoChangeNotification + object:_undoManager]; + + [defaultCenter removeObserver:self + name:CPUndoManagerDidRedoChangeNotification + object:_undoManager]; + + [defaultCenter removeObserver:self + name:CPUndoManagerWillCloseUndoGroupNotification + object:_undoManager]; + } + + _undoManager = anUndoManager; + + if (_undoManager) + { + + [defaultCenter addObserver:self + selector:@selector(_undoManagerDidUndoChange:) + name:CPUndoManagerDidUndoChangeNotification + object:_undoManager]; + + [defaultCenter addObserver:self + selector:@selector(_undoManagerDidRedoChange:) + name:CPUndoManagerDidRedoChangeNotification + object:_undoManager]; + + [defaultCenter addObserver:self + selector:@selector(_undoManagerWillCloseGroup:) + name:CPUndoManagerWillCloseUndoGroupNotification + object:_undoManager]; + } +} + +- (CPUndoManager)undoManager +{ + if (_hasUndoManager && !_undoManager) + [self setUndoManager:[[CPUndoManager alloc] init]]; + + return _undoManager; +} + +- (CPUndoManager)windowWillReturnUndoManager:(CPWindow)aWindow +{ + return [self undoManager]; +} + +// Handling User Actions + +- (void)saveDocument:(id)aSender +{ + if (_fileURL) + { + [[CPNotificationCenter defaultCenter] + postNotificationName:CPDocumentWillSaveNotification + object:self]; + + [self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL]; + } + else + [self saveDocumentAs:self]; +} + +- (void)saveDocumentAs:(id)aSender +{ + _documentName = window.prompt("Document Name:"); + + if (!_documentName) + return; + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPDocumentWillSaveNotification + object:self]; + + [self saveToURL:[self proposedFileURL] ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL]; +} + +- (void)document:(id)aDocument didSave:(BOOL)didSave contextInfo:(id)aContextInfo +{ + if (didSave) + [[CPNotificationCenter defaultCenter] + postNotificationName:CPDocumentDidSaveNotification + object:self]; + else + [[CPNotificationCenter defaultCenter] + postNotificationName:CPDocumentDidFailToSaveNotification + object:self]; +} + +@end + +var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo) +{ + return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo }; +} + +var _CPSaveSessionMake = function(anAbsoluteURL, aSaveOperation, aChangeCount, aDelegate, aDidSaveSelector, aContextInfo, aConnection) +{ + return { absoluteURL:anAbsoluteURL, saveOperation:aSaveOperation, changeCount:aChangeCount, delegate:aDelegate, didSaveSelector:aDidSaveSelector, contextInfo:aContextInfo, connection:aConnection }; +} diff --git a/AppKit/CPDocumentController.j b/AppKit/CPDocumentController.j new file mode 100644 index 0000000000..0445f96ca6 --- /dev/null +++ b/AppKit/CPDocumentController.j @@ -0,0 +1,184 @@ +/* + * CPDocumentController.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + +import "CPDocument.j" + +var CPSharedDocumentController = nil; + +@implementation CPDocumentController : CPObject +{ + CPArray _documents; + CPArray _documentTypes; +} + ++ (id)sharedDocumentController +{ + if (!CPSharedDocumentController) + [[self alloc] init]; + + return CPSharedDocumentController; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + _documents = [[CPArray alloc] init]; + + if (!CPSharedDocumentController) + CPSharedDocumentController = self; + + _documentTypes = [[[CPBundle mainBundle] infoDictionary] objectForKey:@"CPBundleDocumentTypes"]; + } + return self; +} + +// Creating and Opening Documents + +- (CPDocument)documentForURL:(CPURL)aURL +{ + var index = 0, + count = [_documents count]; + + for (; index < count; ++index) + { + var theDocument = _documents[index]; + + if ([[theDocument fileURL] isEqual:aURL]) + return theDocument; + } + + return nil; +} + +- (void)openUntitledDocumentOfType:(CPString)aType display:(BOOL)shouldDisplay +{ + var document = [self makeUntitledDocumentOfType:aType error:nil]; + + if (document) + [self addDocument:document]; + + if (shouldDisplay) + { + [document makeWindowControllers]; + [document showWindows]; + } + + return document; +} + +- (CPDocument)makeUntitledDocumentOfType:(CPString)aType error:({CPError})anError +{ + return [[[self documentClassForType:aType] alloc] initWithType:aType error:anError]; +} + +- (CPDocument)openDocumentWithContentsOfURL:(CPURL)anAbsoluteURL display:(BOOL)shouldDisplay error:(CPError)anError +{ + var result = [self documentForURL:anAbsoluteURL]; + + if (!result) + // FIXME: type(!) + result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:nil]; + + else if (shouldDisplay) + [result showWindows]; + + return result; +} + +- (CPDocument)reopenDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL error:(CPError)anError +{ + return [self makeDocumentForURL:anAbsoluteURL withContentsOfURL:absoluteContentsURL ofType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:nil]; +} + +- (CPDocument)makeDocumentWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo +{ + return [[[self documentClassForType:aType] alloc] initWithContentsOfURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aSelector contextInfo:aContextInfo]; +} + +- (CPDocument)makeDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo +{ + return [[[self documentClassForType:aType] alloc] initForURL:anAbsoluteURL withContentsOfURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aSelector contextInfo:aContextInfo]; +} + +- (void)document:(CPDocument)aDocument didRead:(BOOL)didRead contextInfo:(id)aContextInfo +{ + if (!didRead) + return; + + [self addDocument:aDocument]; + [aDocument makeWindowControllers]; +} + +- (CFAction)newDocument:(id)aSender +{ + [self openUntitledDocumentOfType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] display:YES]; +} + +// Managing Documents + +- (CPArray)documents +{ + return _documents; +} + +- (void)addDocument:(CPDocument)aDocument +{ + [_documents addObject:aDocument]; +} + +- (void)removeDocument:(CPDocument)aDocument +{ + [_documents removeObjectIdenticalTo:aDocument]; +} + +// Managing Document Types + +- (CPDictionary)_infoForType:(CPString)aType +{ + var i = 0, + count = [_documentTypes count]; + + for (;i < count; ++i) + { + var documentType = _documentTypes[i]; + + if ([documentType objectForKey:@"CPBundleTypeName"] == aType) + return documentType; + } + + return nil; +} + +- (Class)documentClassForType:(CPString)aType +{ + var className = [[self _infoForType:aType] objectForKey:@"CPDocumentClass"]; + + return className ? CPClassFromString(className) : Nil; +} + +@end diff --git a/AppKit/CPDragServer.j b/AppKit/CPDragServer.j new file mode 100644 index 0000000000..f9a0ca12b8 --- /dev/null +++ b/AppKit/CPDragServer.j @@ -0,0 +1,263 @@ +/* + * CPDragServer.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import +import + +var CPSharedDragServer = nil; + +var CPDragServerView = nil, + CPDragServerSource = nil, + CPDragServerWindow = nil, + CPDragServerOffset = nil, + CPDragServerLocation = nil, + CPDragServerPasteboard = nil, + CPDragServerDestination = nil, + CPDragServerDraggingInfo = nil; + +var CPDragServerIsDraggingImage = NO, + + CPDragServerShouldSendDraggedViewMovedTo = NO, + CPDragServerShouldSendDraggedImageMovedTo = NO, + + CPDragServerShouldSendDraggedViewEndedAtOperation = NO, + CPDragServerShouldSendDraggedImageEndedAtOperation = NO; + +var CPDragServerStartDragging = function(anEvent) +{ + CPDragServerUpdateDragging(anEvent); +} + +var CPDragServerUpdateDragging = function(anEvent) +{ + // If this is a mouse up, then complete the drag. + if([anEvent type] == CPLeftMouseUp) + { + CPDragServerLocation = [[CPDragServerDestination window] convertBridgeToBase:[[anEvent window] convertBaseToBridge:[anEvent locationInWindow]]]; + + [CPDragServerView removeFromSuperview]; + [CPSharedDragServer._dragWindow orderOut:nil]; + + if (CPDragServerDestination && + (![CPDragServerDestination respondsToSelector:@selector(prepareForDragOperation:)] || [CPDragServerDestination prepareForDragOperation:CPDragServerDraggingInfo]) && + (![CPDragServerDestination respondsToSelector:@selector(performDragOperation:)] || [CPDragServerDestination performDragOperation:CPDragServerDraggingInfo]) && + [CPDragServerDestination respondsToSelector:@selector(concludeDragOperation:)]) + [CPDragServerDestination concludeDragOperation:CPDragServerDraggingInfo]; + + if (CPDragServerShouldSendDraggedImageEndedAtOperation) + [CPDragServerSource draggedImage:[CPDragServerView image] endedAt:CPDragServerLocation operation:NO]; + else if (CPDragServerShouldSendDraggedViewEndedAtOperation) + [CPDragServerSource draggedView:CPDragServerView endedAt:CPDragServerLocation operation:NO]; + + CPDragServerIsDraggingImage = NO; + CPDragServerDestination = nil; + + return; + } + + // If we're not a mouse up, then we're going to want to grab the next event. + [CPApp setCallback:CPDragServerUpdateDragging + forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask + untilDate:nil inMode:0 dequeue:NO]; + + var location = [anEvent locationInWindow], + operation = + bridgeLocation = [[anEvent window] convertBaseToBridge:location]; + + // We have to convert base to bridge since the drag event comes from the source window, not the drag window. + var draggingDestination = [[CPDOMWindowBridge sharedDOMWindowBridge] _dragHitTest:bridgeLocation pasteboard:CPDragServerPasteboard]; + + CPDragServerLocation = [[CPDragServerDestination window] convertBridgeToBase:bridgeLocation]; + + if(draggingDestination != CPDragServerDestination) + { + if (CPDragServerDestination && [CPDragServerDestination respondsToSelector:@selector(draggingExited:)]) + [CPDragServerDestination draggingExited:CPDragServerDraggingInfo]; + + CPDragServerDestination = draggingDestination; + + if (CPDragServerDestination && [CPDragServerDestination respondsToSelector:@selector(draggingEntered:)]) + [CPDragServerDestination draggingEntered:CPDragServerDraggingInfo]; + } + else if (CPDragServerDestination && [CPDragServerDestination respondsToSelector:@selector(draggingUpdated:)]) + [CPDragServerDestination draggingUpdated:CPDragServerDraggingInfo]; + + location.x -= CPDragServerOffset.x; + location.y -= CPDragServerOffset.y; + + [CPDragServerView setFrameOrigin:location]; + + if (CPDragServerShouldSendDraggedImageMovedTo) + [CPDragServerSource draggedImage:[CPDragServerView image] movedTo:location]; + else if (CPDragServerShouldSendDraggedViewMovedTo) + [CPDragServerSource draggedView:CPDragServerView movedTo:location]; +} + +@implementation CPDraggingInfo : CPObject +{ + CPWindow _window; +} + +- (id)initWithWindow:(CPWindow)aWindow +{ + self = [super init]; + + if (self) + _window = aWindow; + + return this; +} + +- (id)draggingSource +{ + return CPDragServerSource; +} + +- (CPPoint)draggingLocation +{ + return CPDragServerLocation; +} + +- (CPPasteboard)draggingPasteboard +{ + return CPDragServerPasteboard; +} + +@end + +@implementation CPDragServer : CPObject +{ + CPWindow _dragWindow; + CPImageView _imageView; +} + ++ (void)initialize +{ + if (self != [CPDragServer class]) + return; + + CPDragServerDraggingInfo = [[CPDraggingInfo alloc] init]; +} + ++ (CPDragServer)sharedDragServer +{ + if (!CPSharedDragServer) + CPSharedDragServer = [[CPDragServer alloc] init]; + + return CPSharedDragServer; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + _dragWindow = [[CPWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:CPBorderlessWindowMask]; + [_dragWindow setLevel:CPDraggingWindowLevel]; + } + + return self; +} + +- (void)dragView:(CPView)aView fromWindow:(CPWindow)aWindow at:(CPPoint)viewLocation offset:(CPSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack +{ + var eventLocation = [anEvent locationInWindow]; + + CPDragServerView = aView; + CPDragServerSource = aSourceObject; + CPDragServerWindow = aWindow; + CPDragServerOffset = CPPointMake(eventLocation.x - viewLocation.x, eventLocation.y - viewLocation.y); + CPDragServerPasteboard = [CPPasteboard pasteboardWithName:CPDragPboard];//aPasteboard; + + [_dragWindow setFrameSize:CGSizeMakeCopy([[CPDOMWindowBridge sharedDOMWindowBridge] frame].size)]; + [_dragWindow orderFront:self]; + + [aView setFrameOrigin:viewLocation]; + [[_dragWindow contentView] addSubview:aView]; + + if (CPDragServerIsDraggingImage) + { + if ([CPDragServerSource respondsToSelector:@selector(draggedImage:beganAt:)]) + [CPDragServerSource draggedImage:[aView image] beganAt:viewLocation]; + + CPDragServerShouldSendDraggedImageMovedTo = [CPDragServerSource respondsToSelector:@selector(draggedImage:movedTo:)]; + CPDragServerShouldSendDraggedImageEndedAtOperation = [CPDragServerSource respondsToSelector:@selector(draggedImage:endAt:operation:)]; + + CPDragServerShouldSendDraggedViewMovedTo = NO; + CPDragServerShouldSendDraggedViewEndedAtOperation = NO; + } + else + { + if ([CPDragServerSource respondsToSelector:@selector(draggedView:beganAt:)]) + [CPDragServerSource draggedView:aView beganAt:viewLocation]; + + CPDragServerShouldSendDraggedViewMovedTo = [CPDragServerSource respondsToSelector:@selector(draggedView:movedTo:)]; + CPDragServerShouldSendDraggedViewEndedAtOperation = [CPDragServerSource respondsToSelector:@selector(draggedView:endedAt:operation:)]; + + + CPDragServerShouldSendDraggedImageMovedTo = NO; + CPDragServerShouldSendDraggedImageEndedAtOperation = NO; + } + + CPDragServerStartDragging(anEvent); +} + +- (void)dragImage:(CPImage)anImage fromWindow:(CPWindow)aWindow at:(CPPoint)imageLocation offset:(CPSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack +{ + CPDragServerIsDraggingImage = YES; + + if (!_imageView) + _imageView = [[CPImageView alloc] initWithFrame:CPRectMakeZero()]; + + [_imageView setImage:anImage]; + [_imageView setFrameSize:CGSizeMakeCopy([anImage size])]; + + [self dragView:_imageView fromWindow:aWindow at:imageLocation offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack]; +} + +@end + +@implementation CPView (CPDraggingAdditions) + +- (CPView)_dragHitTest:(CPPoint)aPoint pasteboard:(CPPasteboard)aPasteboard +{ + if(!CPRectContainsPoint(_frame, aPoint) || self == CPDragServerView) + return nil; + + var view = nil, + i = [_subviews count], + adjustedPoint = CPPointMake(aPoint.x - CPRectGetMinX(_frame), aPoint.y - CPRectGetMinY(_frame)); + + while (i--) + if (view = [_subviews[i] _dragHitTest:adjustedPoint pasteboard:aPasteboard]) + return view; + + if ([aPasteboard availableTypeFromArray:_registeredDraggedTypes]) + return self; + + return nil; +} + +@end diff --git a/AppKit/CPEvent.j b/AppKit/CPEvent.j new file mode 100644 index 0000000000..56d5469cc3 --- /dev/null +++ b/AppKit/CPEvent.j @@ -0,0 +1,326 @@ +/* + * CPEvent.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +#include "CoreGraphics/CGGeometry.h" + + +CPLeftMouseDown = 1; +CPLeftMouseUp = 2; +CPRightMouseDown = 3; +CPRightMouseUp = 4; +CPMouseMoved = 5; +CPLeftMouseDragged = 6; +CPRightMouseDragged = 7; +CPMouseEntered = 8; +CPMouseExited = 9; +CPKeyDown = 10; +CPKeyUp = 11; +CPFlagsChanged = 12; +CPAppKitDefined = 13; +CPSystemDefined = 14; +CPApplicationDefined = 15; +CPPeriodic = 16; +CPCursorUpdate = 17; +CPScrollWheel = 22; +CPOtherMouseDown = 25; +CPOtherMouseUp = 26; +CPOtherMouseDragged = 27; + +CPAlphaShiftKeyMask = 1 << 16; +CPShiftKeyMask = 1 << 17; +CPControlKeyMask = 1 << 18; +CPAlternateKeyMask = 1 << 19; +CPCommandKeyMask = 1 << 20; +CPNumericPadKeyMask = 1 << 21; +CPHelpKeyMask = 1 << 22; +CPFunctionKeyMask = 1 << 23; +CPDeviceIndependentModifierFlagsMask = 0xffff0000; + +CPLeftMouseDownMask = 1 << CPLeftMouseDown; +CPLeftMouseUpMask = 1 << CPLeftMouseUp; +CPRightMouseDownMask = 1 << CPRightMouseDown; +CPRightMouseUpMask = 1 << CPRightMouseUp; +CPOtherMouseDownMask = 1 << CPOtherMouseDown; +CPOtherMouseUpMask = 1 << CPOtherMouseUp; +CPMouseMovedMask = 1 << CPMouseMoved; +CPLeftMouseDraggedMask = 1 << CPLeftMouseDragged; +CPRightMouseDraggedMask = 1 << CPRightMouseDragged; +CPOtherMouseDragged = 1 << CPOtherMouseDragged; +CPMouseEnteredMask = 1 << CPMouseEntered; +CPMouseExitedMask = 1 << CPMouseExited; +CPCursorUpdateMask = 1 << CPCursorUpdate; +CPKeyDownMask = 1 << CPKeyDown; +CPKeyUpMask = 1 << CPKeyUp; +CPFlagsChangedMask = 1 << CPFlagsChanged; +CPAppKitDefinedMask = 1 << CPAppKitDefined; +CPSystemDefinedMask = 1 << CPSystemDefined; +CPApplicationDefinedMask = 1 << CPApplicationDefined; +CPPeriodicMask = 1 << CPPeriodic; +CPScrollWheelMask = 1 << CPScrollWheel; +CPAnyEventMask = 0xffffffff; + +CPDOMEventDoubleClick = "dblclick", +CPDOMEventMouseDown = "mousedown", +CPDOMEventMouseUp = "mouseup", +CPDOMEventMouseMoved = "mousemove", +CPDOMEventMouseDragged = "mousedrag", +CPDOMEventKeyUp = "keyup", +CPDOMEventKeyDown = "keydown", +CPDOMEventKeyPress = "keypress"; +CPDOMEventCopy = "copy"; +CPDOMEventPaste = "paste"; +CPDOMEventScrollWheel = "mousewheel"; + +var _CPEventPeriodicEventPeriod = 0, + _CPEventPeriodicEventTimer = nil; + +@implementation CPEvent : CPObject +{ + CPEventType _type; + CPPoint _location; + unsigned _modifierFlags; + CPTimeInterval _timestamp; + CPGraphicsContext _context; + int _eventNumber; + unsigned _clickCount; + float _pressure; + CPWindow _window; + CPString _characters; + CPString _charactersIgnoringModifiers + BOOL _isARepeat; + unsigned _keyCode; + DOMEvent _DOMEvent; + + float _deltaX; + float _deltaY; + float _deltaZ; +} + ++ (CPEvent)keyEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned int)modifierFlags + timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext + characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code +{ + return [[self alloc] _initKeyEventWithType:anEventType location:aPoint modifierFlags:modifierFlags + timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext + characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code]; +} + ++ (id)mouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags + timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext + eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure +{ + return [[self alloc] _initMouseEventWithType:anEventType location:aPoint modifierFlags:modifierFlags + timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber clickCount:aClickCount pressure:aPressure]; +} + ++ (CPEvent)otherEventWithType:(CPEventType)anEventType location:(CGPoint)aLocation modifierFlags:(unsigned)modifierFlags + timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext + subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2 +{ + return [[self alloc] _initOtherEventWithType:anEventType location:aLocation modifierFlags:modifierFlags + timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext subtype:aSubtype data1:aData1 data2:aData2]; +} + +- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags + timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext + eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure +{ + self = [super init]; + + if (self) + { + _type = anEventType; + _location = CPPointCreateCopy(aPoint); + _modifierFlags = modifierFlags; + _timestamp = aTimestamp; + _context = aGraphicsContext; + _eventNumber = anEventNumber; + _clickCount = aClickCount; + _pressure = aPressure; + _window = [CPApp windowWithWindowNumber:aWindowNumber]; + } + + return self; +} + +- (id)_initKeyEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned int)modifierFlags + timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext + characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code +{ + self = [super init]; + + if (self) + { + _type = anEventType; + _location = CPPointCreateCopy(aPoint); + _modifierFlags = modifierFlags; + _timestamp = aTimestamp; + _context = aGraphicsContext; + _characters = characters; + _charactersIgnoringModifiers = unmodCharacters; + _isARepeat = isARepeat; + _keyCode = code; + _window = [CPApp windowWithWindowNumber:aWindowNumber]; + } + + return self; +} + +- (id)_initOtherEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags + timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext + subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2 +{ + self = [super init]; + + if (self) + { + _type = anEventType; + _location = CPPointCreateCopy(aPoint); + _modifierFlags = modifierFlags; + _timestamp = aTimestamp; + _context = aGraphicsContext; + _subtype = aSubtype; + _data1 = aData1; + _data2 = aData2; + } + + return self; +} + +- (CPPoint)locationInWindow +{ + return _location; +} + +- (unsigned)modifierFlags +{ + return _modifierFlags; +} + +- (CPTimeInterval)timestamp +{ + return _timestamp; +} + +- (CPEventType)type +{ + return _type; +} + +- (CPWindow)window +{ + return _window; +} + +- (int)windowNumber +{ + return _windowNumber; +} + +// Mouse Event Information + +- (int)buttonNumber +{ + return _buttonNumber; +} + +- (int)clickCount +{ + return _clickCount; +} + +- (CPString)characters +{ + return _characters; +} + +- (CPString)charactersIgnoringModifiers +{ + return _charactersIgnoringModifiers; +} + +- (BOOL)isARepeat +{ + return _isARepeat; +} + +- (unsigned short)keyCode +{ + return _keyCode; +} + +- (float)pressure +{ + return _pressure; +} + +- (DOMEvent)_DOMEvent +{ + return _DOMEvent; +} + +// Getting Scroll Wheel Event Infomration + +- (float)deltaX +{ + return _deltaX; +} + +- (float)deltaY +{ + return _deltaY; +} + +- (float)deltaZ +{ + return _deltaZ; +} + ++ (void)startPeriodicEventsAfterDelay:(CPTimeInterval)aDelay withPeriod:(CPTimeInterval)aPeriod +{ + _CPEventPeriodicEventPeriod = aPeriod; + + // FIXME: OH TIMERS!!! + _CPEventPeriodicEventTimer = window.setTimeout(function() { _CPEventPeriodicEventTimer = window.setInterval(_CPEventFirePeriodEvent, aPeriod * 1000.0); }, aDelay * 1000.0); +} + ++ (void)stopPeriodicEvents +{ + if (_CPEventPeriodicEventTimer === nil) + return; + + window.clearTimeout(_CPEventPeriodicEventTimer); + + _CPEventPeriodicEventTimer = nil; +} + +@end + +function _CPEventFirePeriodEvent() +{ + [CPApp sendEvent:[CPEvent otherEventWithType:CPPeriodic location:_CGPointMakeZero() modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]]; + + [[CPRunLoop currentRunLoop] performSelectors]; +} + diff --git a/AppKit/CPFlashMovie.j b/AppKit/CPFlashMovie.j new file mode 100644 index 0000000000..278d0664cf --- /dev/null +++ b/AppKit/CPFlashMovie.j @@ -0,0 +1,46 @@ +/* + * CPFlashMovie.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + + +@implementation CPFlashMovie : CPObject +{ + CPString _fileName; +} + ++ (id)flashMovieWithFile:(CPString)aFileName +{ + return [[self alloc] initWithFile:aFileName]; +} + +- (id)initWithFile:(CPString)aFileName +{ + self = [super init]; + + if (self) + _fileName = aFileName; + + return self; +} + +@end \ No newline at end of file diff --git a/AppKit/CPFlashView.j b/AppKit/CPFlashView.j new file mode 100644 index 0000000000..95db3b0397 --- /dev/null +++ b/AppKit/CPFlashView.j @@ -0,0 +1,110 @@ +/* + * CPFlashView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPDOMWindowBridge.j" +import "CPFlashMovie.j" +import "CPView.j" + + +@implementation CPFlashView : CPView +{ + CPFlashMovie _flashMovie; + + DOMElement _DOMEmbedElement; + DOMElement _DOMMParamElement; + DOMElement _DOMObjectElement; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + 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.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); + } + + return self; +} + +- (void)setFlashMovie:(CPFlashMovie)aFlashMovie +{ + if (_flashMovie == aFlashMovie) + return; + + _flashMovie = aFlashMovie; + + _DOMParamElement.value = aFlashMovie._fileName; + + if (_DOMEmbedElement) + _DOMEmbedElement.src = aFlashMovie._fileName; +} + +- (CPFlashMovie)flashMovie +{ + return _flashMovie; +} + +- (void)mouseDragged:(CPEvent)anEvent +{ + [[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES]; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + [[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES]; +} + +- (void)mouseUp:(CPEvent)anEvent +{ + [[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES]; +} + +@end diff --git a/AppKit/CPFont.j b/AppKit/CPFont.j new file mode 100644 index 0000000000..fa247d67cb --- /dev/null +++ b/AppKit/CPFont.j @@ -0,0 +1,112 @@ +/* + * CPFont.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +var _CPFonts = {}; + _CPFontSystemFontFace = @"Arial"; + +#define _CPCachedFont(aName, aSize, isBold) _CPFonts[(isBold ? @"bold " : @"") + ROUND(aSize) + @"px '" + aName + @"'"] + +@implementation CPFont : CPObject +{ + CPString _name; + float _size; + BOOL _isBold; + + CPString _cssString; +} + ++ (id)fontWithName:(CPString)aName size:(float)aSize +{ + return _CPCachedFont(aName, aSize, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:NO]; +} + ++ (id)boldFontWithName:(CPString)aName size:(float)aSize +{ + return _CPCachedFont(aName, aSize, YES) || [[CPFont alloc] _initWithName:aName size:aSize bold:YES]; +} + ++ (id)systemFontOfSize:(CPSize)aSize +{ + return _CPCachedFont(_CPFontSystemFontFace, aSize, NO) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:NO]; +} + ++ (id)boldSystemFontOfSize:(CPSize)aSize +{ + return _CPCachedFont(_CPFontSystemFontFace, aSize, YES) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:YES]; +} +// FIXME Font Descriptor +- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold +{ + self = [super init]; + + if (self) + { + _name = aName; + _size = aSize; + _isBold = isBold; + + _cssString = (_isBold ? @"bold " : @"") + ROUND(aSize) + @"px '" + aName + @"'"; + + _CPFonts[_cssString] = self; + } + + return self; +} + +- (float)size +{ + return _size; +} + +- (CPString)cssString +{ + return _cssString; +} + +- (CPString)familyName +{ + return _name; +} + +@end + +var CPFontNameKey = @"CPFontNameKey", + CPFontSizeKey = @"CPFontSizeKey", + CPFontIsBoldKey = @"CPFontIsBoldKey"; + +@implementation CPFont (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self _initWithName:[aCoder decodeObjectForKey:CPFontNameKey] + size:[aCoder decodeFloatForKey:CPFontSizeKey] + bold:[aCoder decodeBoolForKey:CPFontIsBoldKey]]; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_name forKey:CPFontNameKey]; + [aCoder encodeFloat:_size forKey:CPFontSizeKey]; + [aCoder encodeBool:_isBold forKey:CPFontIsBoldKey]; +} + +@end diff --git a/AppKit/CPFontManager.j b/AppKit/CPFontManager.j new file mode 100644 index 0000000000..0601129f51 --- /dev/null +++ b/AppKit/CPFontManager.j @@ -0,0 +1,158 @@ +/* + * CPFontManager.j + * AppKit + * + * Created by Tom Robinson. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import + + +var CPSharedFontManager = nil, + CPFontManagerFactory = Nil; + +@implementation CPFontManager : CPObject +{ + CPArray _availableFonts; +} + +// Getting the Shared Font Manager + ++ (id)sharedFontManager +{ + if (!CPSharedFontManager) + CPSharedFontManager = [[CPFontManagerFactory alloc] init]; + + return CPSharedFontManager; +} + +// Changing the Default Font Conversion Classes + ++ (void)setFontManagerFactory:(Class)aClass +{ + CPFontManagerFactory = aClass; +} + +- (CPArray)availableFonts +{ + if (!_availableFonts) + { + _CPFontDetectSpan = document.createElement("span"); + _CPFontDetectSpan.fontSize = "24px"; + _CPFontDetectSpan.appendChild(document.createTextNode("mmmmmmmmmml")); + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.top = "-1000px"; + div.appendChild(_CPFontDetectSpan); + document.getElementsByTagName("body")[0].appendChild(div); + + _CPFontDetectReferenceFonts = _CPFontDetectPickTwoDifferentFonts(["monospace", "serif", "sans-serif", "cursive"]); + + _availableFonts = []; + for (var i = 0; i < _CPFontDetectAllFonts.length; i++) { + var available = _CPFontDetectFontAvailable(_CPFontDetectAllFonts[i]); + if (available) + _availableFonts.push(_CPFontDetectAllFonts[i]); + } + } + return _availableFonts; +} + +- (CPArray)fontWithNameIsAvailable:(CPString)aFontName +{ + return _CPFontDetectFontAvailable(aFontName); +} + +@end + +var _CPFontDetectSpan, + _CPFontDetectReferenceFonts, + _CPFontDetectAllFonts = [ + /* "04b_21","A Charming Font","Abadi MT Condensed","Abadi MT Condensed Extra Bold","Abadi MT Condensed Light","Academy Engraved LET","Agency FB","Alba","Alba Matter","Alba Super","Algerian",*/ + "American Typewriter", + /* "Andale Mono","Andale Mono IPA","Andy", */ + "Apple Chancery","Arial","Arial Black","Arial Narrow","Arial Rounded MT Bold","Arial Unicode MS", + /* "Avant Garde","Avantgarde","Baby Kruffy","Base 02","Baskerville","Baskerville Old Face","Bauhaus 93","Beesknees ITC","Bell MT","Berlin Sans FB","Berlin Sans FB Demi","Bernard MT Condensed","Bickley Script",*/ + "Big Caslon","Bitstream Vera Sans","Bitstream Vera Sans Mono","Bitstream Vera Serif", + /* "Blackadder ITC","Blackletter686 BT","Bodoni MT","Bodoni MT Black","Bodoni MT Condensed","Bodoni MT Poster Compressed","Book Antiqua","Bookman","Bookman Old Style","Bradley Hand ITC","Braggadocio","Britannic Bold","Broadway","Broadway BT",*/ + "Brush Script MT", + /* "BudHand","CAMPBELL","Calibri","Californian FB","Calisto MT","Calligraph421 BT",*/ + "Cambria", + /* "Candara","Capitals",*/ + "Caslon","Castellar","Cataneo BT","Centaur","Century Gothic","Century Schoolbook","Century Schoolbook L", + /* "Champignon","Charcoal","Charter","Charter BT","Chicago","Chick","Chiller","ClearlyU","Colonna MT",*/ + "Comic Sans", "Comic Sans MS","Consolas","Constantia","Cooper Black","Copperplate","Copperplate Gothic Bold","Copperplate Gothic Light","Corbel","Courier","Courier New", + /* "Croobie","Curlz MT","Desdemona","Didot","DomBold BT","Edwardian Script ITC","Engravers MT","Eras Bold ITC","Eras Demi ITC","Eras Light ITC","Eras Medium ITC","Eurostile","FIRSTHOME","Fat","Felix Titling","Fine Hand","Fixed","Footlight MT Light","Forte","Franklin Gothic Book","Franklin Gothic Demi","Franklin Gothic Demi Cond","Franklin Gothic Heavy","Franklin Gothic Medium","Franklin Gothic Medium Cond","Freestyle Script","French Script MT","Freshbot","Frosty",*/ + "Futura", + /* "GENUINE","Gadget","Garamond",*/ + "Geneva","Georgia","Georgia Ref", "Geeza Pro", "Gigi","Gill Sans","Gill Sans MT","Gill Sans MT Condensed","Gill Sans MT Ext Condensed Bold","Gill Sans Ultra Bold","Gill Sans Ultra Bold Condensed", + /* "GlooGun","Gloucester MT Extra Condensed","Goudy Old Style","Goudy Stout","Haettenschweiler","Harlow Solid Italic","Harrington",*/ + "Helvetica","Helvetica Narrow","Helvetica Neue","Herculanum","High Tower Text","Highlight LET","Hoefler Text","Impact","Imprint MT Shadow", + /* "Informal Roman","Jenkins v2.0","John Handy LET","Jokerman","Jokerman LET","Jokewood","Juice ITC","Kabel Ult BT","Kartika","Kino MT","Kristen ITC","Kunstler Script","La Bamba LET", */ + "Lucida","Lucida Bright","Lucida Calligraphy","Lucida Console","Lucida Fax","Lucida Grande","Lucida Handwriting","Lucida Sans","Lucida Sans Typewriter","Lucida Sans Unicode", + /* "Luxi Mono","Luxi Sans","Luxi Serif","MARKETPRO","MS Reference Sans Serif","MS Reference Serif","Magneto","Maiandra GD", */ + "Marker Felt", + /* "Matisse ITC","Matura MT Script Capitals","Mead Bold","Mekanik LET","Mercurius Script MT Bold", */ + "Microsoft Sans Serif","Milano LET","Minion Web","MisterEarl BT","Mistral","Monaco","Monotype Corsiva","Monotype.com","New Century Schoolbook","New York","News Gothic MT", + /* "Niagara Engraved","Niagara Solid","Nimbus Mono L","Nimbus Roman No9 L","OCR A Extended","OCRB","Odessa LET","Old English Text MT","OldDreadfulNo7 BT","One Stroke Script LET","Onyx","Optima","Orange LET","Palace Script MT","Palatino","Palatino Linotype", */ + "Papyrus", + /* "ParkAvenue BT","Pepita MT","Perpetua","Perpetua Titling MT","Placard Condensed","Playbill","Poornut","Pristina","Pump Demi Bold LET","Pussycat","Quixley LET","Rage Italic","Rage Italic LET","Ravie","Rockwell","Rockwell Condensed","Rockwell Extra Bold","Ruach LET","Runic MT Condensed","Sand","Script MT Bold","Scruff LET","Segoe UI","Showcard Gothic","Skia","Smudger LET","Snap ITC","Square721 BT","Staccato222 BT","Stencil","Sylfaen", */ + "Tahoma","Techno","Tempus Sans ITC","Terminal","Textile","Times","Times New Roman","Tiranti Solid LET","Trebuchet MS", + /* "Tw Cen MT","Tw Cen MT Condensed","Tw Cen MT Condensed Extra Bold","URW Antiqua T","URW Bookman L","URW Chancery L","URW Gothic L","URW Palladio L","Univers","University Roman LET","Utopia", */ + "Verdana","Verdana Ref", /* "Victorian LET","Viner Hand ITC","Vivaldi","Vladimir Script","Vrinda","Weltron Urban","Westwood LET","Wide Latin","Zapf Chancery", */ + "Zapfino"]; + +// Compare against the reference fonts. Return true if it produces a different size than at least one of them. +var _CPFontDetectFontAvailable = function(font) { + for (var i = 0; i < _CPFontDetectReferenceFonts.length; i++) + if (_CPFontDetectCompareFonts(_CPFontDetectReferenceFonts[i], font)) + return true; + return false; +} + +var _CPFontDetectCache = {}; + +// Compares two given fonts. Returns true if they produce different sizes (i.e. fontA didn't fallback to fontB) +var _CPFontDetectCompareFonts = function(fontA, fontB) { + var a; + if (_CPFontDetectCache[fontA]) { + a = _CPFontDetectCache[fontA]; + } else { + _CPFontDetectSpan.style.fontFamily = '"' + fontA + '"'; + _CPFontDetectCache[fontA] = a = { w: _CPFontDetectSpan.offsetWidth, h: _CPFontDetectSpan.offsetHeight }; + } + + _CPFontDetectSpan.style.fontFamily= '"' + fontB + '", "' + fontA + '"'; + var bWidth = _CPFontDetectSpan.offsetWidth; + var bHeight = _CPFontDetectSpan.offsetHeight; + + return (a.w != bWidth || a.h != bHeight); +} + +// Test the candidate fonts pairwise until we find two that are different. Otherwise return the first. +var _CPFontDetectPickTwoDifferentFonts = function(candidates) { + for (var i = 0; i < candidates.length; i++) + for (var j = 0; j < i; j++) + if (_CPFontDetectCompareFonts(candidates[i], candidates[j])) + return [candidates[i], candidates[j]]; + return [candidates[0]]; +} + +[CPFontManager setFontManagerFactory:[CPFontManager class]]; diff --git a/AppKit/CPGeometry.j b/AppKit/CPGeometry.j new file mode 100644 index 0000000000..74a6e8834c --- /dev/null +++ b/AppKit/CPGeometry.j @@ -0,0 +1,264 @@ +/* + * CPGeometry.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CGGeometry.j" + +CPMinXEdge = 0; +CPMinYEdge = 1; +CPMaxXEdge = 2; +CPMaxYEdge = 3; + +// FIXME: the rest! +CPMakePoint = CGPointMake; +CPMakeSize = CGSizeMake; +CPMakeRect = CGRectMake; + +function CPPointCreateCopy(aPoint) +{ + return { x: aPoint.x, y: aPoint.y }; +} + +function CPPointMake(x, y) +{ + return { x: x, y: y }; +} + +function CPRectInset(aRect, dX, dY) +{ + return CPRectMake( aRect.origin.x + dX, aRect.origin.y + dY, + aRect.size.width - 2 * dX, aRect.size.height - 2*dY); +} + +function CPRectIntegral(aRect) +{ + // FIXME!!! + alert("CPRectIntegral unimplemented"); +} + +function CPRectIntersection(lhsRect, rhsRect) +{ + var intersection = CPRectMake( + Math.max(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)), + Math.max(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)), + 0, 0); + + intersection.size.width = Math.min(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)) - CPRectGetMinX(intersection); + intersection.size.height = Math.min(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect)) - CPRectGetMinY(intersection); + + return CPRectIsEmpty(intersection) ? CPRectMakeZero() : intersection; +} + +function CPRectCreateCopy(aRect) +{ + return { origin: CPPointCreateCopy(aRect.origin), size: CPSizeCreateCopy(aRect.size) }; +} + +function CPRectMake(x, y, width, height) +{ + return { origin: CPPointMake(x, y), size: CPSizeMake(width, height) }; +} + +function CPRectOffset(aRect, dX, dY) +{ + return CPRectMake(aRect.origin.x + dX, aRect.origin.y + dY, aRect.size.width, aRect.size.height); +} + +function CPRectStandardize(aRect) +{ + var width = CPRectGetWidth(aRect), + height = CPRectGetHeight(aRect), + standardized = CPRectCreateCopy(aRect); + + if (width < 0.0) + { + standardized.origin.x += width; + standardized.size.width = -width; + } + + if (height < 0.0) + { + standardized.origin.y += height; + standardized.size.height = -height; + } + + return standardized; +} + +function CPRectUnion(lhsRect, rhsRect) +{ + var minX = Math.min(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)), + minY = Math.min(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)), + maxX = Math.max(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)), + maxY = Math.max(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect)); + + return CPRectMake(minX, minY, maxX - minX, maxY - minY); +} + +function CPSizeCreateCopy(aSize) +{ + return { width: aSize.width, height: aSize.height }; +} + +function CPSizeMake(width, height) +{ + return { width: width, height: height }; +} + +function CPRectContainsPoint(aRect, aPoint) +{ + return aPoint.x >= CPRectGetMinX(aRect) && + aPoint.y >= CPRectGetMinY(aRect) && + aPoint.x < CPRectGetMaxX(aRect) && + aPoint.y < CPRectGetMaxY(aRect); +} + +function CPRectContainsRect(lhsRect, rhsRect) +{ + return CPRectEqualToRect(CPUnionRect(lhsRect, rhsRect), rhsRect); +} + +function CPPointEqualToPoint(lhsPoint, rhsPoint) +{ + return lhsPoint.x == rhsPoint.x && lhsPoint.y == rhsPoint.y; +} + +function CPRectEqualToRect(lhsRect, rhsRect) +{ + return CPPointEqualToPoint(lhsRect.origin, rhsRect.origin) && + CPSizeEqualToSize(lhsRect.size, rhsRect.size); +} + +function CPRectGetHeight(aRect) +{ + return aRect.size.height; +} + +function CPRectGetMaxX(aRect) +{ + return aRect.origin.x + aRect.size.width; +} + +function CPRectGetMaxY(aRect) +{ + return aRect.origin.y + aRect.size.height; +} + +function CPRectGetMidX(aRect) +{ + return aRect.origin.x + (aRect.size.width) / 2.0; +} + +function CPRectGetMidY(aRect) +{ + return aRect.origin.y + (aRect.size.height) / 2.0; +} + +function CPRectGetMinX(aRect) +{ + return aRect.origin.x; +} + +function CPRectGetMinY(aRect) +{ + return aRect.origin.y; +} + +function CPRectGetWidth(aRect) +{ + return aRect.size.width; +} + +function CPRectIntersectsRect(lhsRect, rhsRect) +{ + return !CPRectIsEmpty(CPRectIntersection(lhsRect, rhsRect)); +} + +function CPRectIsEmpty(aRect) +{ + return aRect.size.width <= 0.0 || aRect.size.height <= 0.0; +} + +function CPRectIsNull(aRect) +{ + return aRect.size.width <= 0.0 || aRect.size.height <= 0.0; +} + +function CPSizeEqualToSize(lhsSize, rhsSize) +{ + return lhsSize.width == rhsSize.width && lhsSize.height == rhsSize.height; +} + +function CPStringFromPoint(aPoint) +{ + return "{" + aPoint.x + ", " + aPoint.y + "}"; +} + +function CPStringFromSize(aSize) +{ + return "{" + aSize.width + ", " + aSize.height + "}"; +} + +function CPStringFromRect(aRect) +{ + return "{" + CPStringFromPoint(aRect.origin) + ", " + CPStringFromSize(aRect.size) + "}"; +} + +function CPPointFromString(aString) +{ + var comma = aString.indexOf(','); + + return { x:parseInt(aString.substr(1, comma - 1)), y:parseInt(aString.substring(comma + 1, aString.length)) }; +} + +function CPSizeFromString(aString) +{ + var comma = aString.indexOf(','); + + return { width:parseInt(aString.substr(1, comma - 1)), height:parseInt(aString.substring(comma + 1, aString.length)) }; +} + +function CPRectFromString(aString) +{ + var comma = aString.indexOf(',', aString.indexOf(',') + 1); + + return { origin:CPPointFromString(aString.substr(1, comma - 1)), size:CPSizeFromString(aString.substring(comma + 2, aString.length)) }; +} + +function CPPointFromEvent(anEvent) +{ + return CPPointMake(anEvent.clientX, anEvent.clientY, 0); +} + +function CPSizeMakeZero() +{ + return CPSizeMake(0, 0); +} + +function CPRectMakeZero() +{ + return CPRectMake(0, 0, 0, 0); +} + +function CPPointMakeZero() +{ + return CPPointMake(0, 0, 0); +} diff --git a/AppKit/CPGraphicsContext.j b/AppKit/CPGraphicsContext.j new file mode 100644 index 0000000000..6b4d77f1d0 --- /dev/null +++ b/AppKit/CPGraphicsContext.j @@ -0,0 +1,60 @@ +/* + * CPGraphicsContext.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +var CPGraphicsContextCurrent = nil; + +@implementation CPGraphicsContext : CPObject +{ + CPContext _graphicsPort; +} + ++ (void)currentContext +{ + return CPGraphicsContextCurrent; +} + ++ (void)setCurrentContext:(CPGraphicsContext)aGraphicsContext +{ + CPGraphicsContextCurrent = aGraphicsContext; +} + ++ (id)graphicsContextWithGraphicsPort:(CPContext)aContext flipped:(BOOL)aFlag +{ + return [[self alloc] initWithGraphicsPort:aContext]; +} + +- (id)initWithGraphicsPort:(CPContext)aGraphicsPort +{ + self = [super init]; + + if (self) + _graphicsPort = aGraphicsPort; + + return self; +} + +- (CPContext)graphicsPort +{ + return _graphicsPort; +} + +@end diff --git a/AppKit/CPImage.j b/AppKit/CPImage.j new file mode 100644 index 0000000000..2089602a4d --- /dev/null +++ b/AppKit/CPImage.j @@ -0,0 +1,323 @@ +/* + * CPImage.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import +import + +import "CPGeometry.j" + +CPImageLoadStatusInitialized = 0; +CPImageLoadStatusLoading = 1; +CPImageLoadStatusCompleted = 2; +CPImageLoadStatusCancelled = 3; +CPImageLoadStatusInvalidData = 4; +CPImageLoadStatusUnexpectedEOF = 5; +CPImageLoadStatusReadError = 6; + +@implementation CPBundle (CPImageAdditions) + +- (CPString)pathForResource:(CPString)aFilename +{ + return [self bundlePath] + "Resources/" + aFilename; +} + +@end + +@implementation CPImage : CPObject +{ + CPSize _size; + CPString _filename; + + id _delegate; + unsigned _loadStatus; + + Image _image; +} + +- (CPImage)initByReferencingFile:(CPString)aFilename size:(CPSize)aSize +{ + self = [super init]; + + if (self) + { + _size = CPSizeCreateCopy(aSize); + _filename = aFilename; + _loadStatus = CPImageLoadStatusInitialized; + } + + return self; +} + +- (CPImage)initWithContentsOfFile:(CPString)aFilename size:(CPSize)aSize +{ + self = [self initByReferencingFile:aFilename size:aSize]; + + if (self) + [self load]; + + return self; +} + +- (CPImage)initWithContentsOfFile:(CPString)aFilename +{ + self = [self initByReferencingFile:aFilename size: CGSizeMake(-1, -1)]; + + if (self) + [self load]; + + return self; +} + +- (CPString)filename +{ + return _filename; +} + +- (void)setSize:(CPSize)aSize +{ + _size = CGSizeMakeCopy(aSize); +} + +- (CGSize)size +{ + return _size; +} + +- (void)setDelegate:(id)aDelegate +{ + _delegate = aDelegate; +} + +- (id)delegate +{ + return _delegate; +} + +- (BOOL)loadStatus +{ + return _loadStatus; +} + +- (void)load +{ + if (_loadStatus == CPImageLoadStatusLoading || _loadStatus == CPImageLoadStatusCompleted) + return; + + _loadStatus = CPImageLoadStatusLoading; + + _image = new Image(); + + var isSynchronous = YES; + + // FIXME: We need a better/performance way of doing this. + _image.onload = function () { if (isSynchronous) window.setTimeout(function() { [self _imageDidLoad] }, 0); else [self _imageDidLoad]; } + _image.onerror = function () { if (isSynchronous) window.setTimeout(function() { [self _imageDidError] }, 0); else [self _imageDidError]; } + _image.onabort = function () { if (isSynchronous) window.setTimeout(function() { [self _imageDidAbort] }, 0); else [self _imageDidAbort]; } + + _image.src = _filename; + + isSynchronous = NO; +} + +- (BOOL)isThreePartImage +{ + return NO; +} + +- (BOOL)isNinePartImage +{ + return NO; +} + +- (void)_imageDidLoad +{ + _loadStatus = CPImageLoadStatusCompleted; + + // FIXME: IE is wrong on image sizes???? + if (!_size || (_size.width == -1 && _size.height == -1)) + _size = CGSizeMake(_image.width, _image.height); + + if ([_delegate respondsToSelector:@selector(imageDidLoad:)]) + [_delegate imageDidLoad:self]; + + [[CPRunLoop currentRunLoop] performSelectors]; +} + +- (void)_imageDidError +{ + _loadStatus = CPImageLoadStatusReadError; + + if ([_delegate respondsToSelector:@selector(imageDidError:)]) + [_delegate imageDidError:self]; + + [[CPRunLoop currentRunLoop] performSelectors]; +} + +- (void)_imageDidAbort +{ + _loadStatus = CPImageLoadStatusCancelled; + + if ([_delegate respondsToSelector:@selector(imageDidAbort:)]) + [_delegate imageDidAbort:self]; + + [[CPRunLoop currentRunLoop] performSelectors]; +} + +@end + +@implementation CPImage (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]]; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_filename forKey:@"CPFilename"]; + [aCoder encodeSize:_size forKey:@"CPSize"]; +} + +@end + +@implementation CPThreePartImage : CPObject +{ + CPArray _imageSlices; + BOOL _isVertical; +} + +- (id)initWithImageSlices:(CPArray)imageSlices isVertical:(BOOL)isVertical +{ + self = [super init]; + + if (self) + { + _imageSlices = imageSlices; + _isVertical = isVertical; + } + + return self; +} + +- (CPArray)imageSlices +{ + return _imageSlices; +} + +- (BOOL)isVertical +{ + return _isVertical; +} + +- (BOOL)isThreePartImage +{ + return YES; +} + +- (BOOL)isNinePartImage +{ + return NO; +} + +@end + +var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey", + CPThreePartImageIsVerticalKey = @"CPThreePartImageIsVerticalKey"; + +@implementation CPThreePartImage (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + { + _imageSlices = [aCoder decodeObjectForKey:CPThreePartImageImageSlicesKey]; + _isVertical = [aCoder decodeBoolForKey:CPThreePartImageIsVerticalKey]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_imageSlices forKey:CPThreePartImageImageSlicesKey]; + [aCoder encodeBool:_isVertical forKey:CPThreePartImageIsVerticalKey]; +} + +@end + + +@implementation CPNinePartImage : CPObject +{ + CPArray _imageSlices; +} + +- (id)initWithImageSlices:(CPArray)imageSlices +{ + self = [super init]; + + if (self) + _imageSlices = imageSlices; + + return self; +} + +- (CPArray)imageSlices +{ + return _imageSlices; +} + +- (BOOL)isThreePartImage +{ + return NO; +} + +- (BOOL)isNinePartImage +{ + return YES; +} + +@end + +var CPNinePartImageImageSlicesKey = @"CPNinePartImageImageSlicesKey"; + +@implementation CPNinePartImage (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + _imageSlices = [aCoder decodeObjectForKey:CPNinePartImageImageSlicesKey]; + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_imageSlices forKey:CPNinePartImageImageSlicesKey]; +} + +@end diff --git a/AppKit/CPImageView.j b/AppKit/CPImageView.j new file mode 100644 index 0000000000..d39f3f4b1c --- /dev/null +++ b/AppKit/CPImageView.j @@ -0,0 +1,307 @@ +/* + * CPImageView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPImage.j" +import "CPControl.j" +import "CPShadowView.j" + +#include "Platform/Platform.h" +#include "Platform/DOM/CPDOMDisplayServer.h" + +#include "CoreGraphics/CGGeometry.h" + + +CPScaleProportionally = 0; +CPScaleToFit = 1; +CPScaleNone = 2; + + +var CPImageViewShadowBackgroundColor = nil; + +var LEFT_SHADOW_INSET = 3.0, + RIGHT_SHADOW_INSET = 3.0, + TOP_SHADOW_INSET = 3.0, + BOTTOM_SHADOW_INSET = 5.0, + VERTICAL_SHADOW_INSET = TOP_SHADOW_INSET + BOTTOM_SHADOW_INSET, + HORIZONTAL_SHADOW_INSET = LEFT_SHADOW_INSET + RIGHT_SHADOW_INSET; + +@implementation CPImageView : CPControl +{ + CPImage _image; + DOMElement _DOMImageElement; + + CPImageScaling _imageScaling; + + BOOL _hasShadow; + CPView _shadowView; + + CGRect _imageRect; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { +#if PLATFORM(DOM) + _DOMImageElement = document.createElement("img"); + _DOMImageElement.style.position = "absolute"; + _DOMImageElement.style.left = "0px"; + _DOMImageElement.style.top = "0px"; + + CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement); + + _DOMImageElement.style.visibility = "hidden"; +#endif + } + + return self; +} + +- (CPImage)image +{ + return _image; +} + +- (void)setImage:(CPImage)anImage +{ + if (_image == anImage) + return; + + _image = anImage; + + _DOMImageElement.src = [anImage filename]; + + if (_imageScaling == CPScaleNone && _image) + { + var size = [_image size]; + + _DOMImageElement.width = ROUND(size.width); + _DOMImageElement.height = ROUND(size.height); + } + + [self hideOrDisplayContents]; + + [self tile]; +} + +- (void)setHasShadow:(BOOL)shouldHaveShadow +{ + if (_hasShadow == shouldHaveShadow) + return; + + _hasShadow = shouldHaveShadow; + + if (_hasShadow) + { + _shadowView = [[CPShadowView alloc] initWithFrame:[self bounds]]; + + [self addSubview:_shadowView]; + + [self tile]; + } + else + { + [_shadowView removeFromSuperview]; + + _shadowView = nil; + } + + [self hideOrDisplayContents]; +} + +- (void)setImageScaling:(CPImageScaling)anImageScaling +{ + if (_imageScaling == anImageScaling) + return; + + _imageScaling = anImageScaling; + + if (_imageScaling == CPScaleToFit) + { + CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, 0.0, 0.0); + } + else if (_imageScaling == CPScaleNone && _image) + { + var size = [_image size]; + + _DOMImageElement.width = ROUND(size.width); + _DOMImageElement.height = ROUND(size.height); + } + + [self tile]; +} + +- (CPImageScaling)imageScaling +{ + return _imageScaling; +} + +- (void)setFrameSize:(CGSize)aSize +{ + [super setFrameSize:aSize]; + + [self tile]; +} + +- (void)hideOrDisplayContents +{ + if (!_image) + { + _DOMImageElement.style.visibility = "hidden"; + [_shadowView setHidden:YES]; + } + else + { + _DOMImageElement.style.visibility = "visible"; + [_shadowView setHidden:NO]; + } +} + +- (CGRect)imageRect +{ + return _imageRect; +} + +- (void)tile +{ + if (!_image) + return; + + var bounds = [self bounds], + x = 0.0, + y = 0.0, + insetWidth = (_hasShadow ? HORIZONTAL_SHADOW_INSET : 0.0), + insetHeight = (_hasShadow ? VERTICAL_SHADOW_INSET : 0.0), + boundsWidth = _CGRectGetWidth(bounds), + boundsHeight = _CGRectGetHeight(bounds), + width = boundsWidth - insetWidth, + height = boundsHeight - insetHeight; + + if (_imageScaling == CPScaleToFit) + { + _DOMImageElement.width = ROUND(width); + _DOMImageElement.height = ROUND(height); + } + else + { + var size = [_image size]; + + if (_imageScaling == CPScaleProportionally) + { + // The max size it can be is size.width x size.height, so only + // only proportion otherwise. + if (width >= size.width && height >= size.height) + { + width = size.width; + height = size.height; + } + else + { + var imageRatio = size.width / size.height, + viewRatio = width / height; + + if (viewRatio > imageRatio) + width = height * imageRatio; + else + height = width / imageRatio; + } + + _DOMImageElement.width = ROUND(width); + _DOMImageElement.height = ROUND(height); + } + else + { + width = size.width; + height = size.height; + } + + var x = (boundsWidth - width) / 2.0, + y = (boundsHeight - height) / 2.0; + + CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, x, y); + } + + _imageRect = _CGRectMake(x, y, width, height); + + if (_hasShadow) + [_shadowView setFrame:_CGRectMake(x - LEFT_SHADOW_INSET, y - TOP_SHADOW_INSET, width + insetWidth, height + insetHeight)]; +} + +@end + +var CPImageViewImageKey = @"CPImageViewImageKey", + CPImageViewImageScalingKey = @"CPImageViewImageScalingKey", + CPImageViewHasShadowKey = @"CPImageViewHasShadowKey"; + +@implementation CPImageView (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + _DOMImageElement = document.createElement("img"); + _DOMImageElement.style.position = "absolute"; + _DOMImageElement.style.left = "0px"; + _DOMImageElement.style.top = "0px"; + + _DOMElement.appendChild(_DOMImageElement); + _DOMImageElement.style.visibility = "hidden"; + + [self setImage:[aCoder decodeObjectForKey:CPImageViewImageKey]]; + + [self setImageScaling:[aCoder decodeIntForKey:CPImageViewImageScalingKey]]; + [self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]]; + + [self tile]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + // We do this in order to avoid encoding the _shadowView, which + // should just automatically be created programmatically as needed. + if (_shadowView) + { + var actualSubviews = _subviews; + + _subviews = [_subviews copy]; + [_subviews removeObjectIdenticalTo:_shadowView]; + } + + [super encodeWithCoder:aCoder]; + + if (_shadowView) + _subviews = actualSubviews; + + [aCoder encodeObject:_image forKey:CPImageViewImageKey]; + + [aCoder encodeInt:_imageScaling forKey:CPImageViewImageScalingKey]; + [aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey]; +} + +@end diff --git a/AppKit/CPKulerColorPicker.j b/AppKit/CPKulerColorPicker.j new file mode 100644 index 0000000000..d1d6df9383 --- /dev/null +++ b/AppKit/CPKulerColorPicker.j @@ -0,0 +1,367 @@ +/* + * CPApplication.j + * AppKit + * + * Created by Ross Boucher. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import "CPView.j" +import "CPButton.j" +import "CPImage.j" +import "CPImageView.j" +import "CPColorPicker.j" +import "CPColorPanel.j" +import "CPTabView.j" +import "CPTabViewItem.j" + + +@implementation CPKulerColorPicker : CPColorPicker +{ + CPView _contentView; + CPTabView _tabView; + + CPScrollView _searchView; + CPScrollView _popularView; + + CPView _searchView; + DOMElement _FIXME_searchField; + + CPURLConnection _searchConnection; + CPURLConnection _popularConnection +} + +- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel +{ + return [super initWithPickerMask:mask colorPanel: owningColorPanel]; +} + +-(id)initView +{ + aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight); + _contentView = [[CPView alloc] initWithFrame:aFrame]; + + _tabView = [[CPTabView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(aFrame), CGRectGetHeight(aFrame) - 20)]; + [_tabView setAutoresizingMask: CPViewWidthSizable | CPViewHeightSizable]; + [_tabView setDelegate: self]; + [_contentView addSubview: _tabView]; + + var label = [[CPButton alloc] initWithFrame:CPRectMake((CPColorPickerViewWidth-150)/2, CPColorPickerViewHeight-20, 150, 20)]; + [label setAutoresizingMask: CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin]; + [label setFont:[CPFont boldSystemFontOfSize:10.0]]; + [label setTextColor: [CPColor whiteColor]]; + [label setTitle: @"Powered by Adobe Kuler"]; + [label setTarget: self]; + [label setAction: @selector(openKulerLink:)]; + [label setBordered: NO]; + [_contentView addSubview: label]; + + var searchThemeView = [[_CPKulerThemeView alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, 0)]; + [searchThemeView setDelegate: self]; + [searchThemeView setAutoresizingMask: CPViewWidthSizable]; + + _searchView = [[CPScrollView alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, CPColorPickerViewHeight - 10)]; + [_searchView setDocumentView: searchThemeView]; + [_searchView setHasHorizontalScroller: NO]; + [_searchView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)]; + [[_searchView verticalScroller] setControlSize:CPSmallControlSize]; + [_searchView setAutohidesScrollers:YES]; + + var auxiliarySearchView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, CPColorPickerViewWidth, 26.0)]; + [auxiliarySearchView setAutoresizingMask: (CPViewMinYMargin | CPViewWidthSizable)]; + + _FIXME_searchField = document.createElement("input"); + + if(!window.addEventListener) + _FIXME_searchField.type = "text"; + else + _FIXME_searchField.type = "search"; + + _FIXME_searchField.style.position = "absolute"; + _FIXME_searchField.style.width = "96%"; + _FIXME_searchField.style.left = "2%"; + _FIXME_searchField.style.top = "2px"; + _FIXME_searchField.onkeypress = function(aDOMEvent) + { + aDOMEvent = aDOMEvent || window.event; + if (aDOMEvent.keyCode == 13) + { + [self search]; + + if(aDOMEvent.preventDefault) + aDOMEvent.preventDefault(); + else if(aDOMEvent.stopPropagation) + aDOMEvent.stopPropagation(); + + _FIXME_searchField.blur(); + } + }; + + auxiliarySearchView._DOMElement.appendChild(_FIXME_searchField); + + var popularThemeView = [[_CPKulerThemeView alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, 0)]; + [popularThemeView setDelegate: self]; + [popularThemeView setAutoresizingMask: CPViewWidthSizable]; + + _popularView = [[CPScrollView alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, CPColorPickerViewHeight)]; + [_popularView setDocumentView: popularThemeView]; + [_popularView setHasHorizontalScroller: NO]; + [_popularView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)]; + [[_popularView verticalScroller] setControlSize:CPSmallControlSize]; + [_popularView setAutohidesScrollers:YES]; + + var mostPopularItem = [[CPTabViewItem alloc] initWithIdentifier:@"Popular"]; + var searchItem = [[CPTabViewItem alloc] initWithIdentifier:@"Search"]; + + [searchItem setLabel:@"Search"]; + [searchItem setView:_searchView]; + [searchItem setAuxiliaryView: auxiliarySearchView]; + + [mostPopularItem setLabel:@"Popular"]; + [mostPopularItem setView: _popularView]; + + [_tabView addTabViewItem: mostPopularItem]; + [_tabView addTabViewItem: searchItem]; + + [self popularThemes]; +} + +- (void)openKulerLink:(id)sender +{ + window.open("http://kuler.adobe.com"); +} + +- (CPView)provideNewView:(BOOL)initialRequest +{ + if (initialRequest) + [self initView]; + + return _contentView; +} + +- (int)currentMode +{ + return CPKulerColorPickerMode; +} + +- (BOOL)supportsMode:(int)mode +{ + return (mode == CPKulerColorPickerMode) ? YES : NO; +} + +-(void)search +{ + var query = escape(_FIXME_searchField.value); + + if(query.replace("%20", "") == "") + return; + + [_searchConnection cancel]; + + _searchConnection = nil; + _searchConnection = [CPURLConnection connectionWithRequest: + [CPURLRequest requestWithURL: BASE_URL + "kuler.php?mode=search&query="+query] + delegate: self]; +} + +-(void)popularThemes +{ + [_popularConnection cancel]; + _popularConnection = nil; + _popularConnection = [CPURLConnection connectionWithRequest: + [CPURLRequest requestWithURL: BASE_URL + "kuler.php?mode=popular"] delegate: self]; +} + +/* +-(void)topRatedThemes +{ + _connection = [CPURLConnection connectionWithRequest: + [CPURLRequest requestWithURL: BASE_URL + "kuler.php?mode=rating"] delegate:self]; +} +*/ + +- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)data +{ + var data = CPJSObjectCreateWithJSON(data); + + if(!data) + var themes = []; + else + var themes = data.result; + + if(aConnection == _popularConnection) + [[_popularView documentView] setThemes: themes]; + + if(aConnection == _searchConnection) + [[_searchView documentView] setThemes: themes]; +} + +- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError +{ + [self connectionDidFinishLoading:aConnection]; +} + +-(void)connectionDidFinishLoading:(CPURLConnection)aConnection +{ + +} + +-(void)chooseColor:(CPColor)aColor +{ + [[self colorPanel] setColor: aColor]; +} + +@end + +@implementation _CPKulerThemeView : CPView +{ + CPArray _themes; + id _delegate; + + CPColor _alternateBGColor; +} + +-(id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + _alternateBGColor = [CPColor colorWithCalibratedRed: 241.0/255.0 green: 245.0/255.0 blue: 250.0/255.0 alpha: 1.0]; + + return self; +} + +-(id)delegate +{ + return _delegate; +} + +-(void)setDelegate:(id)delegate +{ + _delegate = delegate; +} + +-(CPArray)themes +{ + return _themes; +} + +-(void)setThemes:(CPArray)themes +{ + _themes = themes; + [self updateDisplay]; +} + +-(void)updateDisplay +{ + var width = [self frame].size.width; + + var subviews = [self subviews]; + for(var i = [subviews count]-1; i >= 0; --i) + [subviews[i] removeFromSuperview]; + + for(var i=0, count = [_themes count]; i +import +import +import + +import "_CPImageAndTitleView.j" +import "CPApplication.j" +import "CPClipView.j" +import "CPMenuItem.j" +import "CPPanel.j" + +#include "Platform/Platform.h" + + +CPMenuDidAddItemNotification = @"CPMenuDidAddItemNotification"; +CPMenuDidChangeItemNotification = @"CPMenuDidChangeItemNotification"; +CPMenuDidRemoveItemNotification = @"CPMenuDidRemoveItemNotification"; + +CPMenuDidEndTrackingNotification = @"CPMenuDidEndTrackingNotification"; + +var MENUBAR_HEIGHT = 19.0; + +var _CPMenuBarVisible = NO, + _CPMenuBarTitle = @"", + _CPMenuBarIconImage = nil, + _CPMenuBarIconImageAlphaValue = 1.0, + _CPMenuBarSharedWindow = nil; + +@implementation CPMenu : CPObject +{ + CPString _title; + + CPMutableArray _items; + CPMenu _attachedMenu; + + BOOL _autoenablesItems; + BOOL _showsStateColumn; + + id _delegate; + + CPMenuItem _highlightedIndex; + _CPMenuWindow _menuWindow; +} + +// Managing the Menu Bar + ++ (BOOL)menuBarVisible +{ + return _CPMenuBarVisible; +} + ++ (void)setMenuBarVisible:(BOOL)menuBarShouldBeVisible +{ + if (_CPMenuBarVisible == menuBarShouldBeVisible) + return; + + _CPMenuBarVisible = menuBarShouldBeVisible; + + if (menuBarShouldBeVisible) + { + if (!_CPMenuBarSharedWindow) + _CPMenuBarSharedWindow = [[_CPMenuBarWindow alloc] init]; + + [_CPMenuBarSharedWindow setMenu:[CPApp mainMenu]]; + + [_CPMenuBarSharedWindow setTitle:_CPMenuBarTitle]; + [_CPMenuBarSharedWindow setIconImage:_CPMenuBarIconImage]; + [_CPMenuBarSharedWindow setIconImageAlphaValue:_CPMenuBarIconImageAlphaValue]; + + [_CPMenuBarSharedWindow orderFront:self]; + } + else + [_CPMenuBarWindow orderOut:self]; + +// FIXME: There must be a better way to do this. +#if PLATFORM(DOM) + [[CPDOMWindowBridge sharedDOMWindowBridge] _bridgeResizeEvent:nil]; +#endif +} + ++ (void)setMenuBarTitle:(CPString)aTitle +{ + _CPMenuBarTitle = aTitle; + [_CPMenuBarSharedWindow setTitle:_CPMenuBarTitle]; +} + ++ (CPString)menuBarTitle +{ + return _CPMenuBarTitle; +} + ++ (void)setMenuBarIconImage:(CPImage)anImage +{ + _CPMenuBarImage = anImage; + [_CPMenuBarSharedWindow setIconImage:anImage]; +} + ++ (CPImage)menuBarIconImage +{ + return _CPMenuBarImage; +} + ++ (void)_setMenuBarIconImageAlphaValue:(float)anAlphaValue +{ + _CPMenuBarIconImageAlphaValue = anAlphaValue; + [_CPMenuBarSharedWindow setIconImageAlphaValue:anAlphaValue]; +} + +- (float)menuBarHeight +{ + if (self == [CPApp mainMenu]) + return MENUBAR_HEIGHT; + + return 0.0; +} + +// Creating a CPMenu Object + +- (id)initWithTitle:(CPString)aTitle +{ + self = [super init]; + + if (self) + { + _title = aTitle; + _items = []; + + _autoenablesItems = YES; + _showsStateColumn = YES; + } + + return self; +} + +// Setting Up Menu Commands + +- (void)insertItem:(CPMenuItem)aMenuItem atIndex:(unsigned)anIndex +{ + var menu = [aMenuItem menu]; + + if (menu) + if (menu != self) + [CPException raise:CPInternalInconsistencyException reason:@"Attempted to insert item into menu that was already in another menu."]; + else + return; + + [aMenuItem setMenu:self]; + [_items insertObject:aMenuItem atIndex:anIndex]; + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPMenuDidAddItemNotification + object:self + userInfo:[CPDictionary dictionaryWithObject:anIndex forKey:@"CPMenuItemIndex"]]; + +} + +- (CPMenuItem)insertItemWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent atIndex:(unsigned)anIndex +{ + var item = [[CPMenuItem alloc] initWithTitle:aTitle action:anAction keyEquivalent:aKeyEquivalent]; + + [self insertItem:item atIndex:anIndex]; + + return item; +} + +- (void)addItem:(CPMenuItem)aMenuItem +{ + [self insertItem:aMenuItem atIndex:[_items count]]; +} + +- (CPMenuItem)addItemWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent +{ + return [self insertItemWithTitle:aTitle action:anAction keyEquivalent:aKeyEquivalent atIndex:[_items count]]; +} + +- (void)removeItem:(CPMenuItem)aMenuItem +{ + [self removeItemAtIndex:[_items indexOfObjectIdenticalTo:aMenuItem]]; +} + +- (void)removeItemAtIndex:(unsigned)anIndex +{ + if (anIndex < 0 || anIndex >= _items.length) + return; + + [_items[anIndex] setMenu:nil]; + [_items removeObjectAtIndex:anIndex]; + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPMenuDidAddItemNotification + object:self + userInfo:[CPDictionary dictionaryWithObject:anIndex forKey:@"CPMenuItemIndex"]]; +} + +- (void)itemChanged:(CPMenuItem)aMenuItem +{ + if ([aMenuItem menu] != self) + return; + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPMenuDidChangeItemNotification + object:self + userInfo:[CPDictionary dictionaryWithObject:[_items indexOfObjectIdenticalTo:aMenuItem] forKey:@"CPMenuItemIndex"]]; +} + +// Finding Menu Items + +- (CPMenuItem)menuWithTag:(int)aTag +{ + var index = [self indexOfItemWithTag:aTag]; + + if (index == CPNotFound) + return nil; + + return _items[index]; +} + +- (CPMenuItem)menuWithTitle:(CPString)aTitle +{ + var index = [self indexOfItemWithTitle:aTitle]; + + if (index == CPNotFound) + return nil; + + return _items[index]; +} + +- (CPMenuItem)itemAtIndex:(int)anIndex +{ + return [_items objectAtIndex:anIndex]; +} + +- (unsigned)numberOfItems +{ + return [_items count]; +} + +- (CPArray)itemArray +{ + return _items; +} + +// Finding Indices of Menu Items + +- (int)indexOfItem:(CPMenuItem)aMenuItem +{ + if ([aMenuItem menu] != self) + return CPNotFound; + + return [_items indexOfObjectIdenticalTo:aMenuItem]; +} + +- (int)indexOfItemWithTitle:(CPString)aTitle +{ + var index = 0, + count = _items.length; + + for (; index < count; ++index) + if ([_items[index] title] === aTitle) + return index; + + return CPNotFound; +} + +- (int)indexOfItemWithTag:(int)aTag +{ + var index = 0, + count = _items.length; + + for (; index < count; ++index) + if ([_items[index] tag] == aTag) + return index; + + return CPNotFound; +} + +- (int)indexOfItemWithTarget:(id)aTarget andAction:(SEL)anAction +{ + var index = 0, + count = _items.length; + + for (; index < count; ++index) + { + var item = _items[index]; + + if ([item target] == aTarget && (!anAction || [item action] == anAction)) + return index; + } + + return CPNotFound; +} + +- (int)indexOfItemWithRepresentedObject:(id)anObject +{ + var index = 0, + count = _items.length; + + for (; index < count; ++index) + if ([[_items[index] representedObject] isEqual:anObject]) + return index; + + return CPNotFound; +} + +- (int)indexOfItemWithSubmenu:(CPMenu)aMenu +{ + var index = 0, + count = _items.length; + + for (; index < count; ++index) + if ([_items[index] submenu] == aMenu) + return index; + + return CPNotFound; +} + +// Managing Submenus + +- (void)setSubmenu:(CPMenu)aMenu forItem:(CPMenuItem)aMenuItem +{ + [aMenuItem setTarget:aMenuItem]; + [aMenuItem setAction:@selector(submenuAction:)]; + + [aMenuItem setSubmenu:aMenu]; +} + +- (void)submenuAction:(id)aSender +{ + +} + +- (CPMenu)attachedMenu +{ + return _attachedMenu; +} + +- (BOOL)isAttached +{ + return _isAttached; +} + +- (CGPoint)locationOfSubmenu:(CPMenu)aMenu +{ + // FIXME: IMPLEMENT. +} + +- (CPMenu)supermenu +{ + return _supermenu; +} + +- (void)setSupermenu:(CPMenu)aMenu +{ + _supermenu = aMenu; +} + +- (BOOL)isTornOff +{ + return !_supermenu /* || offscreen(?) */ || self == [CPApp mainMenu]; +} + +// Enabling and Disabling Menu Items + +- (void)setAutoenablesItems:(BOOL)aFlag +{ + _autoenablesItems = aFlag; +} + +- (BOOL)autoenablesItems +{ + return _autoenablesItems; +} + +- (void)update +{ + +} + +// Managing the Title + +- (void)setTitle:(CPString)aTitle +{ + _title = aTitle; +} + +- (CPString)title +{ + return _title; +} + +// + ++ (void)popUpContextMenu:(CPMenu)aMenu withEvent:(CPEvent)anEvent forView:(CPView)aView +{ + [self popUpContextMenu:aMenu withEvent:anEvent forView:aView withFont:nil]; +} + ++ (void)popUpContextMenu:(CPMenu)aMenu withEvent:(CPEvent)anEvent forView:(CPView)aView withFont:(CPFont)aFont +{ + [self _popUpContextMenu:aMenu withEvent:anEvent forView:aView withFont:aFont forMenuBar:NO]; +} + ++ (void)_popUpContextMenu:(CPMenu)aMenu withEvent:(CPEvent)anEvent forView:(CPView)aView withFont:(CPFont)aFont forMenuBar:(BOOL)isForMenuBar +{ + var delegate = [aMenu delegate]; + + if ([delegate respondsToSelector:@selector(menuWillOpen:)]) + [delegate menuWillOpen:aMenu]; + + if (!aFont) + aFont = [CPFont systemFontOfSize:12.0]; + + var theWindow = [aView window], + menuWindow = [_CPMenuWindow menuWindowWithMenu:aMenu font:aFont]; + + [menuWindow setDelegate:self]; + [menuWindow setBackgroundStyle:isForMenuBar ? _CPMenuWindowMenuBarBackgroundStyle : _CPMenuWindowPopUpBackgroundStyle]; + + [menuWindow setFrameOrigin:[[anEvent window] convertBaseToBridge:[anEvent locationInWindow]]]; + + [menuWindow orderFront:self]; + [menuWindow beginTrackingWithEvent:anEvent sessionDelegate:self didEndSelector:@selector(_menuWindowDidFinishTracking:highlightedItem:)]; +} + ++ (void)_menuWindowDidFinishTracking:(_CPMenuWindow)aMenuWindow highlightedItem:(CPMenuItem)aMenuItem +{ + [_CPMenuWindow poolMenuWindow:aMenuWindow]; + +// var index = [self indexOfItem:aMenuItem]; + +// if (index == CPNotFound) +// return; + + var target = nil, + action = [aMenuItem action]; + + if (!action) + { +// target = [self target]; +// action = [self action]; + } + + // FIXME: If [selectedItem target] == nil do we use our own target? + else + target = [aMenuItem target]; + + [CPApp sendAction:action to:target from:nil]; +} + +// Managing Display of State Column + +- (void)setShowsStateColumn:(BOOL)shouldShowStateColumn +{ + _showsStateColumn = shouldShowStateColumn; +} + +- (BOOL)showsStateColumn +{ + return _showsStateColumn; +} + +// Handling Highlighting + +- (BOOL)highlightedItem +{ + return _highlightedIndex >= 0 ? _items[_highlightedIndex] : nil; +} + +// Managing the Delegate + +- (void)setDelegate:(id)aDelegate +{ + _delegate = aDelegate; +} + +- (id)delegate +{ + return _delegate; +} + +// Handling Tracking + +- (void)cancelTracking +{ + [_menuWindow cancelTracking]; +} + +- (void)_setMenuWindow:(_CPMenuWindow)aMenuWindow +{ + _menuWindow = aMenuWindow; +} + +- (BOOL)performKeyEquivalent:(CPEvent)anEvent +{ + if (_autoenablesItems) + [self update]; + + var index = 0, + count = _items.length, + characters = [anEvent charactersIgnoringModifiers], + modifierFlags = [anEvent modifierFlags]; + + for(; index < count; ++index) + { + var item = _items[index], + modifierMask = [item keyEquivalentModifierMask]; + + if ((modifierFlags & (CPShiftKeyMask | CPAlternateKeyMask | CPCommandKeyMask | CPControlKeyMask)) == modifierMask && + [characters caseInsensitiveCompare:[item keyEquivalent]] == CPOrderedSame) + { + if ([item isEnabled]) + [self performActionForItemAtIndex:index]; + else + { + //beep? + } + + return YES; + } + + if ([[item submenu] performKeyEquivalent:anEvent]) + return YES; + } + + return NO; +} + +// Simulating Mouse Clicks + +- (void)performActionForItemAtIndex:(unsigned)anIndex +{ + var item = _items[anIndex]; + + [CPApp sendAction:[item action] to:[item target] from:item]; +} + +// + +- (BOOL)_itemIsHighlighted:(CPMenuItem)aMenuItem +{ + return _items[_highlightedIndex] == aMenuItem; +} + +- (void)_highlightItemAtIndex:(int)anIndex +{ + var previousHighlightedIndex = _highlightedIndex; + + _highlightedIndex = anIndex; + + if (previousHighlightedIndex != CPNotFound) + [[_items[previousHighlightedIndex] _menuItemView] highlight:NO]; + + if (_highlightedIndex != CPNotFound) + [[_items[_highlightedIndex] _menuItemView] highlight:YES]; +} + +@end + + +var CPMenuTitleKey = @"CPMenuTitleKey", + CPMenuItemsKey = @"CPMenuItemsKey"; + +@implementation CPMenu (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + { + _title = [aCoder decodeObjectForKey:CPMenuTitleKey]; + _items = [aCoder decodeObjectForKey:CPMenuItemsKey]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_title forKey:CPMenuTitleKey]; + [aCoder encodeObject:_items forKey:CPMenuItemsKey]; +} + +@end + +var _CPMenuWindowPool = [], + _CPMenuWindowPoolCapacity = 5, + + _CPMenuWindowBackgroundColors = [], + + _CPMenuWindowScrollingStateUp = -1, + _CPMenuWindowScrollingStateDown = 1, + _CPMenuWindowScrollingStateNone = 0; + +_CPMenuWindowMenuBarBackgroundStyle = 0; +_CPMenuWindowPopUpBackgroundStyle = 1; +_CPMenuWindowAttachedMenuBackgroundStyle = 2; + +var STICKY_TIME_INTERVAL = 500, + + TOP_MARGIN = 5.0, + LEFT_MARGIN = 1.0, + RIGHT_MARGIN = 1.0, + BOTTOM_MARGIN = 5.0, + + SCROLL_INDICATOR_HEIGHT = 16.0; + +@implementation _CPMenuWindow : CPWindow +{ + _CPMenuView _menuView; + CPClipView _menuClipView; + CPView _lastMouseOverMenuView; + + CPImageView _moreAboveView; + CPImageView _moreBelowView; + + id _sessionDelegate; + SEL _didEndSelector; + + CPTimeInterval _startTime; + int _scrollingState; + CGPoint _lastScreenLocation; + + BOOL _isShowingTopScrollIndicator; + BOOL _isShowingBottomScrollIndicator; + BOOL _trackingCanceled; + + CGRect _unconstrainedFrame; +} + ++ (id)menuWindowWithMenu:(CPMenu)aMenu font:(CPFont)aFont +{ + var menuWindow = nil; + + if (_CPMenuWindowPool.length) + menuWindow = _CPMenuWindowPool.pop(); + else + menuWindow = [[_CPMenuWindow alloc] init]; + + [menuWindow setFont:aFont]; + [menuWindow setMenu:aMenu]; + + return menuWindow; +} + ++ (void)poolMenuWindow:(_CPMenuWindow)aMenuWindow +{ + if (!aMenuWindow || _CPMenuWindowPool.length >= _CPMenuWindowPoolCapacity) + return; + + _CPMenuWindowPool.push(aMenuWindow); +} + ++ (void)initialize +{ + if (self != [_CPMenuWindow class]) + return; + + var bundle = [CPBundle bundleForClass:self]; + + _CPMenuWindowMoreAboveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreAbove.png"] size:CGSizeMake(38.0, 18.0)]; + _CPMenuWindowMoreBelowImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreBelow.png"] size:CGSizeMake(38.0, 18.0)]; +} + +- (id)init +{ + self = [super initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessWindowMask]; + + if (self) + { + [self setLevel:CPTornOffMenuWindowLevel]; + [self setHasShadow:YES]; + [self setAcceptsMouseMovedEvents:YES]; + + _unconstrainedFrame = CGRectMakeZero(); + + var contentView = [self contentView]; + + _menuView = [[_CPMenuView alloc] initWithFrame:CGRectMakeZero()]; + + _menuClipView = [[CPClipView alloc] initWithFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, 0.0, 0.0)]; + [_menuClipView setDocumentView:_menuView]; + + [contentView addSubview:_menuClipView]; + + _moreAboveView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; + + [_moreAboveView setImage:_CPMenuWindowMoreAboveImage]; + [_moreAboveView setFrameSize:[_CPMenuWindowMoreAboveImage size]]; + + [contentView addSubview:_moreAboveView]; + + _moreBelowView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; + + [_moreBelowView setImage:_CPMenuWindowMoreBelowImage]; + [_moreBelowView setFrameSize:[_CPMenuWindowMoreBelowImage size]]; + + [contentView addSubview:_moreBelowView]; + } + + return self; +} + +- (void)setFont:(CPFont)aFont +{ + [_menuView setFont:aFont]; +} + +- (void)setBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle +{ + var color = _CPMenuWindowBackgroundColors[aBackgroundStyle]; + + if (!color) + { + var bundle = [CPBundle bundleForClass:[self class]]; + + if (aBackgroundStyle == _CPMenuWindowPopUpBackgroundStyle) + color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded0.png"] size:CGSizeMake(4.0, 4.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow1.png"] size:CGSizeMake(1.0, 4.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded2.png"] size:CGSizeMake(4.0, 4.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)] + ]]]; + + else if (aBackgroundStyle == _CPMenuWindowMenuBarBackgroundStyle) + color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 0.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 0.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 0.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)] + ]]]; + + _CPMenuWindowBackgroundColors[aBackgroundStyle] = color; + } + + [self setBackgroundColor:color]; +} + +- (void)setMenu:(CPMenu)aMenu +{ + [aMenu _setMenuWindow:self]; + [_menuView setMenu:aMenu]; + + var menuViewSize = [_menuView frame].size; + + [self setFrameSize:CGSizeMake(LEFT_MARGIN + menuViewSize.width + RIGHT_MARGIN, TOP_MARGIN + menuViewSize.height + BOTTOM_MARGIN)]; + + [_menuView scrollPoint:CGPointMake(0.0, 0.0)]; + [_menuClipView setFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, menuViewSize.width, menuViewSize.height)]; +} + +- (void)setMinWidth:(float)aWidth +{ + var size = [self frame].size; + + [self setFrameSize:CGSizeMake(MAX(size.width, aWidth), size.height)]; +} + +- (CGPoint)rectForItemAtIndex:(int)anIndex +{ + return [_menuView convertRect:[_menuView rectForItemAtIndex:anIndex] toView:nil]; +} + +- (void)orderFront:(id)aSender +{ + [self constrainToScreen]; + + [super orderFront:aSender]; +} + +- (void)constrainToScreen +{ + _unconstrainedFrame = CGRectMakeCopy([self frame]); + + var screenBounds = CGRectInset([[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds], 5.0, 5.0), + constrainedFrame = CGRectIntersection(_unconstrainedFrame, screenBounds), + menuViewOrigin = [self convertBaseToBridge:CGPointMake(LEFT_MARGIN, TOP_MARGIN)]; + + constrainedFrame.origin.x = CGRectGetMinX(_unconstrainedFrame); + constrainedFrame.size.width = CGRectGetWidth(_unconstrainedFrame); + + if (CGRectGetWidth(constrainedFrame) > CGRectGetWidth(screenBounds)) + constrainedFrame.size.width = CGRectGetWidth(screenBounds); + + if (CGRectGetMaxX(constrainedFrame) > CGRectGetMaxX(screenBounds)) + constrainedFrame.origin.x -= CGRectGetMaxX(constrainedFrame) - CGRectGetMaxX(screenBounds); + + if (CGRectGetMinX(constrainedFrame) < CGRectGetMinX(screenBounds)) + constrainedFrame.origin.x = CGRectGetMinX(screenBounds); + + [super setFrame:constrainedFrame]; + + var topMargin = TOP_MARGIN, + bottomMargin = BOTTOM_MARGIN, + + contentView = [self contentView], + bounds = [contentView bounds]; + + var moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + TOP_MARGIN, + moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - BOTTOM_MARGIN; + + if (moreAbove) + { + topMargin += SCROLL_INDICATOR_HEIGHT; + + var frame = [_moreAboveView frame]; + + [_moreAboveView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(frame)) / 2.0, (TOP_MARGIN + SCROLL_INDICATOR_HEIGHT - CGRectGetHeight(frame)) / 2.0)]; + } + + [_moreAboveView setHidden:!moreAbove]; + + if (moreBelow) + { + bottomMargin += SCROLL_INDICATOR_HEIGHT; + + [_moreBelowView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth([_moreBelowView frame])) / 2.0, CGRectGetHeight(bounds) - SCROLL_INDICATOR_HEIGHT - BOTTOM_MARGIN)]; + } + + [_moreBelowView setHidden:!moreBelow]; + + var clipFrame = CGRectMake(LEFT_MARGIN, topMargin, CGRectGetWidth(constrainedFrame) - LEFT_MARGIN - RIGHT_MARGIN, CGRectGetHeight(constrainedFrame) - topMargin - bottomMargin) + + [_menuClipView setFrame:clipFrame]; + [_menuView setFrameSize:CGSizeMake(CGRectGetWidth(clipFrame), CGRectGetHeight([_menuView frame]))]; + + [_menuView scrollPoint:CGPointMake(0.0, [self convertBaseToBridge:clipFrame.origin].y - menuViewOrigin.y)]; +} + +- (void)cancelTracking +{ + _trackingCanceled = YES; +} + +- (void)beginTrackingWithEvent:(CPEvent)anEvent sessionDelegate:(id)aSessionDelegate didEndSelector:(SEL)aDidEndSelector +{ + _startTime = [anEvent timestamp];//new Date(); + _scrollingState = _CPMenuWindowScrollingStateNone; + _trackingCanceled = NO; + + _sessionDelegate = aSessionDelegate; + _didEndSelector = aDidEndSelector; + + [self trackEvent:anEvent]; +} + +- (void)trackEvent:(CPEvent)anEvent +{ + var type = [anEvent type], + theWindow = [anEvent window], + screenLocation = theWindow ? [theWindow convertBaseToBridge:[anEvent locationInWindow]] : [anEvent locationInWindow]; + + if (type == CPPeriodic) + { + var constrainedBounds = CGRectInset([[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds], 5.0, 5.0); + + if (_scrollingState == _CPMenuWindowScrollingStateUp) + { + if (CGRectGetMinY(_unconstrainedFrame) < CGRectGetMinY(constrainedBounds)) + _unconstrainedFrame.origin.y += 10; + } + else if (_scrollingState == _CPMenuWindowScrollingStateDown) + if (CGRectGetMaxY(_unconstrainedFrame) > CGRectGetHeight(constrainedBounds)) + _unconstrainedFrame.origin.y -= 10; + + [self setFrame:_unconstrainedFrame]; + [self constrainToScreen]; + + screenLocation = _lastScreenLocation; + } + + _lastScreenLocation = screenLocation; + + var menu = [_menuView menu], + menuLocation = [self convertBridgeToBase:screenLocation], + activeItemIndex = [_menuView itemIndexAtPoint:[_menuView convertPoint:menuLocation fromView:nil]], + mouseOverMenuView = [[menu itemAtIndex:activeItemIndex] view]; + + // If we're over a custom menu view... + if (mouseOverMenuView) + { + if (!_lastMouseOverMenuView) + [menu _highlightItemAtIndex:CPNotFound]; + + if (_lastMouseOverMenuView != mouseOverMenuView) + { + [mouseOverMenuView mouseExited:anEvent]; + // FIXME: Possibly multiple of these? + [_lastMouseOverMenuView mouseEntered:anEvent]; + + _lastMouseOverMenuView = mouseOverMenuView; + } + + [self sendEvent:[CPEvent mouseEventWithType:type location:menuLocation modifierFlags:[anEvent modifierFlags] + timestamp:[anEvent timestamp] windowNumber:[self windowNumber] context:nil + eventNumber:0 clickCount:[anEvent clickCount] pressure:[anEvent pressure]]]; + } + else + { + if (_lastMouseOverMenuView) + { + [_lastMouseOverMenuView mouseExited:anEvent]; + _lastMouseOverMenuView = nil; + } + + [menu _highlightItemAtIndex:[_menuView itemIndexAtPoint:[_menuView convertPoint:[self convertBridgeToBase:screenLocation] fromView:nil]]]; + + if (type == CPMouseMoved || type == CPLeftMouseDragged || type == CPLeftMouseDown) + { + var frame = [self frame], + oldScrollingState = _scrollingState; + + _scrollingState = _CPMenuWindowScrollingStateNone; + + // If we're at or above of the top scroll indicator... + if (screenLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT) + _scrollingState = _CPMenuWindowScrollingStateUp; + + // If we're at or below the bottom scroll indicator... + else if (screenLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT) + _scrollingState = _CPMenuWindowScrollingStateDown; + + if (_scrollingState != oldScrollingState) + + if (_scrollingState == _CPMenuWindowScrollingStateNone) + [CPEvent stopPeriodicEvents]; + + else if (oldScrollingState == _CPMenuWindowScrollingStateNone) + [CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04]; + } + + else if (type == CPLeftMouseUp && ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL)) + { + // Stop these if they're still goin'. + if (_scrollingState != _CPMenuWindowScrollingStateNone) + [CPEvent stopPeriodicEvents]; + + [self cancelTracking]; + } + } + + if (_trackingCanceled) + { + var highlightedItem = [[_menuView menu] highlightedItem]; + + [menu _highlightItemAtIndex:CPNotFound]; + + // Clear these now so its faster next time around. + [_menuView setMenu:nil]; + + [self orderOut:self]; + + if (_sessionDelegate && _didEndSelector) + objj_msgSend(_sessionDelegate, _didEndSelector, self, highlightedItem); + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPMenuDidEndTrackingNotification + object:menu]; + + var delegate = [menu delegate]; + + if ([delegate respondsToSelector:@selector(menuDidClose:)]) + [delegate menuDidClose:menu]; + + return; + } + + [CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; +} + +@end + +@implementation _CPMenuView : CPView +{ + CPArray _menuItemViews; + + CPFont _font; +} + +/*- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + [self setAutoresizingMask:CPViewWidthSizable]; + + return self; +}*/ + +- (void)setFont:(CPFont)aFont +{ + _font = aFont; +} + +- (CGRect)rectForItemAtIndex:(int)anIndex +{ + return [_menuItemViews[anIndex == CPNotFound ? 0 : anIndex] frame]; +} + +- (int)itemIndexAtPoint:(CGPoint)aPoint +{ + var index = 0, + count = _menuItemViews.length; + + for (; index < count; ++index) + { + var view = _menuItemViews[index]; + + if ([view isHidden]) + continue; + + if (CGRectContainsPoint([view frame], aPoint)) + return index; + } + + return CPNotFound; +} + +- (void)setMenu:(CPMenu)aMenu +{ + [super setMenu:aMenu]; + + [_menuItemViews makeObjectsPerformSelector:@selector(removeFromSuperview)]; + + _menuItemViews = []; + + var menu = [self menu]; + + if (!menu) + return; + + var items = [menu itemArray], + index = 0, + count = [items count], + maxWidth = 0, + y = 0, + showsStateColumn = [menu showsStateColumn]; + + for (; index < count; ++index) + { + var item = items[index], + view = [item _menuItemView]; + + _menuItemViews.push(view); + + if ([item isHidden]) + continue; + + [view setFont:_font]; + [view setShowsStateColumn:showsStateColumn]; + [view synchronizeWithMenuItem]; + + [view setFrameOrigin:CGPointMake(0.0, y)]; + + [self addSubview:view]; + + var size = [view minSize], + width = size.width; + + if (maxWidth < width) + maxWidth = width; + + y += size.height; + } + + for (index = 0; index < count; ++index) + { + var view = _menuItemViews[index]; + + [view setFrameSize:CGSizeMake(maxWidth, CGRectGetHeight([view frame]))]; + } + + [self setAutoresizesSubviews:NO]; + [self setFrameSize:CGSizeMake(maxWidth, y)]; + [self setAutoresizesSubviews:YES]; +} + +@end + +var MENUBAR_HEIGHT = 29.0, + MENUBAR_MARGIN = 10.0, + MENUBAR_LEFT_MARGIN = 10.0, + MENUBAR_RIGHT_MARGIN = 10.0; + +var _CPMenuBarWindowBackgroundColor = nil, + _CPMenuBarWindowFont = nil; + +@implementation _CPMenuBarWindow : CPPanel +{ + CPMenu _menu; + CPView _highlightView; + + CPMenuItem _trackingMenuItem; + + CPImageView _iconImageView; + CPTextField _titleField; +} + ++ (void)initialize +{ + if (self != [_CPMenuBarWindow class]) + return; + + var bundle = [CPBundle bundleForClass:self]; + + _CPMenuBarWindowBackgroundColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackground.png"] size:CGSizeMake(1.0, 18.0)]]; + + _CPMenuBarWindowFont = [CPFont systemFontOfSize:11.0]; +} + +- (id)init +{ + var bridgeWidth = CGRectGetWidth([[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds]); + + self = [super initWithContentRect:CGRectMake(0.0, 0.0, bridgeWidth, MENUBAR_HEIGHT) styleMask:CPBorderlessWindowMask]; + + if (self) + { + // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/39-dont-allow-windows-to-go-above-menubar + [self setLevel:-1];//CPTornOffMenuWindowLevel]; + [self setAutoresizingMask:CPWindowWidthSizable]; + + var contentView = [self contentView]; + + [contentView setBackgroundColor:_CPMenuBarWindowBackgroundColor]; + [contentView setAutoresizesSubviews:NO]; + + [self setBecomesKeyOnlyIfNeeded:YES]; + + // + _iconImageView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 16.0, 16.0)]; + + [contentView addSubview:_iconImageView]; + + _titleField = [[_CPImageAndTitleView alloc] initWithFrame:CGRectMakeZero()]; + + [_titleField setFont:[CPFont boldSystemFontOfSize:12.0]]; + + [_titleField setImagePosition:CPImageLeft]; + [_titleField setAlignment:CPCenterTextAlignment]; + + [contentView addSubview:_titleField]; + } + + return self; +} + +- (void)setTitle:(CPString)aTitle +{ +#if PLATFORM(DOM) + var bundleName = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPBundleName"]; + + if (![bundleName length]) + document.title = aTitle; + else if ([aTitle length]) + document.title = aTitle + @" - " + bundleName; + else + document.title = bundleName; +#endif + + [_titleField setTitle:aTitle]; + [_titleField sizeToFit]; + + [self tile]; +} + +- (void)setIconImage:(CPImage)anImage +{ + [_iconImageView setImage:anImage]; + [_iconImageView setHidden:anImage == nil]; + + [self tile]; +} + +- (void)setIconImageAlphaValue:(float)anAlphaValue +{ + [_iconImageView setAlphaValue:anAlphaValue]; +} + +- (void)setMenu:(CPMenu)aMenu +{ + if (_menu == aMenu) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + if (_menu) + { + [defaultCenter + removeObserver:self + name:CPMenuDidAddItemNotification + object:_menu]; + + [defaultCenter + removeObserver:self + name:CPMenuDidChangeItemNotification + object:_menu]; + + [defaultCenter + removeObserver:self + name:CPMenuDidRemoveItemNotification + object:_menu]; + + var items = [_menu itemArray], + count = items.length; + + while (count--) + [[items[count] _menuItemView] removeFromSuperview]; + } + + _menu = aMenu; + + if (_menu) + { + [defaultCenter + addObserver:self + selector:@selector(menuDidAddItem:) + name:CPMenuDidAddItemNotification + object:_menu]; + + [defaultCenter + addObserver:self + selector:@selector(menuDidChangeItem:) + name:CPMenuDidChangeItemNotification + object:_menu]; + + [defaultCenter + addObserver:self + selector:@selector(menuDidRemoveItem:) + name:CPMenuDidRemoveItemNotification + object:_menu]; + } + + var contentView = [self contentView], + items = [_menu itemArray], + count = items.length; + + for (index = 0; index < count; ++index) + { + var item = items[index], + menuItemView = [item _menuItemView]; + + [menuItemView setShowsStateColumn:NO]; + [menuItemView setBelongsToMenuBar:YES]; + [menuItemView setFont:_CPMenuBarWindowFont]; + [menuItemView setHidden:[item isHidden]]; + + [menuItemView synchronizeWithMenuItem]; + + [contentView addSubview:menuItemView]; + } + + [self tile]; +} + +- (void)menuDidChangeItem:(CPNotification)aNotification +{ + var menuItem = [_menu itemAtIndex:[[aNotification userInfo] objectForKey:@"CPMenuItemIndex"]], + menuItemView = [menuItem _menuItemView]; + + [menuItemView setHidden:[menuItem isHidden]]; + [menuItemView synchronizeWithMenuItem]; + + [self tile]; +} + +- (CGRect)frameForMenuItem:(CPMenuItem)aMenuItem +{ + var frame = [[aMenuItem _menuItemView] frame]; + + frame.origin.x -= 5.0; + frame.origin.y = 0; + frame.size.width += 10.0; + frame.size.height = MENUBAR_HEIGHT; + + return frame; +} + +- (CPView)menuItemAtPoint:(CGPoint)aPoint +{ + var items = [_menu itemArray], + count = items.length; + + while (count--) + { + var item = items[count]; + + if ([item isHidden] || [item isSeparatorItem]) + continue; + + if (CGRectContainsPoint([self frameForMenuItem:item], aPoint)) + return item; + } + + return nil; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + _trackingMenuItem = [self menuItemAtPoint:[anEvent locationInWindow]]; + + if (![_trackingMenuItem isEnabled]) + return; + + if ([[_trackingMenuItem _menuItemView] eventOnSubmenu:anEvent]) + return [self showMenu:anEvent]; + + if ([_trackingMenuItem isEnabled]) + [self trackEvent:anEvent]; +} + +- (void)trackEvent:(CPEvent)anEvent +{ + var type = [anEvent type]; + + if (type == CPPeriodic) + return [self showMenu:anEvent]; + + var frame = [self frameForMenuItem:_trackingMenuItem], + menuItemView = [_trackingMenuItem _menuItemView], + onMenuItemView = CGRectContainsPoint(frame, [anEvent locationInWindow]); + + if (type == CPLeftMouseDown) + { + if ([_trackingMenuItem submenu] != nil) + { + // If the item has a submenu, but not direct action, a.k.a. a "pure" menu, simply show the menu. + if (![_trackingMenuItem action]) + return [self showMenu:anEvent]; + + // If this is a hybrid button/menu, show it in a bit... + [CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.5]; + } + + [menuItemView highlight:onMenuItemView]; + } + + else if (type == CPLeftMouseDragged) + { + if (!onMenuItemView && [_trackingMenuItem submenu]) + return [self showMenu:anEvent]; + + [menuItemView highlight:onMenuItemView]; + } + + else /*if (type == CPLeftMouseUp)*/ + { + [CPEvent stopPeriodicEvents]; + + [menuItemView highlight:NO]; + + if (onMenuItemView) + [CPApp sendAction:[_trackingMenuItem action] to:[_trackingMenuItem target] from:nil]; + + return; + } + + [CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; +} + +- (void)showMenu:(CPEvent)anEvent +{ + [CPEvent stopPeriodicEvents]; + + var frame = [self frameForMenuItem:_trackingMenuItem], + menuItemView = [_trackingMenuItem _menuItemView]; + + if (!_highlightView) + { + _highlightView = [[CPView alloc] initWithFrame:frame]; + + [_highlightView setBackgroundColor:[CPColor colorWithCalibratedRed:81.0 / 255.0 green:83.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]]; + } + else + [_highlightView setFrame:frame]; + + [[self contentView] addSubview:_highlightView positioned:CPWindowBelow relativeTo:menuItemView]; + + [menuItemView activate:YES]; + + var submenu = [_trackingMenuItem submenu]; + + [[CPNotificationCenter defaultCenter] + addObserver:self + selector:@selector(menuDidEndTracking:) + name:CPMenuDidEndTrackingNotification + object:submenu]; + + [CPMenu _popUpContextMenu:submenu + withEvent:[CPEvent mouseEventWithType:CPLeftMouseDown location:CGPointMake(CGRectGetMinX(frame), CGRectGetMaxY(frame)) + modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:[self windowNumber] + context:nil eventNumber:0 clickCount:[anEvent clickCount] pressure:[anEvent pressure]] + forView:[self contentView] + withFont:nil + forMenuBar:YES]; +} + +- (void)menuDidEndTracking:(CPNotification)aNotification +{ + [_highlightView removeFromSuperview]; + + [[_trackingMenuItem _menuItemView] activate:NO]; + + [[CPNotificationCenter defaultCenter] + removeObserver:self + name:CPMenuDidEndTrackingNotification + object:[aNotification object]]; +} + +- (void)tile +{ + var items = [_menu itemArray], + index = 0, + count = items.length, + + x = MENUBAR_LEFT_MARGIN, + y = 0.0, + isLeftAligned = YES; + + for (; index < count; ++index) + { + var item = items[index]; + + if ([item isSeparatorItem]) + { + x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN; + isLeftAligned = NO; + + continue; + } + + if ([item isHidden]) + continue; + + var menuItemView = [item _menuItemView], + frame = [menuItemView frame]; + + if (isLeftAligned) + { + [menuItemView setFrameOrigin:CGPointMake(x, (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)]; + + x += CGRectGetWidth([menuItemView frame]) + MENUBAR_MARGIN; + } + else + { + [menuItemView setFrameOrigin:CGPointMake(x - CGRectGetWidth(frame), (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)]; + + x = CGRectGetMinX([menuItemView frame]) - MENUBAR_MARGIN; + } + } + + var bounds = [[self contentView] bounds], + titleFrame = [_titleField frame]; + + if ([_iconImageView isHidden]) + [_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(titleFrame)) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)]; + else + { + var iconFrame = [_iconImageView frame], + iconWidth = CGRectGetWidth(iconFrame), + totalWidth = iconWidth + CGRectGetWidth(titleFrame); + + [_iconImageView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(iconFrame)) / 2.0)]; + [_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0 + iconWidth, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)]; + } +} + +- (void)setFrameSize:(CGSize)aSize +{ + [super setFrameSize:aSize]; + + [self tile]; +} + +@end diff --git a/AppKit/CPMenuItem.j b/AppKit/CPMenuItem.j new file mode 100644 index 0000000000..68050294e1 --- /dev/null +++ b/AppKit/CPMenuItem.j @@ -0,0 +1,897 @@ +/* + * CPMenuItem.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import + +import +import +import + + +@implementation CPMenuItem : CPObject +{ + CPString _title; + //CPAttributedString _attributedTitle; + + CPFont _font; + + id _target; + SEL _action; + + BOOL _isEnabled; + BOOL _isHidden; + + int _tag; + int _state; + + CPImage _image; + CPImage _alternateImage; + CPImage _onStateImage; + CPImage _offStateImage; + CPImage _mixedStateImage; + + CPMenu _submenu; + CPMenu _menu; + + CPString _keyEquivalent; + unsigned _keyEquivalentModifierMask; + + int _mnemonicLocation; + + BOOL _isAlternate; + int _indentationLevel; + + CPString _toolTip; + id _representedObject; + CPView _view; + + _CPMenuItemView _menuItemView; +} + +- (id)initWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent +{ + self = [super init]; + + if (self) + { + _title = aTitle; + _action = anAction; + + _isEnabled = YES; + + _state = CPOffState; + + _keyEquivalent = aKeyEquivalent; + _keyEquivalentModifierMask = CPPlatformActionKeyMask; + + _mnemonicLocation = CPNotFound; + } + + return self; +} + +// Enabling a Menu Item + +- (void)setEnabled:(BOOL)isEnabled +{ + if ([_menu autoenablesItems]) + return; + + _isEnabled = isEnabled; + + [_menuItemView setDirty]; + + [_menu itemChanged:self]; +} + +- (BOOL)isEnabled +{ + return _isEnabled; +} + +// Managing Hidden Status + +- (void)setHidden:(BOOL)isHidden +{ + _isHidden = isHidden; +} + +- (BOOL)isHidden +{ + return _isHidden; +} + +- (BOOL)isHiddenOrHasHiddenAncestor +{ + if (_isHidden) + return YES; + + if ([[[_menu supermenu] indexOfItemWithSubmenu:_menu] isHiddenOrHasHiddenAncestor]) + return YES; + + return NO; +} + +// Managing Target and Action + +- (void)setTarget:(id)aTarget +{ + _target = aTarget; +} + +- (id)target +{ + return _target; +} + +- (void)setAction:(SEL)anAction +{ + _action = anAction; +} + +- (SEL)action +{ + return _action; +} + +// Managing the Title + +- (void)setTitle:(CPString)aTitle +{ + _mnemonicLocation = CPNotFound; + + if (_title == aTitle) + return; + + _title = aTitle; + + [_menuItemView setDirty]; + + [_menu itemChanged:self]; +} + +- (CPString)title +{ + return _title; +} + +- (void)setFont:(CPFont)aFont +{ + if (_font == aFont) + return; + + _font = aFont; + + [_menu itemChanged:self]; + + [_menuItemView setDirty]; +} + +- (CPFont)font +{ + return _font; +} + +/* +- (void)setAttributedTitle:(CPAttributedString)aTitle +{ +} + +- (CPAttributedString)attributedTitle +{ +} +*/ + +// Managing the Tag + +- (void)setTag:(int)aTag +{ + _tag = aTag; +} + +- (int)tag +{ + return _tag; +} + +- (void)setState:(int)aState +{ + if (_state == aState) + return; + + _state = aState; + + [_menu itemChanged:self]; + + [_menuItemView setDirty]; +} + +- (int)state +{ + return _state; +} + +// Managing the Image + +- (void)setImage:(CPImage)anImage +{ + if (_image == anImage) + return; + + _image = anImage; + + [_menuItemView setDirty]; + + [_menu itemChanged:self]; +} + +- (CPImage)image +{ + return _image; +} + +- (void)setAlternateImage:(CPImage)anImage +{ + _alternateImage = anImage; +} + +- (CPImage)alternateImage +{ + return _alternateImage; +} + +- (void)setOnStateImage:(CPImage)anImage +{ + if (_onStateImage == anImage) + return; + + _onStateImage = anImage; + [_menu itemChanged:self]; +} + +- (CPImage)onStateImage +{ + return _onStateImage; +} + +- (void)setOffStateImage:(CPImage)anImage +{ + if (_offStateImage == anImage) + return; + + _offStateImage = anImage; + [_menu itemChanged:self]; +} + +- (CPImage)offStateImage +{ + return _offStateImage; +} + +- (void)setMixedStateImage:(CPImage)anImage +{ + if (_mixedStateImage == anImage) + return; + + _mixedStateImage = anImage; + [_menu itemChanged:self]; +} + +- (CPImage)mixedStateImage +{ + return _mixedStateImage; +} + +// Managing Subemenus + +- (void)setSubmenu:(CPMenu)aMenu +{ + var supermenu = [_submenu supermenu]; + + if (supermenu == self) + return; + + if (supermenu) + return alert("bad"); + + [_submenu setSupermenu:_menu]; + + _submenu = aMenu; + + [_menuItemView setDirty]; + + [_menu itemChanged:self]; +} + +- (CPMenu)submenu +{ + return _submenu; +} + +- (BOOL)hasSubmenu +{ + return _submenu ? YES : NO; +} + +// Getting a Separator Item + ++ (CPMenuItem)separatorItem +{ + return [[_CPMenuItemSeparator alloc] init]; +} + +- (BOOL)isSeparatorItem +{ + return NO; +} + +// Managing the Owning Menu + +- (void)setMenu:(CPMenu)aMenu +{ + _menu = aMenu; +} + +- (CPMenu)menu +{ + return _menu; +} + +// + +- (void)setKeyEquivalent:(CPString)aString +{ + _keyEquivalent = aString; +} + +- (CPString)keyEquivalent +{ + return _keyEquivalent; +} + +- (void)setKeyEquivalentModifierMask:(unsigned)aMask +{ + _keyEquivalentModifierMask = aMask; +} + +- (unsigned)keyEquivalentModifierMask +{ + return _keyEquivalentModifierMask; +} + +// Managing Mnemonics + +- (void)setMnemonicLocation:(unsigned)aLocation +{ + _mnemonicLocation = aLocation; +} + +- (unsigned)mnemonicLocation +{ + return _mnemonicLocation; +} + +- (void)setTitleWithMnemonicLocation:(CPString)aTitle +{ + var location = [aTitle rangeOfString:@"&"].location; + + if (location == CPNotFound) + [self setTitle:aTitle]; + else + { + [self setTitle:[aTitle substringToIndex:location] + [aTitle substringFromIndex:location + 1]]; + [self setMnemonicLocation:location]; + } +} + +- (CPString)mnemonic +{ + return _mnemonicLocation == CPNotFound ? @"" : [_title characterAtIndex:_mnemonicLocation]; +} + +// Managing Alternates + +- (void)setAlternate:(BOOL)isAlternate +{ + _isAlternate = isAlternate; +} + +- (BOOL)isAlternate +{ + return _isAlternate; +} + +// Managing Indentation Levels + +- (void)setIndentationLevel:(unsigned)aLevel +{ + if (aLevel < 0) + alert("bad"); + + _indentationLevel = MIN(15, aLevel); +} + +- (unsigned)indentationLevel +{ + return _indentationLevel; +} + +// Managing Tool Tips +- (void)setToolTip:(CPString)aToolTip +{ + _toolTip = aToolTip; +} + +- (CPString)toolTip +{ + return _toolTip; +} + +// Representing an Object + +- (void)setRepresentedObject:(id)anObject +{ + _representedObject = anObject; +} + +- (id)representedObject +{ + return _representedObject; +} + +// Managing the View + +- (void)setView:(CPView)aView +{ + if (_view == aView) + return; + + _view = aView; + + [_menuItemView setDirty]; + + [_menu itemChanged:self]; +} + +- (CPView)view +{ + return _view; +} + +// Getting Highlighted Status + +- (BOOL)isHighlighted +{ + return [[self menu] highlightedItem] == self; +} + +// + +- (id)_menuItemView +{ + if (!_menuItemView) + _menuItemView = [[_CPMenuItemView alloc] initWithFrame:CGRectMakeZero() forMenuItem:self]; + + return _menuItemView; +} + +@end + +@implementation _CPMenuItemSeparator : CPMenuItem +{ +} + +- (id)init +{ + return [super initWithTitle:@"" action:nil keyEquivalent:@""]; +} + +- (BOOL)isSeparatorItem +{ + return YES; +} + +@end + +var CPMenuItemTitleKey = @"CPMenuItemTitleKey", + CPMenuItemTargetKey = @"CPMenuItemTargetKey", + CPMenuItemActionKey = @"CPMenuItemActionKey", + + CPMenuItemIsEnabledKey = @"CPMenuItemIsEnabledKey", + CPMenuItemIsHiddenKey = @"CPMenuItemIsHiddenKey", + + CPMenuItemImageKey = @"CPMenuItemImageKey", + CPMenuItemAlternateImageKey = @"CPMenuItemAlternateImageKey", + + CPMenuItemSubmenuKey = @"CPMenuItemSubmenuKey", + CPMenuItemMenuKey = @"CPMenuItemMenuKey", + + CPMenuItemRepresentedObjectKey = @"CPMenuItemRepresentedObjectKey"; + +@implementation CPMenuItem (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + { + _title = [aCoder decodeObjectForKey:CPMenuItemTitleKey]; + +// _font; + + _target = [aCoder decodeObjectForKey:CPMenuItemTargetKey]; + _action = [aCoder decodeObjectForKey:CPMenuItemActionKey]; + + _isEnabled = [aCoder decodeObjectForKey:CPMenuItemIsEnabledKey]; + _isHidden = [aCoder decodeObjectForKey:CPMenuItemIsHiddenKey]; + +// int _tag; +// int _state; + + _image = [aCoder decodeObjectForKey:CPMenuItemImageKey]; + _alternateImage = [aCoder decodeObjectForKey:CPMenuItemAlternateImageKey]; +// CPImage _onStateImage; +// CPImage _offStateImage; +// CPImage _mixedStateImage; + + _submenu = [aCoder decodeObjectForKey:CPMenuItemSubmenuKey]; + _menu = [aCoder decodeObjectForKey:CPMenuItemMenuKey]; + +// CPString _keyEquivalent; +// unsigned _keyEquivalentModifierMask; + +// int _mnemonicLocation; + +// BOOL _isAlternate; +// int _indentationLevel; + +// CPString _toolTip; + + _representedObject = [aCoder decodeObjectForKey:CPMenuItemRepresentedObjectKey]; +// id _representedObject; +// CPView _view; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_title forKey:CPMenuItemTitleKey]; + + [aCoder encodeObject:_target forKey:CPMenuItemTargetKey]; + [aCoder encodeObject:_action forKey:CPMenuItemActionKey]; + + [aCoder encodeObject:_isEnabled forKey:CPMenuItemIsEnabledKey]; + [aCoder encodeObject:_isHidden forKey:CPMenuItemIsHiddenKey]; + + [aCoder encodeObject:_image forKey:CPMenuItemImageKey]; + [aCoder encodeObject:_alternateImage forKey:CPMenuItemAlternateImageKey]; + + [aCoder encodeObject:_submenu forKey:CPMenuItemSubmenuKey]; + [aCoder encodeObject:_menu forKey:CPMenuItemMenuKey]; + + [aCoder encodeObject:_representedObject forKey:CPMenuItemRepresentedObjectKey]; +} + +@end + +var LEFT_MARGIN = 3.0, + RIGHT_MARGIN = 16.0, + STATE_COLUMN_WIDTH = 14.0, + INDENTATION_WIDTH = 17.0; + +var _CPMenuItemSelectionColor = nil, + + _CPMenuItemDefaultStateImages = [], + _CPMenuItemDefaultStateHighlightedImages = [], + + _CPMenuItemViewMenuBarArrowImage = nil, + _CPMenuItemViewMenuBarArrowActivatedImage = nil; + +@implementation _CPMenuItemView : CPView +{ + CPMenuItem _menuItem; + + CPFont _font; + + CGSize _minSize; + BOOL _isDirty; + BOOL _showsStateColumn; + BOOL _belongsToMenuBar; + + CPImageView _stateView; + _CPImageAndTitleView _imageAndTitleView; + CPImageView _submenuImageView; +} + ++ (void)initialize +{ + if (self != [_CPMenuItemView class]) + return; + + _CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:81.0 / 255.0 green:83.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]; + + var bundle = [CPBundle bundleForClass:self]; + + _CPMenuItemDefaultStateImages[CPOffState] = nil; + _CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil; + + _CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)]; + _CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)]; + + _CPMenuItemDefaultStateImages[CPMixedState] = nil; + _CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil; +} + ++ (float)leftMargin +{ + return LEFT_MARGIN + STATE_COLUMN_WIDTH; +} + +- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _menuItem = aMenuItem; + _showsStateColumn = YES; + _isDirty = YES; + + [self setAutoresizingMask:CPViewWidthSizable]; + + [self synchronizeWithMenuItem]; + } + + return self; +} + +- (CGSize)minSize +{ + return _minSize; +} + +- (void)setDirty +{ + _isDirty = YES; +} + +- (void)synchronizeWithMenuItem +{ + if (!_isDirty) + return; + + _isDirty = NO; + + var view = [_menuItem view]; + + if (view) + { + [_imageAndTitleView removeFromSuperview]; + _imageAndTitleView = nil; + + [_stateView removeFromSuperview]; + _stateView = nil; + + [_submenuImageView removeFromSuperview]; + _submenuImageView = nil; + + _minSize = [view frame].size; + + [self setFrameSize:_minSize]; + + [self addSubview:view]; + + return; + } + + // State Column + var x = _belongsToMenuBar ? 0.0 : (LEFT_MARGIN + [_menuItem indentationLevel] * INDENTATION_WIDTH); + + if (_showsStateColumn) + { + if (!_stateView) + { + _stateView = [[CPImageView alloc] initWithFrame:CGRectMake(x, (CGRectGetHeight([self frame]) - STATE_COLUMN_WIDTH) / 2.0, STATE_COLUMN_WIDTH, STATE_COLUMN_WIDTH)]; + + [_stateView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin]; + + [self addSubview:_stateView]; + } + + var state = [_menuItem state]; + + switch (state) + { + case CPOffState: + case CPOnState: + case CPMixedState: [_stateView setImage:_CPMenuItemDefaultStateImages[state]]; + break; + + default: [_stateView setImage:nil]; + } + + x += STATE_COLUMN_WIDTH; + } + else + { + [_stateView removeFromSuperview]; + + _stateView = nil; + } + + // Image and Title + + if (!_imageAndTitleView) + { + _imageAndTitleView = [[_CPImageAndTitleView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)]; + + [_imageAndTitleView setImagePosition:CPImageLeft]; + + [self addSubview:_imageAndTitleView]; + } + + var font = [_menuItem font]; + + if (!font) + font = _font; + + [_imageAndTitleView setFont:font]; + [_imageAndTitleView setImage:[_menuItem image]]; + [_imageAndTitleView setTitle:[_menuItem title]]; + [_imageAndTitleView setTextColor:[_menuItem isEnabled] ? [CPColor blackColor] : [CPColor darkGrayColor]]; + [_imageAndTitleView setFrameOrigin:CGPointMake(x, 0.0)]; + + [_imageAndTitleView sizeToFit]; + + var frame = [_imageAndTitleView frame]; + + x += CGRectGetWidth(frame); + + // Submenu Arrow + if ([_menuItem hasSubmenu]) + { + if (!_submenuImageView) + { + _submenuImageView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 10.0)]; + + [self addSubview:_submenuImageView]; + } + + if (_belongsToMenuBar && !_CPMenuItemViewMenuBarArrowImage) + _CPMenuItemViewMenuBarArrowImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[_CPMenuItemView class]] pathForResource:@"_CPMenuItemView/_CPMenuItemViewMenuBarArrow.png"] size:CGSizeMake(10.0, 10.0)]; + + [_submenuImageView setHidden:NO]; + [_submenuImageView setImage:_belongsToMenuBar ? _CPMenuItemViewMenuBarArrowImage : nil]; + [_submenuImageView setFrameOrigin:CGPointMake(x, (CGRectGetHeight(frame) - 10.0) / 2.0)]; + + x += 10.0; + } + else + [_submenuImageView setHidden:YES]; + + _minSize = CGSizeMake(x + (_belongsToMenuBar ? 0.0 : RIGHT_MARGIN), CGRectGetHeight(frame)); + + [self setFrameSize:_minSize]; +} + +- (float)calculatedLeftMargin +{ + if (_belongsToMenuBar) + return 0.0; + + return LEFT_MARGIN + ([[_menuItem menu] showsStateColumn] ? STATE_COLUMN_WIDTH : 0.0) + [_menuItem indentationLevel] * INDENTATION_WIDTH; +} + +- (void)setShowsStateColumn:(BOOL)shouldShowStateColumn +{ + _showsStateColumn = shouldShowStateColumn; +} + +- (void)setBelongsToMenuBar:(BOOL)shouldBelongToMenuBar +{ + _belongsToMenuBar = shouldBelongToMenuBar; +} + +- (void)highlight:(BOOL)shouldHighlight +{ + // ASSERT(![_menuItem view]); + + if (_belongsToMenuBar) + [_imageAndTitleView setImage:shouldHighlight ? [_menuItem alternateImage] : [_menuItem image]]; + + else + { + [_imageAndTitleView setTextColor:shouldHighlight ? [CPColor whiteColor] : [CPColor blackColor]]; + + if (shouldHighlight) + [self setBackgroundColor:_CPMenuItemSelectionColor]; + else + [self setBackgroundColor:nil]; + + var state = [_menuItem state]; + + switch (state) + { + case CPOffState: + case CPOnState: + case CPMixedState: [_stateView setImage:shouldHighlight ? _CPMenuItemDefaultStateHighlightedImages[state] : _CPMenuItemDefaultStateImages[state]]; + break; + + default: [_stateView setImage:nil]; + } + } +} + +- (void)activate:(BOOL)shouldActivate +{ + [_imageAndTitleView setImage:[_menuItem image]]; + + if (shouldActivate) + { + if (!_CPMenuItemViewMenuBarArrowActivatedImage) + _CPMenuItemViewMenuBarArrowActivatedImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[_CPMenuItemView class]] pathForResource:@"_CPMenuItemView/_CPMenuItemViewMenuBarArrowActivated.png"] size:CGSizeMake(10.0, 10.0)]; + + [_imageAndTitleView setTextColor:[CPColor whiteColor]]; + [_submenuImageView setImage:_CPMenuItemViewMenuBarArrowActivatedImage]; + } + else + { + [_imageAndTitleView setTextColor:[CPColor blackColor]]; + [_submenuImageView setImage:_CPMenuItemViewMenuBarArrowImage]; + } +} + +- (BOOL)eventOnSubmenu:(CPEvent)anEvent +{ + if (![_menuItem hasSubmenu]) + return NO; + + return CGRectContainsPoint([_submenuImageView frame], [self convertPoint:[anEvent locationInWindow] fromView:nil]); +} + +- (BOOL)isHidden +{ + return [_menuItem isHidden]; +} + +- (CPMenuItem)menuItem +{ + return _menuItem; +} + +- (void)setFont:(CPFont)aFont +{ + if (_font == aFont) + return; + + _font = aFont; + + [self setDirty]; +} + +@end + diff --git a/AppKit/CPOutlineView.j b/AppKit/CPOutlineView.j new file mode 100644 index 0000000000..a19b008a8f --- /dev/null +++ b/AppKit/CPOutlineView.j @@ -0,0 +1,79 @@ +/* + * CPOutlineView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPTableView.j" + + +@implementation CPOutlineView : CPTableView +{ + id _outlineDataSource; + CPArray _itemsByRow; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + [super setDataSource:self]; + _itemsByRow = [[CPArray alloc] init]; + } + + return self; +} + +- (void)setDataSource:(id)aDataSource +{ + _outlineDataSource = aDataSource; + + [self reloadData]; +} + +- (void)reloadData +{ + _numberOfVisibleItems = [_outlineDataSource outlineView:self numberOfChildrenOfItem:nil]; + _numberOfRows = _numberOfVisibleItems; + + var i = 0; + + for (; i < _numberOfVisibleItems; ++i) + _itemsByRow[i] = [_outlineDataSource outlineView:self child:i ofItem:nil]; + + [self loadTableCellsInRect:[self bounds]]; +} + +@end + +@implementation CPOutlineView (CPTableDataSource) + +- (void)numberOfRowsInTableView:(CPTableView)aTableView +{ + return _numberOfVisibleItems; +} + +- (void)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)aTableColumn row:(int)aRowIndex +{ + return [_outlineDataSource outlineView:self objectValueForTableColumn:aTableColumn byItem:_itemsByRow[aRowIndex]]; +} + +@end \ No newline at end of file diff --git a/AppKit/CPPanel.j b/AppKit/CPPanel.j new file mode 100644 index 0000000000..345acf5a4e --- /dev/null +++ b/AppKit/CPPanel.j @@ -0,0 +1,70 @@ +/* + * CPPanel.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPWindow.j" + + +CPOKButton = 1; +CPCancelButton = 0; + +@implementation CPPanel : CPWindow +{ + BOOL _becomesKeyOnlyIfNeeded; + BOOL _worksWhenModal; +} + +- (BOOL)isFloatingPanel +{ + return [self level] == CPFloatingWindowLevel; +} + +- (void)setFloatingPanel:(BOOL)isFloatingPanel +{ + [self setLevel:isFloatingPanel ? CPFloatingWindowLevel : CPNormalWindowLevel]; +} + +- (BOOL)becomesKeyOnlyIfNeeded +{ + return _becomesKeyOnlyIfNeeded; +} + +- (void)setBecomesKeyOnlyIfNeeded:(BOOL)shouldBecomeKeyOnlyIfNeeded +{ + _becomesKeyOnlyIfNeeded = shouldBecomeKeyOnlyIfNeeded +} + +- (BOOL)worksWhenModal +{ + return _worksWhenModal; +} + +- (void)setWorksWhenModal:(BOOL)shouldWorkWhenModal +{ + _worksWhenModal = shouldWorkWhenModal; +} + +- (BOOL)canBecomeMainWindow +{ + return NO; +} + +@end diff --git a/AppKit/CPPasteboard.j b/AppKit/CPPasteboard.j new file mode 100644 index 0000000000..80283397fe --- /dev/null +++ b/AppKit/CPPasteboard.j @@ -0,0 +1,226 @@ +/* + * CPPasteboard.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import +import + + +CPGeneralPboard = @"CPGeneralPboard"; +CPFontPboard = @"CPFontPboard"; +CPRulerPboard = @"CPRulerPboard"; +CPFindPboard = @"CPFindPboard"; +CPDragPboard = @"CPDragPboard"; + +CPColorPboardType = @"CPColorPboardType"; +CPFilenamesPboardType = @"CPFilenamesPboardType"; +CPFontPboardType = @"CPFontPboardType"; +CPHTMLPboardType = @"CPHTMLPboardType"; +CPStringPboardType = @"CPStringPboardType"; +CPURLPboardType = @"CPURLPboardType"; +CPImagePboardType = @"CPImagePboardType"; + +var CPPasteboards = nil; + +@implementation CPPasteboard : CPObject +{ + CPArray _types; + CPDictionary _owners; + CPDictionary _provided; + + unsigned _changeCount; + CPString _stateUID; +} + ++ (void)initialize +{ + if (self != [CPPasteboard class]) + return; + + [self setVersion:1.0]; + + CPPasteboards = [CPDictionary dictionary]; +} + ++ (id)generalPasteboard +{ + return [CPPasteboard pasteboardWithName:CPGeneralPboard]; +} + ++ (id)pasteboardWithName:(CPString)aName +{ + var pasteboard = [CPPasteboards objectForKey:aName]; + + if (pasteboard) + return pasteboard; + + pasteboard = [[CPPasteboard alloc] _initWithName:aName]; + [CPPasteboards setObject:pasteboard forKey:aName]; + + return pasteboard; +} + +- (id)_initWithName:(CPString)aName +{ + self = [super init]; + + if (self) + { + _name = aName; + _types = []; + + _owners = [CPDictionary dictionary]; + _provided = [CPDictionary dictionary]; + + _changeCount = 0; + } + + return self; +} + +- (unsigned)addTypes:(CPArray)types owner:(id)anOwner +{ + var i = 0, + count = types.length; + + for (; i < count; ++i) + { + var type = types[i]; + + if(![_owners objectForKey:type]) + { + [_types addObject:type]; + [_provided removeObjectForKey:type]; + } + + [_owners setObject:anOwner forKey:type]; + } + + return ++_changeCount; +} + +- (unsigned)declareTypes:(CPArray)types owner:(id)anOwner +{ + [_types setArray:types]; + + _owners = [CPDictionary dictionary]; + _provided = [CPDictionary dictionary]; + + var count = _types.length; + + while (count--) + [_owners setObject:anOwner forKey:_types[count]]; + + return ++_changeCount; +} + +- (BOOL)setData:(CPData)aData forType:(CPString)aType +{ + [_provided setObject:aData forKey:aType]; + + return YES; +} + +- (BOOL)setPropertyList:(id)aPropertyList forType:(CPString)aType +{ + return [self setData:[CPPropertyListSerialization dataFromPropertyList:aPropertyList format:CPPropertyListXMLFormat_v1_0 errorDescription:nil] forType:aType]; +} + +- (void)setString:(CPString)aString forType:(CPString)aType +{ + return [self setPropertyList:aString forType:aType]; +} + +// Determining Types + +- (CPString)availableTypeFromArray:(CPArray)anArray +{ + return [_types firstObjectCommonWithArray:anArray]; +} + +- (CPArray)types +{ + return _types; +} + +// Reading data + +- (unsigned)changeCount +{ + return _changeCount; +} + +- (CPData)dataForType:(CPString)aType +{ + var data = [_provided objectForKey:aType]; + + if (data) + return data; + + var owner = [_owners objectForKey:aType]; + + if (owner) + { + [owner pasteboard:self provideDataForType:aType]; + + ++_changeCount; + + return [_provided objectForKey:aType]; + } + + return nil; +} + +- (id)propertyListForType:(CPString)aType +{ + var data = [self dataForType:aType]; + + if (data) + return [CPPropertyListSerialization propertyListFromData:data format:CPPropertyListXMLFormat_v1_0 errorDescription:nil]; + + return nil; +} + +- (CPString)stringForType:(CPString)aType +{ + return [self propertyListForType:aType]; +} + +- (CPString)_generateStateUID +{ + var bits = 32; + + _stateUID = @""; + + while (bits--) + _stateUID += FLOOR(RAND() * 16.0).toString(16).toUpperCase(); + + return _stateUID; +} + +- (CPString)_stateUID +{ + return _stateUID; +} + +@end diff --git a/AppKit/CPPopUpButton.j b/AppKit/CPPopUpButton.j new file mode 100644 index 0000000000..2c1898990d --- /dev/null +++ b/AppKit/CPPopUpButton.j @@ -0,0 +1,577 @@ +/* + * CPPopUpButton.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPButton.j" +import "CPGeometry.j" +import "CPMenu.j" +import "CPMenuItem.j" + + +var VISIBLE_MARGIN = 7.0; + +var CPPopUpButtonArrowsImage = nil; + +@implementation CPPopUpButton : CPButton +{ + BOOL _pullsDown; + int _selectedIndex; + CPRectEdge _preferredEdge; + + CPImageView _arrowsView; + + CPMenu _menu; +} + +- (id)initWithFrame:(CGRect)aFrame pullsDown:(BOOL)shouldPullDown +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _pullsDown = shouldPullDown; + _selectedIndex = CPNotFound; + _preferredEdge = CPMaxYEdge; + + [self setBezelStyle:CPTexturedRoundedBezelStyle]; + + [self setImagePosition:CPImageLeft]; + [self setAlignment:CPLeftTextAlignment]; + + [self setMenu:[[CPMenu alloc] initWithTitle:@""]]; + } + + return self; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + return [self initWithFrame:aFrame pullsDown:NO]; +} + +- (void)setBordered:(BOOL)shouldBeBordered +{ + if (shouldBeBordered) + { + var bounds = [self bounds]; + + _arrowsView = [[CPImageView alloc] initWithFrame:CGRectMake(CGRectGetWidth(bounds) - 10.0, (CGRectGetHeight(bounds) - 8.0) / 2.0, 5.0, 8.0)]; + + if (!CPPopUpButtonArrowsImage) + CPPopUpButtonArrowsImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPPopUpButton class]] pathForResource:@"CPPopUpButton/CPPopUpButtonArrows.png"] size:CGSizeMake(5.0, 8.0)]; + + [_arrowsView setImage:CPPopUpButtonArrowsImage]; + [_arrowsView setAutoresizingMask:CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin]; + + + [self addSubview:_arrowsView]; + } + else + { + [_arrowsView removeFromSuperview]; + + _arrowsView = nil; + } + + [super setBordered:shouldBeBordered]; +} + +// Setting the Type of Menu + +- (void)setPullsDown:(BOOL)shouldPullDown +{ + if (_pullsDown == shouldPullDown) + return; + + _pullsDown = shouldPullDown; + + var items = [_menu itemArray]; + + if (items.length <= 0) + return; + + [items[0] setHidden:_pullsDown]; + + [self synchronizeTitleAndSelectedItem]; +} + +- (BOOL)pullsDown +{ + return _pullsDown; +} + +// Inserting and Deleting Items + +- (void)addItemWithTitle:(CPString)aTitle +{ + [_menu addItemWithTitle:aTitle action:NULL keyEquivalent:NULL]; +} + +- (void)addItemsWithTitles:(CPArray)titles +{ + var index = 0, + count = [titles count]; + + for (; index < count; ++index) + [self addItemWithTitle:titles[index]]; +} + +- (void)insertItemWithTitle:(CPString)aTitle atIndex:(int)anIndex +{ + var items = [self itemArray], + count = [items count]; + + while (count--) + if ([items[count] title] == aTitle) + [self removeItemAtIndex:count]; + + [_menu insertItemWithTitle:aTitle action:NULL keyEquivalent:NULL atIndex:anIndex]; +} + +- (void)removeAllItems +{ + var count = [_menu numberOfItems]; + + while (count--) + [_menu removeItemAtIndex:0]; +} + +- (void)removeItemWithTitle:(CPString)aTitle +{ + [self removeItemAtIndex:[self indexOfItemWithTitle:aTitle]]; + [self synchronizeTitleAndSelectedItem]; +} + +- (void)removeItemAtIndex:(int)anIndex +{ + [_menu removeItemAtIndex:anIndex]; + [self synchronizeTitleAndSelectedItem]; +} + +// Getting the User's Selection + +- (CPMenuItem)selectedItem +{ + if (_selectedIndex < 0) + return nil; + + return [_menu itemAtIndex:_selectedIndex]; +} + +- (CPString)titleOfSelectedItem +{ + return [[self selectedItem] title]; +} + +- (int)indexOfSelectedItem +{ + return _selectedIndex; +} + +// For us, CPNumber is toll-free bridged to Number, so just return the selected index. +- (id)objectValue +{ + return _selectedIndex; +} + +// Setting the Current Selection + +- (void)selectItem:(CPMenuItem)aMenuItem +{ + [self selectItemAtIndex:[self indexOfItem:aMenuItem]]; +} + +- (void)selectItemAtIndex:(int)anIndex +{ + if (_selectedIndex == anIndex) + return; + + if (_selectedIndex >= 0 && !_pullsDown) + [[self selectedItem] setState:CPOffState]; + + _selectedIndex = anIndex; + + if (_selectedIndex >= 0 && !_pullsDown) + [[self selectedItem] setState:CPOnState]; + + [self synchronizeTitleAndSelectedItem]; +} + +- (void)selectItemWithTag:(int)aTag +{ + [self selectItemAtIndex:[self indexOfItemWithTag:aTag]]; +} + +- (void)selectItemWithTitle:(CPString)aTitle +{ + [self selectItemAtIndex:[self indexOfItemWithTitle:aTitle]]; +} + +- (void)setObjectValue:(id)aValue +{ + [self selectItemAtIndex:[aValue intValue]]; +} + +// Getting Menu Items + +- (CPMenu)menu +{ + return _menu; +} + +- (void)setMenu:(CPMenu)aMenu +{ + if (_menu == aMenu) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + if (_menu) + { + [defaultCenter + removeObserver:self + selector:@selector(menuDidAddItem:) + name:CPMenuDidAddItemNotification + object:_menu]; + + [defaultCenter + removeObserver:self + selector:@selector(menuDidChangeItem:) + name:CPMenuDidChangeItemNotification + object:_menu]; + + [defaultCenter + removeObserver:self + selector:@selector(menuDidRemoveItem:) + name:CPMenuDidRemoveItemNotification + object:_menu]; + } + + _menu = aMenu; + + if (_menu) + { + [defaultCenter + addObserver:self + selector:@selector(menuDidAddItem:) + name:CPMenuDidAddItemNotification + object:_menu]; + + [defaultCenter + addObserver:self + selector:@selector(menuDidChangeItem:) + name:CPMenuDidChangeItemNotification + object:_menu]; + + [defaultCenter + addObserver:self + selector:@selector(menuDidRemoveItem:) + name:CPMenuDidRemoveItemNotification + object:_menu]; + } + + [self synchronizeTitleAndSelectedItem]; +} + +- (int)numberOfItems +{ + return [_menu numberOfItems]; +} + +- (CPArray)itemArray +{ + return [_menu itemArray]; +} + +- (CPMenuItem)itemAtIndex:(unsigned)anIndex +{ + return [_menu itemAtIndex:anIndex]; +} + +- (CPString)itemTitleAtIndex:(unsigned)anIndex +{ + return [[_menu itemAtIndex:anIndex] title]; +} + +- (CPArray)itemTitles +{ + var titles = [], + items = [self itemArray], + + index = 0, + count = [items count]; + + for (; index < count; ++index) + items.push([items[index] title]); +} + +- (CPMenuItem)itemWithTitle:(CPString)aTitle +{ + return [_menu itemAtIndex:[_menu indexOfItemWithTitle:aTitle]]; +} + +- (CPMenuItem)lastItem +{ + return [[_menu itemArray] lastObject]; +} + +// Getting the Indices of Menu Items + +- (int)indexOfItem:(CPMenuItem)aMenuItem +{ + return [_menu indexOfItem:aMenuItem]; +} + +- (int)indexOfItemWithTag:(int)aTag +{ + return [_menu indexOfItemWithTag:aMenuItem]; +} + +- (int)indexOfItemWithTitle:(CPString)aTitle +{ + return [_menu indexOfItemWithTitle:aTitle]; +} + +- (int)indexOfItemWithRepresentedObject:(id)anObject +{ + return [_menu indexOfItemWithRepresentedObejct:anObject]; +} + +- (int)indexOfItemWithTarget:(id)aTarget action:(SEL)anAction +{ + return [_menu indexOfItemWithTarget:aTarget action:anAction]; +} + +// Setting the Cell Edge to Pop out in Restricted Situations + +- (CPRectEdge)preferredEdge +{ + return _preferredEdge; +} + +- (void)setPreferredEdge:(CPRectEdge)aRectEdge +{ + _preferredEdge = aRectEdge; +} + +// Setting the Title + +- (void)setTitle:(CPString)aTitle +{ + if ([self title] == aTitle) + return; + + if (_pullsDown) + { + [_items[0] setTitle:aTitle]; + [self synchronizeTitleAndSelectedItem]; + } + else + { + var index = [self indexOfItemWithTitle:aTitle]; + + if (index < 0) + { + [self addItemWithTitle:aTitle]; + + index = [self numberOfItems] - 1; + } + + [self selectItemAtIndex:index]; + } +} + +// Setting the Image + +- (void)setImage:(CPImage)anImage +{ + // The Image is set by the currently selected item. +} + +// Setting the State + +- (void)synchronizeTitleAndSelectedItem +{ + var item = nil; + + if (_pullsDown) + { + var items = [_menu itemArray]; + + if (items.length > 0) + item = items[0]; + } + else + item = [self selectedItem]; + + [super setImage:[item image]]; + [super setTitle:[item title]]; +} + +// + +- (void)menuDidAddItem:(CPNotification)aNotification +{ + var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"]; + + if (_selectedIndex < 0) + [self selectItemAtIndex:0]; + + else if (index == _selectedIndex) + [self synchronizeTitleAndSelectedItem]; + + else if (index < _selectedIndex) + ++_selectedIndex; + + if (index == 0 && _pullsDown) + { + var items = [_menu itemArray]; + + [items[0] setHidden:YES]; + + if (items.length > 0) + [items[1] setHidden:NO]; + } +} + +- (void)menuDidChangeItem:(CPNotification)aNotification +{ + var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"]; + + if (_pullsDown && index != 0) + return; + + if (!_pullsDown && index != _selectedIndex) + return; + + [self synchronizeTitleAndSelectedItem]; +} + +- (void)menuDidRemoveItem:(CPNotification)aNotification +{ + var numberOfItems = [self numberOfItems]; + + if (numberOfItems <= _selectedIndex) + [self selectItemAtIndex:numberOfItems - 1]; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + if (![self isEnabled]) + return; + + [self highlight:YES]; + + var theWindow = [self window], + menuWindow = [_CPMenuWindow menuWindowWithMenu:[self menu] font:[self font]]; + + [menuWindow setDelegate:self]; + [menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle]; + + var menuOrigin = [theWindow convertBaseToBridge:[self convertPoint:CGPointMakeZero() toView:nil]]; + + // Pull Down Menus show up directly below their buttons. + if (_pullsDown) + menuOrigin.y += CGRectGetHeight([self frame]); + + // Pop Up Menus attempt to show up "on top" of the selected item. + else + { + var contentRect = [menuWindow rectForItemAtIndex:_selectedIndex]; + + menuOrigin.x -= CGRectGetMinX(contentRect) + [_CPMenuItemView leftMargin]; + menuOrigin.y -= CGRectGetMinY(contentRect); + } + + [menuWindow setFrameOrigin:menuOrigin]; + + var menuMaxX = CGRectGetMaxX([menuWindow frame]), + buttonMaxX = CGRectGetMaxX([self convertRect:[self bounds] toView:nil]); + + if (menuMaxX < buttonMaxX) + [menuWindow setMinWidth:CGRectGetWidth([menuWindow frame]) + buttonMaxX - menuMaxX - VISIBLE_MARGIN]; + + [menuWindow orderFront:self]; + [menuWindow beginTrackingWithEvent:anEvent sessionDelegate:self didEndSelector:@selector(menuWindowDidFinishTracking:highlightedItem:)]; +} + +- (void)menuWindowDidFinishTracking:(_CPMenuWindow)aMenuWindow highlightedItem:(CPMenuItem)aMenuItem +{ + [_CPMenuWindow poolMenuWindow:aMenuWindow]; + + [self highlight:NO]; + + var index = [_menu indexOfItem:aMenuItem]; + + if (index == CPNotFound) + return; + + [self selectItemAtIndex:index]; + + var selectedItem = [self selectedItem], + target = nil, + action = [selectedItem action]; + + if (!action) + { + target = [self target]; + action = [self action]; + } + + // FIXME: If [selectedItem target] == nil do we use our own target? + else + target = [selectedItem target]; + + [self sendAction:action to:target]; +} + +@end + +var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey", + CPPopUpButtonSelectedIndexKey = @"CPPopUpButtonSelectedIndexKey", + CPPopUpButtonPullsDownKey = @"CPPopUpButtonPullsDownKey"; + +@implementation CPPopUpButton (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + [self setMenu:[aCoder decodeObjectForKey:CPPopUpButtonMenuKey]]; + [self selectItemAtIndex:[aCoder decodeObjectForKey:CPPopUpButtonSelectedIndexKey]]; + [self setPullsDown:[aCoder decodeBoolForKey:CPPopUpButtonPullsDownKey]]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [super encodeWithCoder:aCoder]; + + [aCoder encodeObject:_menu forKey:CPPopUpButtonMenuKey]; + [aCoder encodeInt:_selectedIndex forKey:CPPopUpButtonSelectedIndexKey]; + [aCoder encodeBool:_pullsDown forKey:CPPopUpButtonPullsDownKey]; +} + +@end diff --git a/AppKit/CPProgressIndicator.j b/AppKit/CPProgressIndicator.j new file mode 100644 index 0000000000..e5717ed406 --- /dev/null +++ b/AppKit/CPProgressIndicator.j @@ -0,0 +1,375 @@ +/* + * CPProgressIndicator.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import + +#include "CoreGraphics/CGGeometry.h" + + +CPProgressIndicatorBarStyle = 0; +CPProgressIndicatorSpinningStyle = 1; +CPProgressIndicatorHUDBarStyle = 2; + +var CPProgressIndicatorSpinningStyleColors = nil, + + CPProgressIndicatorClassName = nil, + CPProgressIndicatorStyleIdentifiers = nil, + CPProgressIndicatorStyleSizes = nil; + +@implementation CPProgressIndicator : CPView +{ + double _minValue; + double _maxValue; + + double _doubleValue; + + CPControlSize _controlSize; + + BOOL _isIndeterminate; + CPProgressIndicatorStyle _style; + + BOOL _isAnimating; + + BOOL _isDisplayedWhenStoppedSet; + BOOL _isDisplayedWhenStopped; + + CPView _barView; +} + ++ (void)initialize +{ + if (self != [CPProgressIndicator class]) + return; + + var bundle = [CPBundle bundleForClass:self]; + + CPProgressIndicatorSpinningStyleColors = []; + + CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile: + [bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]]; + CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile: + [bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]]; + CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile: + [bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]]; + + CPProgressIndicatorBezelBorderViewPool = []; + + var start = CPProgressIndicatorBarStyle, + end = CPProgressIndicatorHUDBarStyle; + + for (; start <= end; ++start) + { + CPProgressIndicatorBezelBorderViewPool[start] = []; + + CPProgressIndicatorBezelBorderViewPool[start][CPMiniControlSize] = []; + CPProgressIndicatorBezelBorderViewPool[start][CPSmallControlSize] = []; + CPProgressIndicatorBezelBorderViewPool[start][CPRegularControlSize] = []; + } + + CPProgressIndicatorClassName = [self className]; + + + CPProgressIndicatorStyleIdentifiers = []; + + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] = @"Bar"; + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorSpinningStyle] = @"Spinny"; + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle] = @"HUDBar"; + + var regularIdentifier = _CPControlIdentifierForControlSize(CPRegularControlSize), + smallIdentifier = _CPControlIdentifierForControlSize(CPSmallControlSize), + miniIdentifier = _CPControlIdentifierForControlSize(CPMiniControlSize); + + CPProgressIndicatorStyleSizes = []; + + // Bar Sttyle + var prefix = CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle]; + + CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)]; + CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)]; + CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)]; + + prefix = CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle]; + + CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = _CGSizeMake(1.0, 9.0); + CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = _CGSizeMake(1.0, 9.0); + CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = _CGSizeMake(1.0, 9.0); + + // HUD Bar Style + prefix = CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle]; + + CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)]; + CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)]; + CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)]; + + prefix = CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle]; + + CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = _CGSizeMake(1.0, 9.0); + CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = _CGSizeMake(1.0, 9.0); + CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = _CGSizeMake(1.0, 9.0); +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _minValue = 0.0; + _maxValue = 100.0; + + _doubleValue = 0.0; + + _style = CPProgressIndicatorBarStyle; + _isDisplayedWhenStoppedSet = NO; + + _controlSize = CPRegularControlSize; + + [self updateBackgroundColor]; + [self drawBar]; + } + + return self; +} + +- (void)setUsesThreadedAnimation:(BOOL)aFlag +{ +} + +- (void)startAnimation:(id)aSender +{ + _isAnimating = YES; + + [self _hideOrDisplay]; +} + +- (void)stopAnimation:(id)aSender +{ + _isAnimating = NO; + + [self _hideOrDisplay]; +} + +- (BOOL)usesThreadedAnimation +{ + return NO; +} + +// Advancing the Progress Bar + +- (void)incrementBy:(double)aValue +{ + [self setDoubleValue:_doubleValue + aValue]; +} + +- (void)setDoubleValue:(double)aValue +{ + _doubleValue = MIN(MAX(aValue, _minValue), _maxValue); + + [self drawBar]; +} + +- (double)doubleValue +{ + return _doubleValue; +} + +- (void)setMinValue:(double)aValue +{ + _minValue = aValue; +} + +- (double)minValue +{ + return _minValue; +} + +- (void)setMaxValue:(double)aValue +{ + _maxValue = aValue; +} + +- (double)maxValue +{ + return _maxValue; +} + +// Setting the Appearance + +- (void)setControlSize:(CPControlSize)aControlSize +{ + if (_controlSize == aControlSize) + return; + + _controlSize = aControlSize; + + [self updateBackgroundColor]; +} + +- (CPControlSize)controlSize +{ + return _controlSize; +} + +- (void)setControlTint:(CPControlTint)aControlTint +{ +} + +- (CPControlTint)controlTint +{ + return 0; +} + +- (void)setBezeled:(BOOL)isBezeled +{ +} + +- (BOOL)isBezeled +{ + return YES; +} + +- (void)setIndeterminate:(BOOL)isIndeterminate +{ + if (_indeterminate == isIndeterminate) + return; + + _isIndeterminate = isIndeterminate; + + [self updateBackgroundColor]; +} + +- (BOOL)isIndeterminate +{ + return _isIndeterminate; +} + +- (void)setStyle:(CPProgressIndicatorStyle)aStyle +{ + if (_style == aStyle) + return; + + _style = aStyle; + + [self updateBackgroundColor]; +} + +- (void)sizeToFit +{ + if (_style == CPProgressIndicatorSpinningStyle) + [self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]]; + else + [self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), CPProgressIndicatorStyleSizes[ + CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] + + _CPControlIdentifierForControlSize(_controlSize)][0].height)]; +} + +- (void)setDisplayedWhenStopped:(BOOL)isDisplayedWhenStopped +{ + if (_isDisplayedWhenStoppedSet && _isDisplayedWhenStopped == isDisplayedWhenStopped) + return; + + _isDisplayedWhenStoppedSet = YES; + + _isDisplayedWhenStopped = isDisplayedWhenStopped; + + [self _hideOrDisplay]; +} + +- (BOOL)isDisplayedWhenStopped +{ + if (_isDisplayedWhenStoppedSet) + return _isDisplayedWhenStopped; + + if (_style == CPProgressIndicatorBarStyle || _style == CPProgressIndicatorHUDBarStyle) + return YES; + + return NO; +} + +- (void)_hideOrDisplay +{ + [self setHidden:!_isAnimating && ![self isDisplayedWhenStopped]]; +} + +- (void)setFrameSize:(CGSize)aSize +{ + [super setFrameSize:aSize]; + + [self drawBar]; +} + +- (void)drawBar +{ + if (_style == CPProgressIndicatorSpinningStyle) + return; + + if (!_barView) + { + _barView = [[CPView alloc] initWithFrame:CGRectMake(2.0, 2.0, 0.0, 9.0)]; + + [_barView setBackgroundColor:[CPColor redColor]]; + + [self addSubview:_barView]; + } + + [_barView setBackgroundColor:_CPControlColorWithPatternImage( + CPProgressIndicatorStyleSizes, + CPProgressIndicatorClassName, + @"Bar", + CPProgressIndicatorStyleIdentifiers[_style], + _CPControlIdentifierForControlSize(_controlSize))]; + + [_barView setFrameSize:CGSizeMake(CGRectGetWidth([self bounds]) * (_doubleValue - _minValue) / (_maxValue - _minValue) - 4.0, 9.0)]; +} + +- (void)updateBackgroundColor +{ + if (YES)//_isBezeled) + { + if (_style == CPProgressIndicatorSpinningStyle) + { + [_barView removeFromSuperview]; + + _barView = nil; + + [self setBackgroundColor:CPProgressIndicatorSpinningStyleColors[_controlSize]]; + } + else + { + [self setBackgroundColor:_CPControlThreePartImagePattern( + NO, + CPProgressIndicatorStyleSizes, + CPProgressIndicatorClassName, + @"BezelBorder", + CPProgressIndicatorStyleIdentifiers[_style], + _CPControlIdentifierForControlSize(_controlSize))]; + + [self drawBar]; + } + } + else + [self setBackgroundColor:nil]; +} + +@end diff --git a/AppKit/CPResponder.j b/AppKit/CPResponder.j new file mode 100644 index 0000000000..e51ed792ff --- /dev/null +++ b/AppKit/CPResponder.j @@ -0,0 +1,225 @@ +/* + * CPResponder.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +CPDeleteKeyCode = 8; +CPUpArrowKeyCode = 63232; +CPDownArrowKeyCode = 63233; +CPLeftArrowKeyCode = 63234; +CPRightArrowKeyCode = 63235; + +@implementation CPResponder : CPObject +{ + CPMenu _menu; + CPResponder _nextResponder; +} + +// Changing the first responder + +- (BOOL)acceptsFirstResponder +{ + return NO; +} + +- (BOOL)becomeFirstResponder +{ + return YES; +} + +- (BOOL)resignFirstResponder +{ + return YES; +} + +// Setting the next responder + +- (void)setNextResponder:(CPResponder)aResponder +{ + _nextResponder = aResponder; +} + +- (CPResponder)nextResponder +{ + return _nextResponder; +} + +- (void)interpretKeyEvents:(CPArray)events +{ + var event, + index = 0; + + while(event = events[index++]) + { + switch([event keyCode]) + { + case CPLeftArrowKeyCode: [self moveBackward:self]; + break; + case CPRightArrowKeyCode: [self moveForward:self]; + break; + case CPUpArrowKeyCode: [self moveUp:self]; + break; + case CPDownArrowKeyCode: [self moveDown:self]; + break; + case CPDeleteKeyCode: [self deleteBackward:self]; + break; + case 3: + case 13: [self insertLineBreak:self]; + break; + default: [self insertText:[event characters]]; + } + } +} + +- (void)mouseDown:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + +- (void)mouseDragged:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + +- (void)mouseUp:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + +- (void)mouseMoved:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + +- (void)mouseEntered:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + +- (void)mouseExited:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + +- (void)scrollWheel:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + +- (void)keyDown:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + +- (void)keyUp:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + +- (BOOL)performKeyEquivalent:(CPEvent)anEvent +{ + return NO; +} + +// Action Methods + +- (void)deleteBackward:(id)aSender +{ +} + +- (void)insertLineBreak:(id)aSender +{ +} + +- (void)insertText:(CPString)aString +{ +} + +// Dispatch methods + +- (void)doCommandBySelector:(SEL)aSelector +{ + if([self respondsToSelector:aSelector]) + [self performSelector:aSelector]; + else + [_nextResponder doCommandBySelector:aSelector]; +} + +- (BOOL)tryToPerform:(SEL)aSelector with:(id)anObject +{ + if([self respondsToSelector:aSelector]) + { + [self performSelector:aSelector withObject:anObject]; + + return YES; + } + + return [_nextResponder tryToPerform:aSelector with:anObject]; +} + +// Managing a Responder's menu + +- (void)setMenu:(CPMenu)aMenu +{ + _menu = aMenu; +} + +- (CPMenu)menu +{ + return _menu; +} + +// Getting the Undo Manager + +- (CPUndoManager)undoManager +{ + return [_nextResponder performSelector:_cmd]; +} + +// Terminating the responder chain + +- (void)noResponderFor:(SEL)anEventSelector +{ +} + +@end + +var CPResponderNextResponderKey = @"CPResponderNextResponderKey"; + +@implementation CPResponder (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [self init]; + + if (self) + _nextResponder = [aCoder decodeObjectForKey:CPResponderNextResponderKey]; + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeConditionalObject:_nextResponder forKey:CPResponderNextResponderKey]; +} + +@end diff --git a/AppKit/CPScrollView.j b/AppKit/CPScrollView.j new file mode 100644 index 0000000000..30cacbe55f --- /dev/null +++ b/AppKit/CPScrollView.j @@ -0,0 +1,467 @@ +/* + * CPScrollView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPView.j" +import "CPClipView.j" +import "CPScroller.j" + +#include "CoreGraphics/CGGeometry.h" + + +@implementation CPScrollView : CPView +{ + CPClipView _contentView; + + BOOL _hasVerticalScroller; + BOOL _hasHorizontalScroller; + BOOL _autohidesScrollers; + + CPScroller _verticalScroller; + CPScroller _horizontalScroller; + + int _recursionCount; + + float _verticalLineScroll; + float _verticalPageScroll; + float _horizontalLineScroll; + float _horizontalPageScroll; +} + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _verticalLineScroll = 10.0; + _verticalPageScroll = 10.0; + + _horizontalLineScroll = 10.0; + _horizontalPageScroll = 10.0; + + _contentView = [[CPClipView alloc] initWithFrame:[self bounds]]; + + [self addSubview:_contentView]; + + [self setHasVerticalScroller:YES]; + [self setHasHorizontalScroller:YES]; + } + + return self; +} + +// Determining component sizes +- (CPRect)contentSize +{ + return [_contentView frame].size; +} + +- (id)documentView +{ + return [_contentView documentView]; +} + +- (void)setContentView:(CPClipView)aContentView +{ + if (!aContentView) + return; + + var documentView = [aContentView documentView]; + + if (documentView) + [documentView removeFromSuperview]; + + [_contentView removeFromSuperview]; + + var size = [self contentSize]; + + _contentView = aContentView; + + [_contentView setFrame:CGRectMake(0.0, 0.0, size.width, size.height)]; + [_contentView setDocumentView:documentView]; + + [self addSubview:_contentView]; +} + +- (CPClipView)contentView +{ + return _contentView; +} + +- (void)setDocumentView:(CPView)aView +{ + [_contentView setDocumentView:aView]; + [self reflectScrolledClipView:_contentView]; +} + +- (void)reflectScrolledClipView:(CPClipView)aClipView +{ + if(_contentView != aClipView) + return; + + if (_recursionCount > 5) + return; + + ++_recursionCount; + + var documentView = [self documentView]; + + if (!documentView) + { + if (_autohidesScrollers) + { + [_verticalScroller setHidden:YES]; + [_horizontalScroller setHidden:YES]; + } + else + { + [_verticalScroller setEnabled:NO]; + [_horizontalScroller setEnabled:NO]; + } + + [_contentView setFrame:[self bounds]]; + + --_recursionCount; + + return; + } + + var documentFrame = [documentView frame], + contentViewFrame = [self bounds], + scrollPoint = [_contentView bounds].origin, + difference = _CGSizeMake(CPRectGetWidth(documentFrame) - CPRectGetWidth(contentViewFrame), CPRectGetHeight(documentFrame) - CPRectGetHeight(contentViewFrame)), + shouldShowVerticalScroller = (!_autohidesScrollers || difference.height > 0.0) && _hasVerticalScroller, + shouldShowHorizontalScroller = (!_autohidesScrollers || difference.width > 0.0) && _hasHorizontalScroller, + wasShowingVerticalScroller = ![_verticalScroller isHidden], + wasShowingHorizontalScroller = ![_horizontalScroller isHidden], + verticalScrollerWidth = [CPScroller scrollerWidthForControlSize:[_verticalScroller controlSize]], + horizontalScrollerHeight = [CPScroller scrollerWidthForControlSize:[_horizontalScroller controlSize]]; + + if (_autohidesScrollers) + { + if (shouldShowVerticalScroller) + shouldShowHorizontalScroller = (!_autohidesScrollers || difference.width > -verticalScrollerWidth) && _hasHorizontalScroller; + if (shouldShowHorizontalScroller) + shouldShowVerticalScroller = (!_autohidesScrollers || difference.height > -horizontalScrollerHeight) && _hasVerticalScroller; + } + + [_verticalScroller setHidden:!shouldShowVerticalScroller]; + [_verticalScroller setEnabled:!_autohidesScrollers && difference.height < 0]; + + [_horizontalScroller setHidden:!shouldShowHorizontalScroller]; + [_horizontalScroller setEnabled:!_autohidesScrollers && difference.width < 0]; + + if (shouldShowVerticalScroller) + { + var verticalScrollerHeight = CPRectGetHeight(contentViewFrame); + + if (shouldShowHorizontalScroller) + verticalScrollerHeight -= horizontalScrollerHeight; + + difference.width += verticalScrollerWidth; + contentViewFrame.size.width -= verticalScrollerWidth; + + [_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height + knobProportion:CPRectGetHeight(contentViewFrame) / CPRectGetHeight(documentFrame)]; + [_verticalScroller setFrame:CPRectMake(CPRectGetMaxX(contentViewFrame), 0.0, verticalScrollerWidth, verticalScrollerHeight)]; + } + else if (wasShowingVerticalScroller) + [_verticalScroller setFloatValue:0.0 knobProportion:1.0]; + + if (shouldShowHorizontalScroller) + { + difference.height += horizontalScrollerHeight; + contentViewFrame.size.height -= horizontalScrollerHeight; + + [_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width + knobProportion:CPRectGetWidth(contentViewFrame) / CPRectGetWidth(documentFrame)]; + [_horizontalScroller setFrame:CPRectMake(0.0, CPRectGetMaxY(contentViewFrame), CPRectGetWidth(contentViewFrame), horizontalScrollerHeight)]; + } + else if (wasShowingHorizontalScroller) + [_horizontalScroller setFloatValue:0.0 knobProportion:1.0]; + + [_contentView setFrame:contentViewFrame]; + + // The reason we have to do this is because this is called on a frame size change, so when the frame changes, + // so does the the float value, so we have to update the clip view accordingly. + if (_hasVerticalScroller && (shouldShowVerticalScroller || wasShowingVerticalScroller)) + { + //[self _verticalScrollerDidScroll:_verticalScroller]; + var value = [_verticalScroller floatValue], + contentBounds = [_contentView bounds]; + + contentBounds.origin.y = value * (_CGRectGetHeight([[_contentView documentView] frame]) - _CGRectGetHeight(contentBounds)); + + [_contentView scrollToPoint:contentBounds.origin]; + } + if (_hasHorizontalScroller && (shouldShowHorizontalScroller || wasShowingHorizontalScroller)) + { + //[self _horizontalScrollerDidScroll:_horizontalScroller]; + + var value = [_horizontalScroller floatValue], + contentBounds = [_contentView bounds]; + + contentBounds.origin.x = value * (_CGRectGetWidth([[_contentView documentView] frame]) - _CGRectGetWidth(contentBounds)); + + [_contentView scrollToPoint:contentBounds.origin]; + } + + --_recursionCount; +} + +// Managing Scrollers + +- (void)setHorizontalScroller:(CPScroller)aScroller +{ + if (_horizontalScroller == aScroller) + return; + + [_horizontalScroller removeFromSuperview]; + [_horizontalScroller setTarget:nil]; + [_horizontalScroller setAction:nil]; + + _horizontalScroller = aScroller; + + [_horizontalScroller setTarget:self]; + [_horizontalScroller setAction:@selector(_horizontalScrollerDidScroll:)]; + [self addSubview:_horizontalScroller]; +} + +- (CPScroller)horizontalScroller +{ + return _horizontalScroller; +} + +- (void)setHasHorizontalScroller:(BOOL)hasHorizontalScroller +{ + _hasHorizontalScroller = hasHorizontalScroller; + + if (_hasHorizontalScroller && !_horizontalScroller) + [self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CPRectMake(0.0, 0.0, CPRectGetWidth([self bounds]), [CPScroller scrollerWidth])]]; + else if (!hasHorizontalScroller && _horizontalScroller) + [_horizontalScroller setHidden:YES]; +} + +- (BOOL)hasHorizontalScroller +{ + return _hasHorizontalScroller; +} + +- (void)setVerticalScroller:(CPScroller)aScroller +{ + if (_verticalScroller == aScroller) + return; + + [_verticalScroller removeFromSuperview]; + [_verticalScroller setTarget:nil]; + [_verticalScroller setAction:nil]; + + _verticalScroller = aScroller; + + [_verticalScroller setTarget:self]; + [_verticalScroller setAction:@selector(_verticalScrollerDidScroll:)]; + [self addSubview:_verticalScroller]; +} + +- (CPScroller)verticalScroller +{ + return _verticalScroller; +} + +- (void)setHasVerticalScroller:(BOOL)hasVerticalScroller +{ + _hasVerticalScroller = hasVerticalScroller; + + if (_hasVerticalScroller && !_verticalScroller) + [self setVerticalScroller:[[CPScroller alloc] initWithFrame:CPRectMake(0.0, 0.0, [CPScroller scrollerWidth], CPRectGetHeight([self bounds]))]]; + else if (!hasVerticalScroller && _verticalScroller) + [_verticalScroller setHidden:YES]; +} + +- (BOOL)hasHorizontalScroller +{ + return _hasHorizontalScroller; +} + +- (void)setAutohidesScrollers:(BOOL)autohidesScrollers +{ + _autohidesScrollers = autohidesScrollers; +} + +- (BOOL)autohidesScrollers +{ + return _autohidesScrollers; +} +/* +- (void)setFrameSize:(CPRect)aSize +{ + [super setFrameSize:aSize]; + + [self reflectScrolledClipView:_contentView]; +}*/ + +- (void)_verticalScrollerDidScroll:(CPScroller)aScroller +{ + var value = [aScroller floatValue], + documentFrame = [[_contentView documentView] frame]; + contentBounds = [_contentView bounds]; + + switch ([_verticalScroller hitPart]) + { + case CPScrollerDecrementLine: contentBounds.origin.y -= _verticalLineScroll; + break; + + case CPScrollerIncrementLine: contentBounds.origin.y += _verticalLineScroll; + break; + + case CPScrollerDecrementPage: contentBounds.origin.y -= _CGRectGetHeight(contentBounds) - _verticalPageScroll; + break; + + case CPScrollerIncrementPage: contentBounds.origin.y += _CGRectGetHeight(contentBounds) - _verticalPageScroll; + break; + + case CPScrollerKnobSlot: + case CPScrollerKnob: + default: contentBounds.origin.y = value * (_CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentBounds)); + } + + [_contentView scrollToPoint:contentBounds.origin]; +} + +- (void)_horizontalScrollerDidScroll:(CPScroller)aScroller +{ + var value = [aScroller floatValue], + documentFrame = [[self documentView] frame], + contentBounds = [_contentView bounds]; + + switch ([_horizontalScroller hitPart]) + { + case CPScrollerDecrementLine: contentBounds.origin.x -= _horizontalLineScroll; + break; + + case CPScrollerIncrementLine: contentBounds.origin.x += _horizontalLineScroll; + break; + + case CPScrollerDecrementPage: contentBounds.origin.x -= _CGRectGetWidth(contentBounds) - _horizontalPageScroll; + break; + + case CPScrollerIncrementPage: contentBounds.origin.x += _CGRectGetWidth(contentBounds) - _horizontalPageScroll; + break; + + case CPScrollerKnobSlot: + case CPScrollerKnob: + default: contentBounds.origin.x = value * (_CGRectGetWidth(documentFrame) - _CGRectGetWidth(contentBounds)); + } + + [_contentView scrollToPoint:contentBounds.origin]; +} + +- (void)tile +{ + // yuck. + // RESIZE: tile->setHidden AND refl + // Outside Change: refl->tile->setHidden AND refl + // scroll: refl. +} + +-(void)resizeSubviewsWithOldSize:(CPSize)aSize +{ + [self reflectScrolledClipView:_contentView]; +} + +// Setting Scrolling Behavior + +- (void)setLineScroll:(float)aLineScroll +{ + [self setHorizonalLineScroll:aLineScroll]; + [self setVerticalLineScroll:aLineScroll]; +} + +- (float)lineScroll +{ + return [self horizontalLineScroll]; +} + +- (void)setHorizontalLineScroll:(float)aLineScroll +{ + _horizontalLineScroll = aLineScroll; +} + +- (float)horizontalLineScroll +{ + return _horizontalLineScroll; +} + +- (void)setVerticalLineScroll:(float)aLineScroll +{ + _verticalLineScroll = aLineScroll; +} + +- (float)verticalLineScroll +{ + return _verticalLineScroll; +} + +- (void)setPageScroll:(float)aPageScroll +{ + [self setHorizontalPageScroll:aPageScroll]; + [self setVerticalPageScroll:aPageScroll]; +} + +- (float)pageScroll +{ + return [self horizontalPageScroll]; +} + +- (void)setHorizontalPageScroll:(float)aPageScroll +{ + _horizontalPageScroll = aPageScroll; +} + +- (float)horizontalPageScroll +{ + return _horizontalPageScroll; +} + +- (void)setVerticalPageScroll:(float)aPageScroll +{ + _verticalPageScroll = aPageScroll; +} + +- (float)verticalPageScroll +{ + return _verticalPageScroll; +} + +- (void)scrollWheel:(CPEvent)anEvent +{ + var value = [_verticalScroller floatValue], + documentFrame = [[self documentView] frame], + contentBounds = [_contentView bounds]; + + contentBounds.origin.x += [anEvent deltaX] * _horizontalLineScroll; + contentBounds.origin.y += [anEvent deltaY] * _verticalLineScroll; + + [_contentView scrollToPoint:contentBounds.origin]; +} + +@end diff --git a/AppKit/CPScroller.j b/AppKit/CPScroller.j new file mode 100644 index 0000000000..795e567936 --- /dev/null +++ b/AppKit/CPScroller.j @@ -0,0 +1,621 @@ +/* + * CPScroller.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPControl.j" + +#include "CoreGraphics/CGGeometry.h" + + +// CPScroller Constants +CPScrollerNoPart = 0; +CPScrollerDecrementPage = 1; +CPScrollerKnob = 2; +CPScrollerIncrementPage = 3; +CPScrollerDecrementLine = 4; +CPScrollerIncrementLine = 5; +CPScrollerKnobSlot = 6; + +CPScrollerIncrementArrow = 0; +CPScrollerDecrementArrow = 1; + +CPNoScrollerParts = 0; +CPOnlyScrollerArrows = 1; +CPAllScrollerParts = 2; + +var _CPScrollerWidths = [], + _CPScrollerKnobMinimumHeights = [], + _CPScrollerArrowHeights = [], + _CPScrollerArrowUsableHeights = []; + +_CPScrollerWidths[CPRegularControlSize] = 14.0; +_CPScrollerWidths[CPSmallControlSize] = 11.0; +_CPScrollerWidths[CPMiniControlSize] = 11.0; // FIXME + +_CPScrollerKnobMinimumHeights[CPRegularControlSize] = 18.0; +_CPScrollerKnobMinimumHeights[CPSmallControlSize] = 12.0; +_CPScrollerKnobMinimumHeights[CPMiniControlSize] = 12.0; // FIXME + +_CPScrollerArrowHeights[CPRegularControlSize] = 21.0; +_CPScrollerArrowHeights[CPSmallControlSize] = 16.0; +_CPScrollerArrowHeights[CPMiniControlSize] = 16.0; // FIXME + +_CPScrollerArrowUsableHeights[CPRegularControlSize] = 16.0 +_CPScrollerArrowUsableHeights[CPSmallControlSize] = 10.0; +_CPScrollerArrowUsableHeights[CPMiniControlSize] = 10.0; // FIXME + +var _CPScrollerKnobIdentifier = @"Knob", + _CPScrollerKnobSlotIdentifier = @"KnobSlot", + _CPScrollerDecrementArrowIdentifier = @"DecrementArrow", + _CPScrollerIncrementArrowIdentifier = @"IncrementArrow", + _CPScrollerHorizontalIdentifier = @"Horizontal", + _CPScrollerVerticalIdentifier = @"Vertical", + _CPScrollerHighlightedIdentifier = @"Highlighted", + _CPScrollerDisabledIdentifier = @"Disabled"; + +var _CPScrollerClassName = nil, + _CPScrollerPartSizes = {}; + +@implementation CPScroller : CPControl +{ + CPControlSize _controlSize; + CPUsableScrollerParts _usableParts; + CPArray _partRects; + + BOOL _isHorizontal; + float _knobProportion; + + CPScrollerPart _hitPart; + + CPScrollerPart _trackingPart; + float _trackingFloatValue; + CGPoint _trackingStartPoint; + + CPView _knobView; + CPView _knobSlotView; + + CPView _decrementArrowView; + CPView _incrementArrowView; +} + ++ (void)initialize +{ + if (self != [CPScroller class]) + return; + + _CPScrollerClassName = [self className]; + + var regularIdentifier = _CPControlIdentifierForControlSize(CPRegularControlSize), + smallIdentifier = _CPControlIdentifierForControlSize(CPSmallControlSize), + miniIdentifier = _CPControlIdentifierForControlSize(CPMiniControlSize); + + // Horizontal Knob Sizes + var prefix = _CPScrollerClassName + _CPScrollerKnobIdentifier + _CPScrollerHorizontalIdentifier; + + _CPScrollerPartSizes[prefix + regularIdentifier] = [_CGSizeMake(9.0, _CPScrollerWidths[CPRegularControlSize]), _CGSizeMake(1.0, _CPScrollerWidths[CPRegularControlSize]), _CGSizeMake(9.0, _CPScrollerWidths[CPRegularControlSize])]; + _CPScrollerPartSizes[prefix + smallIdentifier] = [_CGSizeMake(6.0, _CPScrollerWidths[CPSmallControlSize]), _CGSizeMake(1.0, _CPScrollerWidths[CPSmallControlSize]), _CGSizeMake(6.0, _CPScrollerWidths[CPSmallControlSize])]; + _CPScrollerPartSizes[prefix + miniIdentifier] = [_CGSizeMake(6.0, _CPScrollerWidths[CPMiniControlSize]), _CGSizeMake(1.0, _CPScrollerWidths[CPMiniControlSize]), _CGSizeMake(6.0, _CPScrollerWidths[CPMiniControlSize])]; + + // Vertical Knob Sizes + var prefix = _CPScrollerClassName + _CPScrollerKnobIdentifier + _CPScrollerVerticalIdentifier; + + _CPScrollerPartSizes[prefix + regularIdentifier] = [_CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 9.0), _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 1.0), _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 9.0)]; + _CPScrollerPartSizes[prefix + smallIdentifier] = [_CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 6.0), _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 1.0), _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 6.0)]; + _CPScrollerPartSizes[prefix + miniIdentifier] = [_CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 6.0), _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 1.0), _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 6.0)]; + + // Horizontal Knob Slot Sizes + var prefix = _CPScrollerClassName + _CPScrollerKnobSlotIdentifier + _CPScrollerHorizontalIdentifier; + + _CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(1.0, _CPScrollerWidths[CPRegularControlSize]); + _CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(1.0, _CPScrollerWidths[CPSmallControlSize]); + _CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(1.0, _CPScrollerWidths[CPMiniControlSize]); + + // Vertical Knob Slot Sizes + var prefix = _CPScrollerClassName + _CPScrollerKnobSlotIdentifier + _CPScrollerVerticalIdentifier; + + _CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 1.0); + _CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 1.0); + _CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 1.0); + + // Horizontal Decrement Arrows Sizes + var prefix = _CPScrollerClassName + _CPScrollerDecrementArrowIdentifier + _CPScrollerHorizontalIdentifier; + + _CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]); + _CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]); + _CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize] , _CPScrollerWidths[CPSmallControlSize]); + _CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize] , _CPScrollerWidths[CPSmallControlSize]); + _CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]); + _CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]); + + // Vertical Decrement Arrows Sizes + var prefix = _CPScrollerClassName + _CPScrollerDecrementArrowIdentifier + _CPScrollerVerticalIdentifier; + + _CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]); + _CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]); + _CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]); + _CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]); + _CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]); + _CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]); + + // Horizontal Increment Arrows Sizes + var prefix = _CPScrollerClassName + _CPScrollerIncrementArrowIdentifier + _CPScrollerHorizontalIdentifier; + + _CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]); + _CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]); + _CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize], _CPScrollerWidths[CPSmallControlSize]); + _CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize], _CPScrollerWidths[CPSmallControlSize]); + _CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]); + _CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]); + + // Vertical Increment Arrows Sizes + var prefix = _CPScrollerClassName + _CPScrollerIncrementArrowIdentifier + _CPScrollerVerticalIdentifier; + + _CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]); + _CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]); + _CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]); + _CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]); + _CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]); + _CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]); +} + +// Calculating Layout + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _controlSize = CPRegularControlSize; + _partRects = []; + + [self setFloatValue:0.0 knobProportion:1.0]; + + _isHorizontal = CPRectGetWidth(aFrame) > CPRectGetHeight(aFrame); + + _hitPart = CPScrollerNoPart; + + [self checkSpaceForUsableParts]; + [self drawParts]; + + [self layoutSubviews]; + } + + return self; +} + +// Determining CPScroller Size + ++ (float)scrollerWidth +{ + return [self scrollerWidthForControlSize:CPRegularControlSize]; +} + ++ (float)scrollerWidthForControlSize:(CPControlSize)aControlSize +{ + return _CPScrollerWidths[aControlSize]; +} + +- (void)setControlSize:(CPControlSize)aControlSize +{ + if (_controlSize == aControlSize) + return; + + _controlSize = aControlSize; + + [self drawKnobSlot]; + [self drawKnob]; + [self drawArrow:CPScrollerDecrementArrow highlight:NO]; + [self drawArrow:CPScrollerIncrementArrow highlight:NO]; + + [self layoutSubviews]; +} + +- (CPControlSize)controlSize +{ + return _controlSize; +} + +// Setting the Knob Position + +- (void)setFloatValue:(float)aValue +{ + [super setFloatValue:MIN(1.0, MAX(0.0, aValue))]; + + [self checkSpaceForUsableParts]; + [self layoutSubviews]; +} + +- (void)setFloatValue:(float)aValue knobProportion:(float)aProportion +{ + _knobProportion = MIN(1.0, MAX(0.0001, aProportion)); + + [self setFloatValue:aValue]; +} + +- (float)knobProportion +{ + return _knobProportion; +} + +// Calculating Layout + +- (CGRect)rectForPart:(CPScrollerPart)aPart +{ + if (aPart == CPScrollerNoPart) + return _CGRectMakeZero(); + + return _partRects[aPart]; +} + +- (CPScrollerPart)testPart:(CGPoint)aPoint +{ + aPoint = [self convertPoint:aPoint fromView:nil]; + + // The ordering of these tests is important. We check the knob and + // page rects first since they may overlap with the arrows. + + if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint)) + return CPScrollerKnob; + + if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint)) + return CPScrollerDecrementPage; + + if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementPage], aPoint)) + return CPScrollerIncrementPage; + + if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementLine], aPoint)) + return CPScrollerDecrementLine; + + if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementLine], aPoint)) + return CPScrollerIncrementLine; + + if (CGRectContainsPoint([self rectForPart:CPScrollerKnobSlot], aPoint)) + return CPScrollerKnobSlot; + + return CPScrollerNoPart; +} + +- (void)checkSpaceForUsableParts +{ + var bounds = [self bounds]; + + // Assume we won't be needing the arrows. + if (_knobProportion == 1.0) + { + _usableParts = CPNoScrollerParts; + + _partRects[CPScrollerDecrementPage] = _CGRectMakeZero(); + _partRects[CPScrollerKnob] = _CGRectMakeZero(); + _partRects[CPScrollerIncrementPage] = _CGRectMakeZero(); + _partRects[CPScrollerDecrementLine] = _CGRectMakeZero(); + _partRects[CPScrollerIncrementLine] = _CGRectMakeZero(); + + // In this case, the slot is the entirety of the scroller. + _partRects[CPScrollerKnobSlot] = _CGRectMakeCopy(bounds); + + return; + } + + var width = _CGRectGetWidth(bounds), + height = _CGRectGetHeight(bounds), + usableArrowHeight = _CPScrollerArrowUsableHeights[_controlSize], + slotWidth = (_isHorizontal ? width : height) - 2.0 * usableArrowHeight, + knobWidth = MAX(_CPScrollerKnobMinimumHeights[_controlSize], (slotWidth * _knobProportion)); + + _usableParts = CPAllScrollerParts; + + var arrowHeight = _CPScrollerArrowHeights[_controlSize], + knobLocation = usableArrowHeight + (slotWidth - knobWidth) * [self floatValue]; + + // At this point we know we're going to need arrows. + if (_isHorizontal) + { + // ASSERT(_CPScrollerWidths[_controlSize] == height) + + _partRects[CPScrollerDecrementPage] = _CGRectMake(usableArrowHeight, 0.0, knobLocation - usableArrowHeight, height); + _partRects[CPScrollerKnob] = _CGRectMake(knobLocation, 0.0, knobWidth, _CPScrollerWidths[_controlSize]); + _partRects[CPScrollerIncrementPage] = _CGRectMake(knobLocation + knobWidth, 0.0, width - (knobLocation + knobWidth) - usableArrowHeight, height); + _partRects[CPScrollerKnobSlot] = _CGRectMake(usableArrowHeight, 0.0, slotWidth, height); + _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, arrowHeight, height); + _partRects[CPScrollerIncrementLine] = _CGRectMake(width - _CPScrollerArrowHeights[_controlSize], 0.0, arrowHeight, height); + } + else + { + // ASSERT(_CPScrollerWidths[_controlSize] == width) + + _partRects[CPScrollerDecrementPage] = _CGRectMake(0.0, usableArrowHeight, width, knobLocation - usableArrowHeight); + _partRects[CPScrollerKnob] = _CGRectMake(0.0, knobLocation, _CPScrollerWidths[_controlSize], knobWidth); + _partRects[CPScrollerIncrementPage] = _CGRectMake(0.0, knobLocation + knobWidth, width, height - (knobLocation + knobWidth) - usableArrowHeight); + _partRects[CPScrollerKnobSlot] = _CGRectMake(0.0, usableArrowHeight, width, slotWidth); + _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, width, arrowHeight); + _partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - _CPScrollerArrowHeights[_controlSize], width, arrowHeight); + } +} + +- (CPUsableScrollerParts)usableParts +{ + return _usableParts; +} + +// Drawing the Parts + +- (void)drawArrow:(CPScrollerArrow)anArrow highlight:(BOOL)shouldHighlight +{ + var identifier = (anArrow == CPScrollerDecrementArrow ? _CPScrollerDecrementArrowIdentifier : _CPScrollerIncrementArrowIdentifier), + arrowView = (anArrow == CPScrollerDecrementArrow ? _decrementArrowView : _incrementArrowView); + + [arrowView setBackgroundColor:_CPControlColorWithPatternImage( + _CPScrollerPartSizes, + _CPScrollerClassName, + identifier, + _isHorizontal ? _CPScrollerHorizontalIdentifier : _CPScrollerVerticalIdentifier, + _CPControlIdentifierForControlSize(_controlSize), + shouldHighlight ? _CPScrollerHighlightedIdentifier : @"")]; +} + +- (void)drawKnob +{ + [_knobView setBackgroundColor:_CPControlThreePartImagePattern( + !_isHorizontal, + _CPScrollerPartSizes, + _CPScrollerClassName, + _CPScrollerKnobIdentifier, + _isHorizontal ? _CPScrollerHorizontalIdentifier : _CPScrollerVerticalIdentifier, + _CPControlIdentifierForControlSize(_controlSize))]; +} + +- (void)drawKnobSlot +{ + [_knobSlotView setBackgroundColor:_CPControlColorWithPatternImage( + _CPScrollerPartSizes, + _CPScrollerClassName, + _CPScrollerKnobSlotIdentifier, + _isHorizontal ? _CPScrollerHorizontalIdentifier : _CPScrollerVerticalIdentifier, + _CPControlIdentifierForControlSize(_controlSize))]; +} + +- (void)drawParts +{ + _knobSlotView = [[CPView alloc] initWithFrame:_CGRectMakeZero()]; + + [_knobSlotView setHitTests:NO]; + + [self addSubview:_knobSlotView]; + + [self drawKnobSlot]; + + _knobView = [[CPView alloc] initWithFrame:_CGRectMakeZero()]; + + [_knobView setHitTests:NO]; + + [self addSubview:_knobView]; + + [self drawKnob]; + + _decrementArrowView = [[CPView alloc] initWithFrame:_CGRectMakeZero()]; + + [_decrementArrowView setHitTests:NO]; + + [self addSubview:_decrementArrowView]; + + [self drawArrow:CPScrollerDecrementArrow highlight:NO]; + + _incrementArrowView = [[CPView alloc] initWithFrame:_CGRectMakeZero()]; + + [_incrementArrowView setHitTests:NO]; + + [self addSubview:_incrementArrowView]; + + [self drawArrow:CPScrollerIncrementArrow highlight:NO]; +} + +- (void)highlight:(BOOL)shouldHighlight +{ + if (_trackingPart == CPScrollerDecrementLine) + [self drawArrow:CPScrollerDecrementArrow highlight:shouldHighlight]; + + else if (_trackingPart == CPScrollerIncrementLine) + [self drawArrow:CPScrollerIncrementArrow highlight:shouldHighlight]; +} + +// Event Handling + +- (CPScrollerPart)hitPart +{ + return _hitPart; +} + +- (void)trackKnob:(CPEvent)anEvent +{ + var type = [anEvent type]; + + if (type == CPLeftMouseUp) + { + _hitPart = CPScrollerNoPart; + + return; + } + + if (type == CPLeftMouseDown) + { + _trackingFloatValue = [self floatValue]; + _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil]; + } + + else if (type == CPLeftMouseDragged) + { + var knobRect = [self rectForPart:CPScrollerKnob], + knobSlotRect = [self rectForPart:CPScrollerKnobSlot], + remainder = _isHorizontal ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect)); + + if (remainder <= 0) + [self setFloatValue:0.0]; + else + { + var location = [self convertPoint:[anEvent locationInWindow] fromView:nil]; + delta = _isHorizontal ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y; + + [self setFloatValue:_trackingFloatValue + delta / remainder]; + } + } + + [CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; + + [self sendAction:[self action] to:[self target]]; +} + +- (void)trackScrollButtons:(CPEvent)anEvent +{ + var type = [anEvent type]; + + if (type == CPLeftMouseUp) + { + [self highlight:NO]; + [CPEvent stopPeriodicEvents]; + + _hitPart = CPScrollerNoPart; + + return; + } + + if (type == CPLeftMouseDown) + { + _trackingPart = [self hitPart]; + + _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil]; + + if ([anEvent modifierFlags] & CPAlternateKeyMask) + { + if (_trackingPart == CPScrollerDecrementLine) + _hitPart = CPScrollerDecrementPage; + else if (_trackingPart == CPScrollerIncrementLine) + _hitPart = CPScrollerIncrementPage; + else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage) + { + var knobRect = [self rectForPart:CPScrollerKnob], + knobWidth = _isHorizontal ? _CGRectGetWidth(knobRect) : _CGRectGetHeight(knobRect), + knobSlotRect = [self rectForPart:CPScrollerKnobSlot], + remainder = (_isHorizontal ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth; + + [self setFloatValue:((_isHorizontal ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder]; + + _hitPart = CPScrollerKnob; + + [self sendAction:[self action] to:[self target]]; + + // Now just track the knob. + return [self trackKnob:anEvent]; + } + } + + [self highlight:YES]; + [self sendAction:[self action] to:[self target]]; + + [CPEvent startPeriodicEventsAfterDelay:0.5 withPeriod:0.04]; + } + + else if (type == CPLeftMouseDragged) + { + _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil]; + + if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage) + { + var hitPart = [self testPart:[anEvent locationInWindow]]; + + if (hitPart == CPScrollerDecrementPage || hitPart == CPScrollerIncrementPage) + { + _trackingPart = hitPart; + _hitPart = hitPart; + } + } + + [self highlight:CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)]; + } + else if (type == CPPeriodic && CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)) + [self sendAction:[self action] to:[self target]]; + + [CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; + +} + +- (void)setFrameSize:(CGSize)aSize +{ + [super setFrameSize:aSize]; + + [self checkSpaceForUsableParts]; + + var frame = [self frame], + isHorizontal = CPRectGetWidth(frame) > CPRectGetHeight(frame); + + if (_isHorizontal != isHorizontal) + { + _isHorizontal = isHorizontal; + + [self drawParts]; + } + + [self layoutSubviews]; +} + +- (void)layoutSubviews +{ + [_knobSlotView setFrame:[self rectForPart:CPScrollerKnobSlot]]; + + var usableParts = [self usableParts], + hidden = !(usableParts == CPAllScrollerParts); + + if (hidden != [_knobView isHidden]) + { + [_knobView setHidden:hidden]; + [_decrementArrowView setHidden:hidden]; + [_incrementArrowView setHidden:hidden]; + } + + if (!hidden) + { + [_knobView setFrame:[self rectForPart:CPScrollerKnob]]; + [_decrementArrowView setFrame:[self rectForPart:CPScrollerDecrementLine]]; + [_incrementArrowView setFrame:[self rectForPart:CPScrollerIncrementLine]]; + } +} + +- (void)mouseDown:(CPEvent)anEvent +{ + _hitPart = [self testPart:[anEvent locationInWindow]]; + + switch (_hitPart) + { + case CPScrollerKnob: return [self trackKnob:anEvent]; + + case CPScrollerDecrementLine: + case CPScrollerIncrementLine: + case CPScrollerDecrementPage: + case CPScrollerIncrementPage: return [self trackScrollButtons:anEvent]; + } +} + +// FIXME: This is a result of the "dumb" code that just makes things transparent when disabled. +- (void)setEnabled:(BOOL)shouldBeEnabled +{ +} + +@end diff --git a/AppKit/CPSegmentedControl.j b/AppKit/CPSegmentedControl.j new file mode 100644 index 0000000000..925f69c106 --- /dev/null +++ b/AppKit/CPSegmentedControl.j @@ -0,0 +1,542 @@ +/* + * CPSegmentedControl.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import "CPControl.j" + + +CPSegmentSwitchTrackingSelectOne = 0; +CPSegmentSwitchTrackingSelectAny = 1; +CPSegmentSwitchTrackingMomentary = 2; + +@implementation CPSegmentedControl : CPControl +{ + unsigned _segmentCount; + + CPArray _segments; + CPArray _selectedSegment; + + CPSegmentSwitchTracking _trackingMode; + unsigned _trackingSegment; + BOOL _trackingHighlighted; +} + +- (id)initWithFrame:(CGRect)aRect +{ + self = [super initWithFrame:aRect]; + + if (self) + { + _segments = []; + _selectedSegment = -1; + + _segmentCount = 0; + + _trackingMode = CPSegmentSwitchTrackingSelectOne; + } + + return self; +} + +- (int)selectedTag +{ + return _segments[_selectedSegment].tag; +} + +// Specifying the number of segments + +- (void)setSegmentCount:(unsigned)aCount +{ + if (_segmentCount == aCount) + return; + + var height = CGRectGetHeight([self bounds]); + + if (_segmentCount < aCount) + { + var index = _segmentCount; + + for (; index < aCount; ++index) + { + _segments[index] = _CPSegmentMake(); + _segments[index].frame.size.height = height; + } + } + else if (aCount < _segmentCount) + { + var index = aCount; + + for (; index < _segmentCount; ++index) + { + [_segments[index].imageView removeFromSuperview]; + [_segments[index].labelView removeFromSuperview]; + + _segments[index] = nil; + } + } + + _segmentCount = aCount; + + if (_selectedSegment < _segmentCount) + _selectedSegment = -1; + + [self tileWithChangedSegment:0]; +} + +- (unsigned)segmentCount +{ + return _segmentCount; +} + +// Specifying Selected Segment + +- (void)setSelectedSegment:(unsigned)aSegment +{ + [self setSelected:YES forSegment:aSegment]; +} + +- (unsigned)selectedSegment +{ + return _selectedSegment; +} + +- (BOOL)selectSegmentWithTag:(int)aTag +{ + var index = 0; + + for (; index < _segmentCount; ++index) + if (_segments[index].tag == aTag) + { + [self setSelectedSegment:index]; + + return YES; + } + + return NO; +} + +// Specifying Tracking Mode + +- (BOOL)isTracking +{ + +} + +- (void)setTrackingMode:(CPSegmentSwitchTracking)aTrackingMode +{ + if (_trackingMode == aTrackingMode) + return; + + _trackingMode = aTrackingMode; + + if (_trackingMode == CPSegmentSwitchTrackingSelectOne) + { + var index = 0, + selected = NO; + + for (; index < _segmentCount; ++index) + if (_segments[index].selected) + if (selected) + [self setSelected:NO forSegment:index]; + else + selected = YES; + } + + else if (_trackingMode == CPSegmentSwitchTrackingMomentary) + { + var index = 0; + + for (; index < _segmentCount; ++index) + if (_segments[index].selected) + [self setSelected:NO forSegment:index]; + } +} + +- (CPSegmetnSwitchTracking)trackingMode +{ + return _trackingMode; +} + +// Working with Individual Segments + +- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment +{ + _segments[aSegment].width = aWidth; + + [self tileWithChangedSegment:aSegment]; +} + +- (float)widthForSegment:(unsigned)aSegment +{ + return _segments[aSegment].width; +} + +- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment +{ + var segment = _segments[aSegment]; + + if (!anImage) + { + [segment.imageView removeFromSuperview]; + + segment.imageView = nil; + } + + else + { + if (!segment.imageView) + { + segment.imageView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; + + [self addSubview:segment.imageView]; + } + + [segment.imageView setImage:anImage]; + [segment.imageView setFrameSize:CGSizeMakeCopy([anImage size])]; + } + + segment.image = anImage; + + if (segment.width) + [self drawSegment:aSegment highlight:NO]; + else + [self tileWithChangedSegment:aSegment]; +} + +- (CPImage)imageForSegment:(unsigned)aSegment +{ + return _segments[aSegment].image; +} + +- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment +{ + var segment = _segments[aSegment]; + + if (!aLabel || !aLabel.length) + { + [segment.labelView removeFromSuperview]; + + segment.labelView = nil; + } + + else + { + if (!segment.labelView) + { + segment.labelView = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; + + [segment.labelView setFont:[self font]]; + + [self addSubview:segment.labelView]; + } + + [segment.labelView setStringValue:aLabel]; + [segment.labelView sizeToFit]; + } + + _segments[aSegment].label = aLabel; + + if (segment.width) + [self drawSegment:aSegment highlight:NO]; + else + [self tileWithChangedSegment:aSegment]; +} + +- (CPImage)labelForSegment:(unsigned)aSegment +{ + return _segments[aSegment].label; +} + +- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment +{ + _segments[aSegment].menu = aMenu; +} + +- (CPImage)menuForSegment:(unsigned)aSegment +{ + return _segments[aSegment].menu; +} + +- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment +{ + // FIXME: EXCEPTION REQUIRED + if (aSegment < 0 || aSegment >= _segments.length) + return; + + var segment = _segments[aSegment]; + + // If we're already in this state, bail. + if (segment.selected == isSelected) + return; + + segment.selected = isSelected; + + // We need to do some cleanup if we only allow one selection. + if (isSelected) + { + var oldSelectedSegment = _selectedSegment; + + _selectedSegment = aSegment; + + if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1) + { + _segments[oldSelectedSegment].selected = NO; + + [self drawSegmentBezel:oldSelectedSegment highlight:NO]; + } + } + + if (_trackingMode != CPSegmentSwitchTrackingMomentary) + [self drawSegmentBezel:aSegment highlight:NO]; +} + +- (CPImage)isSelectedForSegment:(unsigned)aSegment +{ + return _segments[aSegment].selected; +} + +- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment +{ + _segments[aSegment].enabled = isEnabled; +} + +- (CPImage)isEnabledForSegment:(unsigned)aSegment +{ + return _segments[aSegment].enabled; +} + +- (void)setTag:(int)aTag forSegment:(unsigned)aSegment +{ + _segments[aSegment].tag = aTag; +} + +- (int)tagForSegment:(unsigned)aSegment +{ + return _segments[aSegment].tag; +} + +// Drawings + +- (void)drawSegmentBezel:(int)aSegment highlgiht:(BOOL)shouldHighlight +{ +} + +- (void)drawSegment:(int)aSegment highlight:(BOOL)shouldHighlight +{ + var segment = _segments[aSegment], + + imageView = segment.imageView, + labelView = segment.labelView, + + frame = segment.frame, + + segmentX = CGRectGetMinX(frame), + segmentWidth = CGRectGetWidth(frame), + segmentHeight = CGRectGetHeight(frame) - 1.0; + + if (imageView && labelView) + { + var imageViewSize = [imageView frame].size, + labelViewSize = [labelView frame].size, + totalHeight = imageViewSize.height + labelViewSize.height, + labelWidth = MIN(labelViewSize.width, width), + y = (segmentHeight - totalHeight) / 2.0; + + [imageView setFrameOrigin:CGPointMake(segmentX + (segmentWidth - imageViewSize.width) / 2.0, y)]; + + if (labelWidth < labelViewSize.width) + [labelView setFrameSize:CGSizeMake(labelWidth, labelViewSize.height)]; + + [labelView setFrameOrigin:CGPointMake(segmentX + (segmentWidth - labelWidth) / 2.0, y + imageViewSize.height)]; + } + else if (imageView) + { + var imageViewSize = [imageView frame].size; + + [imageView setFrameOrigin:CGPointMake(segmentX + (segmentWidth - imageViewSize.width) / 2.0, (segmentHeight - imageViewSize.height) / 2.0)]; + } + else if (labelView) + { + var labelViewSize = [labelView frame].size, + labelWidth = MIN(labelViewSize.width, segmentWidth); + + if (labelWidth < labelViewSize.width) + [labelView setFrameSize:CGSizeMake(labelWidth, labelViewSize.height)]; + + [labelView setFrameOrigin:CGPointMake(segmentX + (segmentWidth - labelWidth) / 2.0, (segmentHeight - labelViewSize.height) / 2.0)]; + } +} + +- (void)tileWithChangedSegment:(unsigned)aSegment +{ + var segment = _segments[aSegment], + segmentWidth = segment.width; + + if (!segmentWidth) + { + if (segment.labelView && segment.imageView) + segmentWidth = MAX(CGRectGetWidth([segment.labelView frame]) , CGRectGetWidth([segment.imageView frame])); + else if (segment.labelView) + segmentWidth = CGRectGetWidth([segment.labelView frame]); + else if (segment.imageView) + segmentWidth = CGRectGetWidth([segment.imageView frame]); + } + + var delta = segmentWidth - CGRectGetWidth(segment.frame); + + if (!delta) + return; + + // Update Contorl Size + var frame = [self frame]; + + [self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))]; + + // Update Segment Width + segment.frame.size.width = segmentWidth; + + // Update Following Segments Widths + var index = aSegment + 1; + + for (; index < _segmentCount; ++index) + { + _segments[index].frame.origin.x += delta; + + [self drawSegmentBezel:index highlight:NO]; + [self drawSegment:index highlight:NO]; + } + + [self drawSegmentBezel:aSegment highlight:NO]; + [self drawSegment:aSegment highlight:NO]; +} + +- (CGRect)frameForSegment:(unsigned)aSegment +{ + return _segments[aSegment].frame; +} + +- (unsigned)testSegment:(CGPoint)aPoint +{ + var location = [self convertPoint:aPoint fromView:nil], + count = _segments.length; + + while (count--) + if (CGRectContainsPoint(_segments[count].frame, aPoint)) + return count; + + return -1; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + if (![self isEnabled]) + return; + + [self trackSegment:anEvent]; +} + +// FIXME: this should be fixed way up in cpbutton/cpcontrol. +- (void)mouseUp:(CPEvent)anEvent +{ +} + +- (void)trackSegment:(CPEvent)anEvent +{ + var type = [anEvent type], + location = [self convertPoint:[anEvent locationInWindow] fromView:nil]; + + if (type == CPLeftMouseUp) + { + if (CGRectContainsPoint(_segments[_trackingSegment].frame, location)) + { + if (_trackingMode == CPSegmentSwitchTrackingSelectAny) + { + [self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment]; + + // With ANY, _selectedSegment means last pressed. + _selectedSegment = _trackingSegment; + } + else + [self setSelected:YES forSegment:_trackingSegment]; + + [self sendAction:[self action] to:[self target]]; + + if (_trackingMode == CPSegmentSwitchTrackingMomentary) + { + [self setSelected:NO forSegment:_trackingSegment]; + + _selectedSegment = -1; + } + } + + [self drawSegmentBezel:_trackingSegment highlight:NO]; + + _trackingSegment = -1; + + return; + } + + if (type == CPLeftMouseDown) + { + _trackingHighlighted = YES; + _trackingSegment = [self testSegment:location]; + + [self drawSegmentBezel:_trackingSegment highlight:YES]; + } + + else if (type == CPLeftMouseDragged) + { + var highlighted = CGRectContainsPoint(_segments[_trackingSegment].frame, location); + + if (highlighted != _trackingHighlighted) + { + _trackingHighlighted = highlighted; + + [self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted]; + } + } + + [CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; +} + +- (void)setFont:(CPFont)aFont +{ + [super setFont:aFont]; + + var count = _segmentCount; + + if (!count) + return; + + while (count--) + [_segments[count].labelView setFont:aFont]; + + [self tileWithChangedSegment:0]; +} + +@end + +var _CPSegmentMake = function() +{ + return { width:0, image:nil, label:@"", menu:nil, selected:NO, enabled:NO, tag:0, labelView:nil, imageView:nil, frame:CGRectMakeZero() } +} diff --git a/AppKit/CPShadow.j b/AppKit/CPShadow.j new file mode 100644 index 0000000000..30b92fd526 --- /dev/null +++ b/AppKit/CPShadow.j @@ -0,0 +1,75 @@ +/* + * CPShadow.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +@implementation CPShadow : CPObject +{ + CPSize _offset; + float _blurRadius; + CPColor _color; + + CPString _cssString; +} + ++ (id)shadowWithOffset:(CPSize)anOffset blurRadius:(float)aBlurRadius color:(CPColor)aColor +{ + return [[CPShadow alloc] _initWithOffset:anOffset blurRadius:aBlurRadius color:aColor]; +} + +- (id)_initWithOffset:(CPSize)anOffset blurRadius:(float)aBlurRadius color:(CPColor)aColor +{ + self = [super init]; + + if (self) + { + _offset = anOffset; + _blurRadius = aBlurRadius; + _color = aColor; + + _cssString = [_color cssString] + " " + Math.round(anOffset.width) + @"px " + Math.round(anOffset.height) + @"px " + Math.round(_blurRadius) + @"px"; + } + + return self; +} + +- (CPSize)shadowOffset +{ + return _offset; +} + +- (float)shadowBlurRadius +{ + return _blurRadius; +} + +- (CPColor)shadowColor +{ + return _color; +} + +- (CPString)cssString +{ + return _cssString; +} + +@end \ No newline at end of file diff --git a/AppKit/CPShadowView.j b/AppKit/CPShadowView.j new file mode 100644 index 0000000000..0f306e2373 --- /dev/null +++ b/AppKit/CPShadowView.j @@ -0,0 +1,175 @@ +/* + * CPShadowView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import "CGGeometry.j" +import "CPImage.j" +import "CPImageView.j" +import "CPView.j" + +#include "CoreGraphics/CGGeometry.h" + +CPLightShadow = 0; +CPHeavyShadow = 1; + +var CPShadowViewLightBackgroundColor = nil, + CPShadowViewHeavyBackgroundColor = nil; + +var LIGHT_LEFT_INSET = 3.0, + LIGHT_RIGHT_INSET = 3.0, + LIGHT_TOP_INSET = 3.0, + LIGHT_BOTTOM_INSET = 5.0, + + HEAVY_LEFT_INSET = 7.0, + HEAVY_RIGHT_INSET = 7.0, + HEAVY_TOP_INSET = 5.0, + HEAVY_BOTTOM_INSET = 5.0; + +@implementation CPShadowView : CPView +{ + CPShadowWeight _weight; +} + ++ (void)initialize +{ + if (self != [CPShadowView class]) + return; + + var bundle = [CPBundle bundleForClass:[self class]]; + + CPShadowViewLightBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopLeft.png"] size:CGSizeMake(9.0, 9.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTop.png"] size:CGSizeMake(1.0, 9.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopRight.png"] size:CGSizeMake(9.0, 9.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightLeft.png"] size:CGSizeMake(9.0, 1.0)], + nil, + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightRight.png"] size:CGSizeMake(9.0, 1.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomLeft.png"] size:CGSizeMake(9.0, 9.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottom.png"] size:CGSizeMake(1.0, 9.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomRight.png"] size:CGSizeMake(9.0, 9.0)] + ]]]; + + CPShadowViewHeavyBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopLeft.png"] size:CGSizeMake(17.0, 17.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTop.png"] size:CGSizeMake(1.0, 17.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopRight.png"] size:CGSizeMake(17.0, 17.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyLeft.png"] size:CGSizeMake(17.0, 1.0)], + nil, + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyRight.png"] size:CGSizeMake(17.0, 1.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomLeft.png"] size:CGSizeMake(17.0, 17.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottom.png"] size:CGSizeMake(1.0, 17.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomRight.png"] size:CGSizeMake(17.0, 17.0)] + ]]]; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _weight = CPLightShadow; + + [self setBackgroundColor:CPShadowViewLightBackgroundColor]; + + [self setHitTests:NO]; + } + + return self; +} + +- (void)setWeight:(CPShadowWeight)aWeight +{ + if (_weight == aWeight) + return; + + _weight = aWeight; + + if (_weight == CPLightShadow) + [self setBackgroundColor:CPShadowViewLightBackgroundColor]; + + else + [self setBackgroundColor:CPShadowViewHeavyBackgroundColor]; +} + +- (float)leftInset +{ + return _weight == CPLightShadow ? LIGHT_LEFT_INSET : HEAVY_LEFT_INSET; +} + +- (float)rightInset +{ + return _weight == CPLightShadow ? LIGHT_RIGHT_INSET : HEAVY_RIGHT_INSET; +} + +- (float)topInset +{ + return _weight == CPLightShadow ? LIGHT_TOP_INSET : HEAVY_TOP_INSET; +} + +- (float)bottomInset +{ + return _weight == CPLightShadow ? LIGHT_BOTTOM_INSET : HEAVY_BOTTOM_INSET; +} + +- (float)horizontalInset +{ + if (_weight == CPLightShadow) + return LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET; + + return HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET; +} + +- (float)verticalInset +{ + if (_weight == CPLightShadow) + return LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET; + + return HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET; +} + ++ (CGRect)frameForContentFrame:(CGRect)aFrame withWeight:(CPShadowWeight)aWeight +{ + if (aWeight == CPLightShadow) + return CGRectMake(_CGRectGetMinX(aFrame) - LIGHT_LEFT_INSET, _CGRectGetMinY(aFrame) - LIGHT_TOP_INSET, _CGRectGetWidth(aFrame) + LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET, _CGRectGetHeight(aFrame) + LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET); + else + return CGRectMake(_CGRectGetMinX(aFrame) - HEAVY_LEFT_INSET, _CGRectGetMinY(aFrame) - HEAVY_TOP_INSET, _CGRectGetWidth(aFrame) + HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET, _CGRectGetHeight(aFrame) + HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET); +} + +- (CGRect)frameForContentFrame:(CGRect)aFrame +{ + return [[self class] frameForContentFrame:aFrame withWeight:_weight]; +} + +- (void)setFrameForContentFrame:(CGRect)aFrame +{ + [self setFrame:[self frameForContentFrame:aFrame]]; +} + +@end diff --git a/AppKit/CPSlider.j b/AppKit/CPSlider.j new file mode 100644 index 0000000000..9c0020a7fd --- /dev/null +++ b/AppKit/CPSlider.j @@ -0,0 +1,378 @@ +/* + * CPSlider.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPControl.j" + +var CPSliderHorizontalKnobImage = nil + CPSliderHorizontalBarLeftImage = nil, + CPSliderHorizontalBarRightImage = nil, + CPSliderHorizontalBarCenterImage = nil; + +@implementation CPSlider : CPControl +{ + double _value; + double _minValue; + double _maxValue; + double _altIncrementValue; + + CPView _bar; + CPView _knob; + + BOOL _isVertical; + CPImageView _standardKnob; + CPView _standardVerticalBar; + CPView _standardHorizontalBar; +} + ++ (void)initialize +{ + if (self != [CPSlider class]) + return; + + var bundle = [CPBundle bundleForClass:self]; + + CPSliderKnobImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderKnobRegular.png"] size:CPSizeMake(11.0, 11.0)], + CPSliderKnobPushedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderKnobRegularPushed.png"] size:CPSizeMake(11.0, 11.0)], + CPSliderHorizontalBarLeftImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderTrackHorizontalLeft.png"] size:CPSizeMake(2.0, 4.0)], + CPSliderHorizontalBarRightImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderTrackHorizontalRight.png"] size:CPSizeMake(2.0, 4.0)], + CPSliderHorizontalBarCenterImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderTrackHorizontalCenter.png"] size:CPSizeMake(1.0, 4.0)]; +} + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _value = 50.0; + _minValue = 0.0; + _maxValue = 100.0; + + _bar = [self bar]; + _knob = [self knob]; + _knobSize = [[self knobImage] size]; + _isVertical = [self isVertical]; + + [_knob setFrameOrigin:[self knobPosition]]; + + [self addSubview:_bar]; + [self addSubview:_knob]; + } + + return self; +} + +- (void)setFrameSize:(CPSize)aSize +{ + if(aSize.height > 21.0) + aSize.height = 21.0; + + if (_isVertical != [self isVertical]) + { + _isVertical = [self isVertical]; + + var bar = [self bar], + knob = [self knob]; + + if (_bar != bar) + [self replaceSubview:_bar = bar withView:_bar]; + + if (_knob != knob) + { + [self replaceSubview:knob withView:_knob]; + + _knob = knob; + [_knob setFrameOrigin:[self knobPosition]]; + } + } + + [super setFrameSize:aSize]; + + [_knob setFrameOrigin:[self knobPosition]]; +} + +- (double)altIncrementValue +{ + return _altIncrementValue; +} + +- (float)knobThickness +{ + return CPRectGetWidth([_knob frame]); +} + +- (CPImage)leftTrackImage +{ + return CPSliderHorizontalBarLeftImage; +} + +- (CPImage)rightTrackImage +{ + return CPSliderHorizontalBarRightImage; +} + +- (CPImage)centerTrackImage +{ + return CPSliderHorizontalBarCenterImage +} + +- (CPImage)knobImage +{ + return CPSliderKnobImage; +} + +- (CPImage)pushedKnobImage +{ + return CPSliderKnobPushedImage; +} + +- (CPView)knob +{ + if (!_standardKnob) + { + var knobImage = [self knobImage], + knobSize = [knobImage size]; + + _standardKnob = [[CPImageView alloc] initWithFrame:CPRectMake(0.0, 0.0, knobSize.width, knobSize.height)]; + + [_standardKnob setImage:knobImage]; + } + + return _standardKnob; +} + +- (CPView)bar +{ + // FIXME: veritcal. + if ([self isVertical]) + return nil; + else + { + if (!_standardHorizontalBar) + { + var frame = [self frame], + barFrame = CPRectMake(0.0, 0.0, CPRectGetWidth(frame), 4.0); + + _standardHorizontalBar = [[CPView alloc] initWithFrame:barFrame]; + + [_standardHorizontalBar setBackgroundColor:[CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: + [[self leftTrackImage], [self centerTrackImage], [self rightTrackImage]] isVertical:NO]]]; + + [_standardHorizontalBar setFrame:CPRectMake(0.0, (CPRectGetHeight(frame) - CPRectGetHeight(barFrame)) / 2.0, CPRectGetWidth(_isVertical ? barFrame : frame), CPRectGetHeight(_isVertical ? frame : barFrame))]; + [_standardHorizontalBar setAutoresizingMask:_isVertical ? CPViewHeightSizable : CPViewWidthSizable]; + } + + return _standardHorizontalBar; + } +} + +- (void)setAltIncrementValue:(double)anIncrementValue +{ + _altIncrementValue = anIncrementValue; +} + +- (int)isVertical +{ + var frame = [self frame]; + + if (CPRectGetWidth(frame) == CPRectGetHeight(frame)) + return -1; + + return CPRectGetWidth(frame) < CPRectGetHeight(frame); +} + +- (double)maxValue +{ + return _maxValue; +} + +- (double)minValue +{ + return _minValue; +} + +- (void)setMaxValue:(double)aMaxValue +{ + _maxValue = aMaxValue; +} + +- (void)setMinValue:(double)aMinValue +{ + _minValue = aMinValue; +} + +- (void)setValue:(double)aValue +{ + _value = aValue; + + [_knob setFrameOrigin:[self knobPosition]]; +} + +- (double)value +{ + return _value; +} + +- (CPPoint)knobPosition +{ + if ([self isVertical]) + return CPPointMake(0.0, 0.0); + else + return CPPointMake( + ((_value - _minValue) / (_maxValue - _minValue)) * (CPRectGetWidth([self frame]) - CPRectGetWidth([_knob frame])), + (CPRectGetHeight([self frame]) - CPRectGetHeight([_knob frame])) / 2.0); +} + +- (float)valueForKnobPosition:(CPPoint)aPoint +{ + if ([self isVertical]) + return 0.0; + else + return MAX(MIN((aPoint.x) * (_maxValue - _minValue) / ( CPRectGetWidth([self frame]) - CPRectGetWidth([_knob frame]) ) + _minValue, _maxValue), _minValue); +} + +- (CPPoint)constrainKnobPosition:(CPPoint)aPoint +{ + //FIXME + aPoint.x -= _knobSize.width / 2.0; + return CPPointMake(MAX(MIN(CPRectGetWidth([self bounds]) - _knobSize.width, aPoint.x), 0.0), (CPRectGetHeight([self bounds]) - CPRectGetHeight([_knob frame])) / 2.0); +} + +- (void)mouseUp:(CPEvent)anEvent +{ + [[self knob] setImage: [self knobImage]]; + + if ([_target respondsToSelector:@selector(sliderDidFinish:)]) + [_target sliderDidFinish:self]; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + [_knob setFrameOrigin:[self constrainKnobPosition:[self convertPoint:[anEvent locationInWindow] fromView:nil]]]; + + _value = [self valueForKnobPosition:[_knob frame].origin]; + + [[self knob] setImage: [self pushedKnobImage]]; + [self sendAction:_action to:_target]; +} + +- (void)mouseDragged:(CPEvent)anEvent +{ + [_knob setFrameOrigin:[self constrainKnobPosition:[self convertPoint:[anEvent locationInWindow] fromView:nil]]]; + + _value = [self valueForKnobPosition:[_knob frame].origin]; + + [self sendAction:_action to:_target]; +} + +@end + +var CPSliderMinValueKey = @"CPSliderMinValueKey", + CPSliderMaxValueKey = @"CPSliderMaxValueKey", + CPSliderValueKey = @"CPSliderValueKey"; + +@implementation CPSlider (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + _value = [aCoder decodeDoubleForKey:CPSliderValueKey]; + _minValue = [aCoder decodeDoubleForKey:CPSliderMinValueKey]; + _maxValue = [aCoder decodeDoubleForKey:CPSliderMaxValueKey]; + + _bar = [self bar]; + _knob = [self knob]; + _knobSize = [[self knobImage] size]; + _isVertical = [self isVertical]; + + [_knob setFrameOrigin:[self knobPosition]]; + + [self addSubview:_bar]; + [self addSubview:_knob]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + var subviews = _subviews; + + _subviews = []; + + [super encodeWithCoder:aCoder]; + + _subviews = subviews; + + [aCoder encodeDouble:_value forKey:CPSliderValueKey]; + [aCoder encodeDouble:_minValue forKey:CPSliderMinValueKey]; + [aCoder encodeDouble:_maxValue forKey:CPSliderMaxValueKey]; +} + +@end + +var CPHUDSliderKnobImage = nil, + CPHUDSliderHorizontalBarLeftImage = nil, + CPHUDSliderHorizontalBarRightImage = nil, + CPHUDSliderHorizontalBarCenterImage = nil; + +@implementation CPHUDSlider : CPSlider +{ +} + ++ (void)initialize +{ + if (self != [CPHUDSlider class]) + return; + + var bundle = [CPBundle bundleForClass:self]; + + CPHUDSliderKnobImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/CPSliderHorizontalKnob.png"] size:CPSizeMake(12.0, 12.0)], + CPHUDSliderHorizontalBarLeftImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/CPSliderHorizontalBarLeft.png"] size:CPSizeMake(3.0, 4.0)], + CPHUDSliderHorizontalBarRightImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/CPSliderHorizontalBarRight.png"] size:CPSizeMake(3.0, 4.0)], + CPHUDSliderHorizontalBarCenterImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/CPSliderHorizontalBarCenter.png"] size:CPSizeMake(1.0, 4.0)]; +} + +- (CPImage)leftTrackImage +{ + return CPHUDSliderHorizontalBarLeftImage; +} + +- (CPImage)rightTrackImage +{ + return CPHUDSliderHorizontalBarRightImage; +} + +- (CPImage)centerTrackImage +{ + return CPHUDSliderHorizontalBarCenterImage +} + +- (CPImage)knobImage +{ + return CPHUDSliderKnobImage; +} + +@end diff --git a/AppKit/CPSliderColorPicker.j b/AppKit/CPSliderColorPicker.j new file mode 100644 index 0000000000..f115b1154d --- /dev/null +++ b/AppKit/CPSliderColorPicker.j @@ -0,0 +1,404 @@ +/* + * CPApplication.j + * AppKit + * + * Created by Ross Boucher. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import +import +import +import + +@implementation CPSliderColorPicker : CPColorPicker +{ + CPView _contentView; + + CPSlider _redSlider; + CPSlider _greenSlider; + CPSlider _blueSlider; + CPSlider _hueSlider; + CPSlider _saturationSlider; + CPSlider _brightnessSlider; + + CPTextField _rgbLabel; + CPTextField _hsbLabel; + CPTextField _redLabel; + CPTextField _greenLabel; + CPTextField _blueLabel; + CPTextField _hueLabel; + CPTextField _saturationLabel; + CPTextField _brightnessLabel; + CPTextField _hexLabel; + + DOMElement _redValue; + DOMElement _greenValue; + DOMElement _blueValue; + DOMElement _hueValue; + DOMElement _saturationValue; + DOMElement _brightnessValue; + DOMElement _hexValue; +} + +- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel +{ + return [super initWithPickerMask:mask colorPanel: owningColorPanel]; +} + +-(id)initView +{ + aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight); + _contentView = [[CPView alloc] initWithFrame:aFrame]; + + _rgbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 10, 100, 20)]; + [_rgbLabel setStringValue: "RGB"]; + [_rgbLabel setTextColor:[CPColor whiteColor]]; + + _redLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 35, 15, 20)]; + [_redLabel setStringValue: "R"]; + [_redLabel setTextColor:[CPColor whiteColor]]; + + _redSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 35, aFrame.size.width - 70, 20)]; + [_redSlider setMaxValue: 1.0]; + [_redSlider setMinValue: 0.0]; + [_redSlider setTarget: self]; + [_redSlider setAction: @selector(sliderChanged:)]; + [_redSlider setAutoresizingMask: CPViewWidthSizable]; + + var updateFunction = function(aDOMEvent) + { + if(isNaN(this.value)) + return; + + switch(this) + { + case _redValue: [_redSlider setValue: MAX(MIN(ROUND(this.value), 255) / 255.0, 0)]; + [self sliderChanged: _redSlider]; + break; + + case _greenValue: [_greenSlider setValue: MAX(MIN(ROUND(this.value), 255) / 255.0, 0)]; + [self sliderChanged: _greenSlider]; + break; + + case _blueValue: [_blueSlider setValue: MAX(MIN(ROUND(this.value), 255) / 255.0, 0)]; + [self sliderChanged: _blueSlider]; + break; + + case _hueValue: [_hueSlider setValue: MAX(MIN(ROUND(this.value), 360), 0)]; + [self sliderChanged: _hueSlider]; + break; + + case _saturationValue: [_saturationSlider setValue: MAX(MIN(ROUND(this.value), 100), 0)]; + [self sliderChanged: _saturationSlider]; + break; + + case _brightnessValue: [_brightnessSlider setValue: MAX(MIN(ROUND(this.value), 100), 0)]; + [self sliderChanged: _brightnessSlider]; + break; + } + + this.blur(); + }; + + var keypressFunction = function(aDOMEvent) + { + aDOMEvent = aDOMEvent || window.event; + if (aDOMEvent.keyCode == 13) + { + updateFunction(aDOMEvent); + + if(aDOMEvent.preventDefault) + aDOMEvent.preventDefault(); + else if(aDOMEvent.stopPropagation) + aDOMEvent.stopPropagation(); + } + } + + //red value input box + var redValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 35, 45, 20)]; + [redValue setAutoresizingMask: CPViewMinXMargin]; + + _redValue = document.createElement("input"); + _redValue.style.width = "40px"; + _redValue.style.backgroundColor = "transparent"; + _redValue.style.border = "1px solid white"; + _redValue.style.color = "white"; + _redValue.style.position = "absolute"; + _redValue.style.top = "0px"; + _redValue.style.left = "0px"; + _redValue.onchange = updateFunction; + + redValue._DOMElement.appendChild(_redValue); + [_contentView addSubview: redValue]; + + _greenLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 58, 15, 20)]; + [_greenLabel setStringValue: "G"]; + [_greenLabel setTextColor:[CPColor whiteColor]]; + + _greenSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 58, aFrame.size.width - 70, 20)]; + [_greenSlider setMaxValue: 1.0]; + [_greenSlider setMinValue: 0.0]; + [_greenSlider setTarget: self]; + [_greenSlider setAction: @selector(sliderChanged:)]; + [_greenSlider setAutoresizingMask: CPViewWidthSizable]; + + //green value input box + var greenValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 58, 45, 20)]; + [greenValue setAutoresizingMask: CPViewMinXMargin]; + + _greenValue = _redValue.cloneNode(false); + _greenValue.onchange = updateFunction; + + greenValue._DOMElement.appendChild(_greenValue); + [_contentView addSubview: greenValue]; + + _blueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 81, 15, 20)]; + [_blueLabel setStringValue: "B"]; + [_blueLabel setTextColor:[CPColor whiteColor]]; + + _blueSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 81, aFrame.size.width - 70, 20)]; + [_blueSlider setMaxValue: 1.0]; + [_blueSlider setMinValue: 0.0]; + [_blueSlider setTarget: self]; + [_blueSlider setAction: @selector(sliderChanged:)]; + [_blueSlider setAutoresizingMask: CPViewWidthSizable]; + + //blue value input box + var blueValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 81, 45, 20)]; + [blueValue setAutoresizingMask: CPViewMinXMargin]; + + _blueValue = _redValue.cloneNode(false); + _blueValue.onchange = updateFunction; + + blueValue._DOMElement.appendChild(_blueValue); + [_contentView addSubview: blueValue]; + + _hsbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 120, 100, 20)]; + [_hsbLabel setStringValue: "HSB"]; + [_hsbLabel setTextColor:[CPColor whiteColor]]; + + _hueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 145, 15, 20)]; + [_hueLabel setStringValue: "H"]; + [_hueLabel setTextColor:[CPColor whiteColor]]; + + _hueSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 145, aFrame.size.width - 70, 20)]; + [_hueSlider setMaxValue: 359.0]; + [_hueSlider setMinValue: 0.0]; + [_hueSlider setTarget: self]; + [_hueSlider setAction: @selector(sliderChanged:)]; + [_hueSlider setAutoresizingMask: CPViewWidthSizable]; + + //red value input box + var hueValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 145, 45, 20)]; + [hueValue setAutoresizingMask: CPViewMinXMargin]; + + _hueValue = _redValue.cloneNode(false); + _hueValue.onchange = updateFunction; + + hueValue._DOMElement.appendChild(_hueValue); + [_contentView addSubview: hueValue]; + + _saturationLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 168, 15, 20)]; + [_saturationLabel setStringValue: "S"]; + [_saturationLabel setTextColor:[CPColor whiteColor]]; + + _saturationSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 168, aFrame.size.width - 70, 20)]; + [_saturationSlider setMaxValue: 100.0]; + [_saturationSlider setMinValue: 0.0]; + [_saturationSlider setTarget: self]; + [_saturationSlider setAction: @selector(sliderChanged:)]; + [_saturationSlider setAutoresizingMask: CPViewWidthSizable]; + + //green value input box + var saturationValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 168, 45, 20)]; + [saturationValue setAutoresizingMask: CPViewMinXMargin]; + + _saturationValue = _redValue.cloneNode(false); + _saturationValue.onchange = updateFunction; + + saturationValue._DOMElement.appendChild(_saturationValue); + [_contentView addSubview: saturationValue]; + + _brightnessLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 191, 15, 20)]; + [_brightnessLabel setStringValue: "B"]; + [_brightnessLabel setTextColor:[CPColor whiteColor]]; + + _brightnessSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 191, aFrame.size.width - 70, 20)]; + [_brightnessSlider setMaxValue: 100.0]; + [_brightnessSlider setMinValue: 0.0]; + [_brightnessSlider setTarget: self]; + [_brightnessSlider setAction: @selector(sliderChanged:)]; + [_brightnessSlider setAutoresizingMask: CPViewWidthSizable]; + + //blue value input box + var brightnessValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 191, 45, 20)]; + [brightnessValue setAutoresizingMask: CPViewMinXMargin]; + + _brightnessValue = _redValue.cloneNode(false); + _brightnessValue.onchange = updateFunction; + + brightnessValue._DOMElement.appendChild(_brightnessValue); + [_contentView addSubview: brightnessValue]; + + _hexLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 230, 100, 20)]; + [_hexLabel setStringValue: "Hex"]; + [_hexLabel setTextColor:[CPColor whiteColor]]; + + //hex input box + _hexValue = _redValue.cloneNode(false); + _hexValue.style.top = "255px"; + _hexValue.style.width = "80px"; + _hexValue.style.left = "0px"; + _hexValue.onkeypress = function(aDOMEvent) + { + aDOMEvent = aDOMEvent || window.event; + if (aDOMEvent.keyCode == 13) + { + var newColor = [CPColor colorWithHexString: this.value]; + + if(newColor) + { + [self setColor: newColor]; + [[self colorPanel] setColor: newColor]; + } + + if(aDOMEvent.preventDefault) + aDOMEvent.preventDefault(); + else if(aDOMEvent.stopPropagation) + aDOMEvent.stopPropagation(); + + this.blur(); + } + }; + + _contentView._DOMElement.appendChild(_hexValue); + + [_contentView addSubview: _rgbLabel]; + [_contentView addSubview: _redLabel]; + [_contentView addSubview: _greenLabel]; + [_contentView addSubview: _blueLabel]; + [_contentView addSubview: _redSlider]; + [_contentView addSubview: _greenSlider]; + [_contentView addSubview: _blueSlider]; + + [_contentView addSubview: _hsbLabel]; + [_contentView addSubview: _hueLabel]; + [_contentView addSubview: _saturationLabel]; + [_contentView addSubview: _brightnessLabel]; + [_contentView addSubview: _hueSlider]; + [_contentView addSubview: _saturationSlider]; + [_contentView addSubview: _brightnessSlider]; + + [_contentView addSubview: _hexLabel]; +} + +- (CPView)provideNewView:(BOOL)initialRequest +{ + if (initialRequest) + [self initView]; + + return _contentView; +} + +- (int)currentMode +{ + return CPSliderColorPickerMode; +} + +- (BOOL)supportsMode:(int)mode +{ + return (mode == CPSliderColorPickerMode) ? YES : NO; +} + +-(void)sliderChanged:(id)sender +{ + var newColor; + + switch(sender) + { + case _hueSlider: + case _saturationSlider: + case _brightnessSlider: newColor = [CPColor colorWithHue: [_hueSlider value] + saturation: [_saturationSlider value] + brightness: [_brightnessSlider value]]; + + [self updateRGBSliders: newColor]; + break; + + case _redSlider: + case _greenSlider: + case _blueSlider: newColor = [CPColor colorWithCalibratedRed: [_redSlider value] + green: [_greenSlider value] + blue: [_blueSlider value] + alpha: 1.0]; + + [self updateHSBSliders: newColor]; + break; + } + + [self updateLabels]; + [self updateHex: newColor]; + [[self colorPanel] setColor: newColor]; +} + +-(void)setColor:(CPColor)aColor +{ + [self updateRGBSliders: aColor]; + [self updateHSBSliders: aColor]; + [self updateHex: aColor]; + [self updateLabels]; +} + +-(void)updateHSBSliders:(CPColor)aColor +{ + var hsb = [aColor hsbComponents]; + + [_hueSlider setValue: hsb[0]]; + [_saturationSlider setValue: hsb[1]]; + [_brightnessSlider setValue: hsb[2]]; +} + +- (void)updateHex:(CPColor)aColor +{ + _hexValue.value = [aColor hexString]; +} + +- (void)updateRGBSliders:(CPColor)aColor +{ + var rgb = [aColor components]; + + [_redSlider setValue: rgb[0]]; + [_greenSlider setValue: rgb[1]]; + [_blueSlider setValue: rgb[2]]; +} + +- (void)updateLabels +{ + _hueValue.value = ROUND([_hueSlider value]); + _saturationValue.value = ROUND([_saturationSlider value]); + _brightnessValue.value = ROUND([_brightnessSlider value]); + + _redValue.value = ROUND([_redSlider value] * 255); + _greenValue.value = ROUND([_greenSlider value] * 255); + _blueValue.value = ROUND([_blueSlider value] * 255); +} + + +@end diff --git a/AppKit/CPStringDrawing.j b/AppKit/CPStringDrawing.j new file mode 100644 index 0000000000..1d36440735 --- /dev/null +++ b/AppKit/CPStringDrawing.j @@ -0,0 +1,67 @@ +/* + * CPStringDrawing.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + + +var CPStringReferenceElement = nil; + +@implementation CPString (CPStringDrawing) + +- (CPString)cssString +{ + return self; +} + +- (CGSize)sizeWithFont:(CPFont)aFont +{ + if (!CPStringReferenceElement) + { + CPStringReferenceElement = document.createElement("span"); + + var style = CPStringReferenceElement.style; + + style.position = "absolute"; + style.whiteSpace = "pre"; + style.visibility = "visible"; + style.padding = "0px"; + style.margin = "0px"; + + style.left = "-100000px"; + style.top = "-100000px"; + style.zIndex = "10000"; + style.background = "red"; + + document.getElementsByTagName("body")[0].appendChild(CPStringReferenceElement); + } + + CPStringReferenceElement.style.font = [aFont ? aFont : [CPFont systemFontOfSize:12.0] cssString]; + + if (CPFeatureIsCompatible(CPJavascriptInnerTextFeature)) + CPStringReferenceElement.innerText = self; + else if (CPFeatureIsCompatible(CPJavascriptTextContentFeature)) + CPStringReferenceElement.textContent = self; + + return CGSizeMake(CPStringReferenceElement.clientWidth, CPStringReferenceElement.clientHeight); +} + +@end diff --git a/AppKit/CPTabView.j b/AppKit/CPTabView.j new file mode 100644 index 0000000000..5eb3dd1a4c --- /dev/null +++ b/AppKit/CPTabView.j @@ -0,0 +1,627 @@ +/* + * CPTabView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPImageView.j" +import "CPTabViewItem.j" +import "CPView.j" + +#include "CoreGraphics/CGGeometry.h" + + +CPTopTabsBezelBorder = 0; +//CPLeftTabsBezelBorder = 1; +//CPBottomTabsBezelBorder = 2; +//CPRightTabsBezelBorder = 3; +CPNoTabsBezelBorder = 4; +CPNoTabsLineBorder = 5; +CPNoTabsNoBorder = 6; + +var CPTabViewBezelBorderLeftImage = nil, + CPTabViewBackgroundCenterImage = nil, + CPTabViewBezelBorderRightImage = nil, + CPTabViewBezelBorderColor = nil, + CPTabViewBezelBorderBackgroundColor = nil; + +var LEFT_INSET = 7.0, + RIGHT_INSET = 7.0; + +var CPTabViewDidSelectTabViewItemSelector = 1, + CPTabViewShouldSelectTabViewItemSelector = 2, + CPTabViewWillSelectTabViewItemSelector = 4, + CPTabViewDidChangeNumberOfTabViewItemsSelector = 8; + +@implementation CPTabView : CPView +{ + CPView _labelsView; + CPView _backgroundView; + CPView _separatorView; + + CPView _auxiliaryView; + CPView _contentView; + + CPArray _tabViewItems; + CPTabViewItem _selectedTabViewItem; + + CPTabViewType _tabViewType; + + id _delegate; + unsigned _delegateSelectors; +} + ++ (void)initialize +{ + if (self != CPTabView) + return; + + var bundle = [CPBundle bundleForClass:self], + + emptyImage = [[CPImage alloc] initByReferencingFile:@"" size:CGSizeMake(7.0, 0.0)], + backgroundImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBackgroundCenter.png"] size:CGSizeMake(1.0, 1.0)], + + bezelBorderLeftImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorderLeft.png"] size:CGSizeMake(7.0, 1.0)], + bezerBorderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorder.png"] size:CGSizeMake(1.0, 1.0)], + bezelBorderRightImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorderRight.png"] size:CGSizeMake(7.0, 1.0)]; + + CPTabViewBezelBorderBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices: + [ + emptyImage, + emptyImage, + emptyImage, + + bezelBorderLeftImage, + backgroundImage, + bezelBorderRightImage, + + bezelBorderLeftImage, + bezerBorderImage, + bezelBorderRightImage + ]]]; + + CPTabViewBezelBorderColor = [CPColor colorWithPatternImage:bezerBorderImage]; +} + ++ (CPColor)bezelBorderColor +{ + return CPTabViewBezelBorderColor; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _tabViewType = CPTopTabsBezelBorder; + _tabViewItems = []; + } + + return self; +} + +- (void)viewDidMoveToWindow +{ + if (_tabViewType != CPTopTabsBezelBorder || _labelsView) + return; + + [self _createBezelBorder]; + [self layoutSubviews]; +} + +- (void)_createBezelBorder +{ + var bounds = [self bounds]; + + _labelsView = [[_CPTabLabelsView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), 0.0)]; + + [_labelsView setTabView:self]; + [_labelsView setAutoresizingMask:CPViewWidthSizable]; + + [self addSubview:_labelsView]; + + _backgroundView = [[CPView alloc] initWithFrame:CGRectMakeZero()]; + + [_backgroundView setBackgroundColor:CPTabViewBezelBorderBackgroundColor]; + + [_backgroundView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + + [self addSubview:_backgroundView]; + + _separatorView = [[CPView alloc] initWithFrame:CGRectMakeZero()]; + + [_separatorView setBackgroundColor:[[self class] bezelBorderColor]]; + [_separatorView setAutoresizingMask:CPViewWidthSizable | CPViewMaxYMargin]; + + [self addSubview:_separatorView]; +} + +- (void)layoutSubviews +{ + if (_tabViewType == CPTopTabsBezelBorder) + { + var backgroundRect = [self bounds], + labelsViewHeight = [_CPTabLabelsView height]; + + backgroundRect.origin.y += labelsViewHeight; + backgroundRect.size.height -= labelsViewHeight; + + [_backgroundView setFrame:backgroundRect]; + + var auxiliaryViewHeight = 5.0; + + if (_auxiliaryView) + { + auxiliaryViewHeight = CGRectGetHeight([_auxiliaryView frame]); + + [_auxiliaryView setFrame:CGRectMake(LEFT_INSET, labelsViewHeight, CGRectGetWidth(backgroundRect) - LEFT_INSET - RIGHT_INSET, auxiliaryViewHeight)]; + } + + [_separatorView setFrame:CGRectMake(LEFT_INSET, labelsViewHeight + auxiliaryViewHeight, CGRectGetWidth(backgroundRect) - LEFT_INSET - RIGHT_INSET, 1.0)]; + } + + // CPNoTabsNoBorder + [_contentView setFrame:[self contentRect]]; +} + +// Adding and Removing Tabs + +- (void)addTabViewItem:(CPTabViewItem)aTabViewItem +{ + [self insertTabViewItem:aTabViewItem atIndex:[_tabViewItems count]]; +} + +- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex +{ + [_tabViewItems insertObject:aTabViewItem atIndex:anIndex]; + + [_labelsView tabView:self didAddTabViewItem:aTabViewItem]; + + if ([_tabViewItems count] == 1) + [self selectFirstTabViewItem:self]; + + if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector) + [_delegate tabViewDidChangeNumberOfTabViewItems:self]; +} + +- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem +{ + [_tabViewItems removeObjectIdenticalTo:aTabViewItem]; + + [_labelsView tabView:self didRemoveTabViewItem:aTabViewItem]; + + if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector) + [_delegate tabViewDidChangeNumberOfTabViewItems:self]; +} + +// Accessing Tabs + +- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem +{ + return [_tabViewItems indexOfObjectIdenticalTo:aTabViewItem]; +} + +- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier +{ + var index = 0, + count = [_tabViewItems count]; + + for (; index < count; ++index) + if ([[_tabViewItems[index] identifier] isEqualTo:anIdentifier]) + return index; + + return index; +} + +- (unsigned)numberOfTabViewItems +{ + return [_tabViewItems count]; +} + +- (CPTabViewItem)tabViewItemAtIndex:(unsigned)anIndex +{ + return _tabViewItems[anIndex]; +} + +- (CPArray)tabViewItems +{ + return _tabViewItems; +} + +// Selecting a Tab + +- (void)selectFirstTabViewItem:(id)aSender +{ + var count = [_tabViewItems count]; + + if (count) + [self selectTabViewItemAtIndex:0]; +} + +- (void)selectLastTabViewItem:(id)aSender +{ + var count = [_tabViewItems count]; + + if (count) + [self selectTabViewItemAtIndex:count - 1]; +} + +- (void)selectNextTabViewItem:(id)aSender +{ + if (!_selectedTabViewItem) + return; + + var index = [self indexOfTabViewItem:_selectedTabViewItem], + count = [_tabViewItems count]; + + [self selectTabViewItemAtIndex:index + 1 % count]; +} + +- (void)selectPreviousTabViewItem:(id)aSender +{ + if (!_selectedTabViewItem) + return; + + var index = [self indexOfTabViewItem:_selectedTabViewItem], + count = [_tabViewItems count]; + + [self selectTabViewItemAtIndex:index == 0 ? count : index - 1]; +} + +- (void)selectTabViewItem:(CPTabViewItem)aTabViewItem +{ + if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem]) + return; + + if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector) + [_delegate tabView:self willSelectTabViewItem:aTabViewItem]; + + if (_selectedTabViewItem) + { + _selectedTabViewItem._tabState = CPBackgroundTab; + [_labelsView tabView:self didChangeStateOfTabViewItem:_selectedTabViewItem]; + + [_contentView removeFromSuperview]; + [_auxiliaryView removeFromSuperview]; + } + _selectedTabViewItem = aTabViewItem; + + _selectedTabViewItem._tabState = CPSelectedTab; + + _contentView = [_selectedTabViewItem view]; + [_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + + _auxiliaryView = [_selectedTabViewItem auxiliaryView]; + [_auxiliaryView setAutoresizingMask:CPViewWidthSizable]; + + [self addSubview:_contentView]; + + if (_auxiliaryView) + [self addSubview:_auxiliaryView]; + + [_labelsView tabView:self didChangeStateOfTabViewItem:_selectedTabViewItem]; + + [self layoutSubviews]; + + if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector) + [_delegate tabView:self didSelectTabViewItem:aTabViewItem]; +} + +- (void)selectTabViewItemAtIndex:(unsigned)anIndex +{ + [self selectTabViewItem:_tabViewItems[anIndex]]; +} + +- (void)selectedTabViewItem +{ + return _selectedTabViewItem; +} + +// + +- (void)setTabViewType:(CPTabViewType)aTabViewType +{ + if (_tabViewType == aTabViewType) + return; + + _tabViewType = aTabViewType; + + if (_tabViewType == CPNoTabsBezelBorder || _tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder) + [_labelsView removeFromSuperview]; + else if (![_labelsView superview]) + [self addSubview:_lablesView]; + + if (_tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder) + [_backgroundView removeFromSuperview]; + else if (![_backgroundView superview]) + [self addSubview:_backgroundView]; + + [self layoutSubviews]; +} + +- (CPTabViewType)tabViewType +{ + return _tabViewType; +} + +// Determining the Size + +- (CGRect)contentRect +{ + var contentRect = CGRectMakeCopy([self bounds]); + + if (_tabViewType == CPTopTabsBezelBorder) + { + var labelsViewHeight = [_CPTabLabelsView height], + auxiliaryViewHeight = _auxiliaryView ? CGRectGetHeight([_auxiliaryView frame]) : 5.0, + separatorViewHeight = 1.0; + + contentRect.origin.y += labelsViewHeight + auxiliaryViewHeight + separatorViewHeight; + contentRect.size.height -= labelsViewHeight + auxiliaryViewHeight + separatorViewHeight * 2.0; // 2 for the bottom border as well. + + contentRect.origin.x += LEFT_INSET; + contentRect.size.width -= LEFT_INSET + RIGHT_INSET; + } + + return contentRect; +} + +- (void)setDelegate:(id)aDelegate +{ + if (_delegate == aDelegate) + return; + + _delegate = aDelegate; + + _delegateSelectors = 0; + + if ([_delegate respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)]) + _delegateSelectors |= CPTabViewShouldSelectTabViewItemSelector; + + if ([_delegate respondsToSelector:@selector(tabView:willSelectTabViewItem:)]) + _delegateSelectors |= CPTabViewWillSelectTabViewItemSelector; + + if ([_delegate respondsToSelector:@selector(tabView:didSelectTabViewItem:)]) + _delegateSelectors |= CPTabViewDidSelectTabViewItemSelector; + + if ([_delegate respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)]) + _delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector; +} + +// + +- (void)mouseDown:(CPEvent)anEvent +{ + var location = [_labelsView convertPoint:[anEvent locationInWindow] fromView:nil], + tabViewItem = [_labelsView representedTabViewItemAtPoint:location]; + + if (tabViewItem) + [self selectTabViewItem:tabViewItem]; +} + +@end + +var _CPTabLabelsViewBackgroundColor = nil, + _CPTabLabelsViewInsideMargin = 10.0, + _CPTabLabelsViewOutsideMargin = 15.0; + +@implementation _CPTabLabelsView : CPView +{ + CPTabView _tabView; + CPDictionary _tabLabels; +} + ++ (void)initialize +{ + if (self != [_CPTabLabelsView class]) + return; + + var bundle = [CPBundle bundleForClass:self]; + + _CPTabLabelsViewBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewLeft.png"] size:CGSizeMake(12.0, 26.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewCenter.png"] size:CGSizeMake(1.0, 26.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewRight.png"] size:CGSizeMake(12.0, 26.0)] + ] isVertical:NO]]; +} + ++ (float)height +{ + return 26.0; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _tabLabels = []; + + [self setBackgroundColor:_CPTabLabelsViewBackgroundColor]; + + [self setFrameSize:CGSizeMake(CGRectGetWidth(aFrame), 26.0)]; + } + + return self; +} + +- (void)setTabView:(CPTabView)aTabView +{ + _tabView = aTabView; +} + +- (CPTabView)tabView +{ + return _tabView; +} + +- (void)tabView:(CPTabView)aTabView didAddTabViewItem:(CPTabViewItem)aTabViewItem +{ + var label = [[_CPTabLabel alloc] initWithFrame:CGRectMakeZero()]; + + [label setTabViewItem:aTabViewItem]; + + _tabLabels.push(label); + + [self addSubview:label]; + + [self layoutSubviews]; +} + +- (void)tabView:(CPTabView)aTabView didRemoveTabViewItem:(CPTabViewItem)aTabViewItem +{ + var index = [aTabView indexOfTabViewItem:aTabViewItem], + label = _tabLabels[index]; + + [_tabLabels removeObjectAtIndex:index]; + + [label removeFromSuperview]; + + [self layoutSubviews]; +} + + -(void)tabView:(CPTabView)aTabView didChangeStateOfTabViewItem:(CPTabViewItem)aTabViewItem + { + [_tabLabels[[aTabView indexOfTabViewItem:aTabViewItem]] setTabState:[aTabViewItem tabState]]; + } + +- (CPTabViewItem)representedTabViewItemAtPoint:(CGPoint)aPoint +{ + var index = 0, + count = _tabLabels.length; + + for (; index < count; ++index) + { + var label = _tabLabels[index]; + + if (CGRectContainsPoint([label frame], aPoint)) + return [label tabViewItem]; + } + + return nil; +} + +- (void)layoutSubviews +{ + var index = 0, + count = _tabLabels.length, + width = (_CGRectGetWidth([self bounds]) - (count - 1) * _CPTabLabelsViewInsideMargin - 2 * _CPTabLabelsViewOutsideMargin) / count, + x = _CPTabLabelsViewOutsideMargin; + + for (; index < count; ++index) + { + var label = _tabLabels[index], + frame = _CGRectMake(x, 8.0, width, 18.0); + + [label setFrame:frame]; + + x = _CGRectGetMaxX(frame) + _CPTabLabelsViewInsideMargin; + } +} + +- (void)setFrameSize:(CGSize)aSize +{ + if (CGSizeEqualToSize([self frame].size, aSize)) + return; + + [super setFrameSize:aSize]; + + [self layoutSubviews]; +} + +@end + +var _CPTabLabelBackgroundColor = nil, + _CPTabLabelSelectedBackgroundColor = nil; + +@implementation _CPTabLabel : CPView +{ + CPTabViewItem _tabViewItem; + CPTextField _labelField; +} + ++ (void)initialize +{ + if (self != [_CPTabLabel class]) + return; + + var bundle = [CPBundle bundleForClass:self]; + + _CPTabLabelBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundLeft.png"] size:CGSizeMake(6.0, 18.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundCenter.png"] size:CGSizeMake(1.0, 18.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundRight.png"] size:CGSizeMake(6.0, 18.0)] + ] isVertical:NO]]; + + _CPTabLabelSelectedBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedLeft.png"] size:CGSizeMake(3.0, 18.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedCenter.png"] size:CGSizeMake(1.0, 18.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedRight.png"] size:CGSizeMake(3.0, 18.0)] + ] isVertical:NO]]; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _labelField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; + + [_labelField setAlignment:CPCenterTextAlignment]; + [_labelField setFrame:CGRectMake(5.0, 0.0, CGRectGetWidth(aFrame) - 10.0, 20.0)]; + [_labelField setAutoresizingMask:CPViewWidthSizable]; + [_labelField setFont:[CPFont boldSystemFontOfSize:11.0]]; + + [self addSubview:_labelField]; + + [self setTabState:CPBackgroundTab]; + } + + return self; +} + +- (void)setTabState:(CPTabState)aTabState +{ + [self setBackgroundColor:aTabState == CPSelectedTab ? _CPTabLabelSelectedBackgroundColor : _CPTabLabelBackgroundColor]; +} + +- (void)setTabViewItem:(CPTabViewItem)aTabViewItem +{ + _tabViewItem = aTabViewItem; + + [self update]; +} + +- (CPTabViewItem)tabViewItem +{ + return _tabViewItem; +} + +- (void)update +{ + [_labelField setStringValue:[_tabViewItem label]]; +} + +@end diff --git a/AppKit/CPTabViewItem.j b/AppKit/CPTabViewItem.j new file mode 100644 index 0000000000..a5f7306e16 --- /dev/null +++ b/AppKit/CPTabViewItem.j @@ -0,0 +1,113 @@ +/* + * CPTabViewItem.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import + + +CPSelectedTab = 0; +CPBackgroundTab = 1; +CPPressedTab = 2; + +@implementation CPTabViewItem : CPObject +{ + id _identifier; + CPString _label; + + CPView _view; + CPView _auxiliaryView; +} + +- (id)initWithIdentifier:(id)anIdentifier +{ + self = [super init]; + + if (self) + _identifier = anIdentifier; + + return self; +} + +// Working With Labels + +- (void)setLabel:(CPString)aLabel +{ + _label = aLabel; +} + +- (CPString)label +{ + return _label; +} + +// Checking the Tab Display State + +- (CPTabState)tabState +{ + return _tabState; +} + +// Assigning an Identifier Object + +- (void)setIdentifier:(id)anIdentifier +{ + _identifier = anIdentifier; +} + +- (id)identifier +{ + return _identifier; +} + +// Assigning a View + +- (void)setView:(CPView)aView +{ + _view = aView; +} + +- (CPView)view +{ + return _view; +} + +// Assigning an Auxiliary View + +- (void)setAuxiliaryView:(CPView)anAuxiliaryView +{ + _auxiliaryView = anAuxiliaryView; +} + +- (CPView)auxiliaryView +{ + return _auxiliaryView; +} + +// Accessing the Parent Tab View + +- (CPView)tabView +{ + return _tabView; +} + +@end diff --git a/AppKit/CPTableColumn.j b/AppKit/CPTableColumn.j new file mode 100644 index 0000000000..84872362f2 --- /dev/null +++ b/AppKit/CPTableColumn.j @@ -0,0 +1,171 @@ +/* + * CPTableColumn.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + + +CPTableColumnNoResizing = 0; +CPTableColumnAutoresizingMask = 1; +CPTableColumnUserResizingMask = 2; + +@implementation CPTableColumn : CPObject +{ + CPString _identifier; + + CPTableView _tableView; + + float _width; + float _minWidth; + float _maxWidth; + + unsigned _resizingMask; +} + +- (id)initWithIdentifier:(CPString)anIdentifier +{ + self = [super init]; + + if (self) + { + _identifier = anIdentifier; + + _width = 40.0; + _minWidth = 8.0; + _maxWidth = 1000.0; + + // FIXME + _dataCell = [[CPTextField alloc] initWithFrame:CPRectMakeZero()]; + } + + return self; +} + +- (void)setIdentifier:(CPString)anIdentifier +{ + _identifier = anIdentifier; +} + +- (CPString)identifier +{ + return _identifier; +} + +// Setting the NSTableView + +- (void)setTabelView:(CPTableView)aTableView +{ + _tableView = aTableView; +} + +- (CPTableView)tableView +{ + return _tableView; +} + +// Controlling size + +- (void)setWidth:(float)aWidth +{ + _width = aWidth; +} + +- (float)width +{ + return _width; +} + +- (void)setMinWidth:(float)aWidth +{ + if (_width < (_minWidth = aWidth)) + [self setWidth:_minWidth]; +} + +- (float)minWidth +{ + return _minWidth; +} + +- (float)setMaxWidth:(float)aWidth +{ + if (_width > (_maxmimumWidth = aWidth)) + [self setWidth:_maxWidth]; +} + +- (void)setResizingMask:(unsigned)aMask +{ + _resizingMask = aMask; +} + +- (unsigned)resizingMask +{ + return _resizingMask; +} + +- (void)sizeToFit +{ + var width = CPRectGetWidth([_headerView frame]); + + if (width < _minWidth) + [self setMinWidth:width]; + else if (width > _maxWidth) + [self setMaxWidth:width] + + if (_width != width) + [self setWidth:width]; +} + +- (void)setEditable:(BOOL)aFlag +{ + _isEditable = aFlag; +} + +- (BOOL)isEditable +{ + return _isEditable; +} + +- (void)setHeaderView:(CPView)aHeaderView +{ + _headerView = aHeaderView; +} + +- (CPView)headerView +{ + return _headerView; +} + +- (void)setDataCell:(id)aDataCell +{ + _dataCell = aDataCell; +} + +- (id)dataCell +{ + return _dataCell; +} + +- (Class)dataCellForRow:(int)aRowIndex +{ + return [self dataCell]; +} + +@end diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j new file mode 100644 index 0000000000..ed5d5aee40 --- /dev/null +++ b/AppKit/CPTableView.j @@ -0,0 +1,339 @@ +/* + * CPTableView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPControl.j" +import "CPTableColumn.j" + + +var CPTableViewCellPlaceholder = nil; + +@implementation CPTableView : CPControl +{ + id _dataSource; + + CPScrollView _scrollView; + CPTableHeaderView _headerView; + + CPArray _tableColumns; + + unsigned _numberOfRows; + unsigned _numberOfColumns; + + CPArray _tableCells; + CPArray _tableColumnViews; + + CPSize _intercellSpacing; +} + ++ (void)initialize +{ + if (self != [CPTableView class]) + return; + + CPTableViewCellPlaceholder = [[CPObject alloc] init]; +} + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + /*_scrollView = [[CPView alloc] initWithFrame:CPRectMakeZero()]; + _headerView = [[CPView alloc] initWithFrame:CPRectMakeZero()]; + + [_scrollView setBackgroundColor:[CPColor redColor]]; + [_headerView setBackgroundColor:[CPColor blueColor]]; + + [self addSubview:_scrollView]; + [self addSubview:_headerView]; + + [self tile];*/ + + //[self setBackgroundColor:[CPColor redColor]]; + + _rowHeight = 17.0; + + _tableCells = [[CPArray alloc] init]; + _tableColumns = [[CPArray alloc] init]; + _tableColumnViews = [[CPArray alloc] init]; + + _intercellSpacing = CPSizeMake(3.0, 2.0); + } + + return self; +} + +- (float)columnHeight +{ + var bounds = [self bounds], + height = _numberOfRows * (_rowHeight + _intercellSpacing.height); + + return CPRectGetHeight(bounds) > height ? CPRectGetHeight(bounds) : height; +} + +- (void)loadTableCellsInRect:(CPRect)aRect +{ + if (!_dataSource) + return; + + // Use a ambitious estimate for our starting row. + var rows = CPMakeRange(MAX(Math.floor((CPRectGetMinY(aRect) + _intercellSpacing.height) / (_rowHeight + _intercellSpacing.height)), 0), 1); + + // Use a conservative estimate for the final row. + rows.length = MIN(_numberOfRows, Math.ceil(CPRectGetMaxY(aRect) / (_rowHeight + _intercellSpacing.height))) - rows.location; + + var columns = CPMakeRange(0, 1); + + // Iterate through all our columns until we find one that intersects the rect. + while (columns.location < _numberOfColumns && !CPRectIntersectsRect([_tableColumnViews[columns.location] frame], aRect)) + ++columns.location; + + // Now iterate through our columns until we find one that doesn't intersect our rect. + while (CPMaxRange(columns) < _numberOfColumns && CPRectIntersectsRect([_tableColumnViews[CPMaxRange(columns)] frame], aRect)) + ++columns.length; + + var row = rows.location, + column = 0; + + for (; row < CPMaxRange(rows); ++row) + for (column = columns.location; column < CPMaxRange(columns); ++column) + { + if (!_tableCells[column][row] || _tableCells[column][row] == CPTableViewCellPlaceholder) + { + _tableCells[column][row] = [[_tableColumns[column] dataCellForRow:row] copy]; + + [_tableCells[column][row] setFrame:CPRectMake(0.0, row * (_rowHeight + _intercellSpacing.height), [_tableColumns[column] width], _rowHeight)]; + //[_tableCells[column][row] setBackgroundColor:[CPColor blueColor]]; + [_tableColumnViews[column] addSubview:_tableCells[column][row]]; + } + + [_tableCells[column][row] setObjectValue:[_dataSource tableView:self objectValueForTableColumn:_tableColumns[column] row:row]]; + } +} + +// Setting display attributes + +- (void)setIntercellSpacing:(CPSize)aSize +{ + if (_intercellSpacing.width != aSize.width) + { + var i = 1, + delta = aSize.width - _intercellSpacing.width; + total = delta; + + for (; i < _numberOfColumns; ++i, total += delta) + { + var origin = [_tableColumnViews[i] frame].origin; + [_tableColumnViews[i] setFrameOrigin:CPPointMake(origin.x + total, origin.y)]; + } + } + + if (_intercellSpacing.height != aSize.height) + { + var i = 0; + + for (; i < _numberOfColumns; ++i, total += delta) + { + [_tableColumnViews[i] setFrameSize:CPSizeMake([_tableColumnViews[i] width], _numberOfRows * (_rowHeight + _intercellSpacing.height))]; + + var j = 1, + y = _rowHeight + _intercellSpacing.height; + + for (; j < _numberOfRows; ++i, y += _rowHeight + _intercellSpacing.height) + { + if (_tableCells[i][j] == CPTableViewCellPlaceholder) + continue; + + [_tableCells[i][j] setFrameOrigin:CPPointMake(0.0, y)]; + } + } + } + + _intercellSpacing = CPSizeCreateCopy(aSize); +} + +- (CPSize)intercellSpacing +{ + return _intercellSpacing; +} + +- (void)setRowHeight:(unsigned)aRowHeight +{ + if (_rowHeight == aRowHeight) + return; + + _rowHeight = aRowHeight; + + var row = 0, + column = 0; + + for (; row < _numberOfRows; ++row) + for (column = 0; column < _numberOfColumns; ++column) + [_tableCells[column][row] setFrameOrigin:CPPointMake(0.0, row * (_rowHeight + _intercellSpacing.height))]; +} + +- (unsigned)rowHeight +{ + return _rowHeight; +} + +- (void)addTableColumn:(CPTableColumn)aTableColumn +{ + var i = 0, + x = _numberOfColumns ? CPRectGetMaxX([self rectOfColumn:_numberOfColumns - 1]) + _intercellSpacing.width : 0.0, + tableColumnView = [[CPView alloc] initWithFrame:CPRectMake(x, 0.0, [aTableColumn width], [self columnHeight])], + tableColumnCells = [[CPArray alloc] init]; + + [_tableColumns addObject:aTableColumn]; + [_tableColumnViews addObject:tableColumnView]; + +// [tableColumnView setBackgroundColor:[CPColor greenColor]]; + + [self addSubview:tableColumnView]; + + [_tableCells addObject:tableColumnCells]; + + for (; i < _numberOfRows; ++i) + _tableCells[_numberOfColumns][i] = CPTableViewCellPlaceholder; + + ++_numberOfColumns; +} + +- (void)removeTableColumn:(CPTableColumn)aTableColumn +{ + var frame = [self frame], + width = [aTableColumn width] + _intercellSpacing.width, + index = [_tableColumns indexOfObjectIdenticalTo:aTableColumn]; + + // Remove the column view and all the cell views from the view hierarchy. + [_tableColumnViews[i] removeFromSuperview]; + + [_tableCells removeObjectAtIndex:index]; + [_tableColumns removeObjectAtIndex:index]; + [_tabelColumnViews removeObjectAtIndex:index]; + + // Shift remaining column views to the left. + for (; index < _numberOfColumns; ++ index) + [_tableColumnViews[index] setFrameOrigin:CPPointMake(CPRectGetMinX([_tableColumnViews[index] frame]) - width, 0.0)] + + // Resize ourself. + [self setFrameSize:CPSizeMake(CPRectGetWidth(frame) - width, CPRectGetHeight(frame))]; +} + +- (void)moveColumn:(unsigned)fromIndex toColumn:(unsinged)toIndex +{ + if (fromIndex == toIndex) + return; +// FIXME: IMPLEMENT +} + +- (CPArray)tableColumns +{ + return _tableColumns; +} + +// Getting the dimensions of the table + +- (int)numberOfColumns +{ + return _numberOfColumns; +} + +- (int)numberOfRows +{ + return _numberOfRows; +} + +- (void)tile +{ + var HEIGHT = 10.0; + + [_headerView setFrame:CPRectMake(0.0, 0.0, CPRectGetWidth([self bounds]), HEIGHT)]; + [_scrollView setFrame:CPRectMake(0.0, HEIGHT, CPRectGetWidth([self bounds]), CPRectGetHeight([self bounds]) - HEIGHT)]; +} + +- (void)setDataSource:(id)aDataSource +{ + _dataSource = aDataSource; + + [self reloadData]; +} + +- (id)dataSource +{ + return _dataSource; +} + +- (void)setFrameSize:(CPSize)aSize +{ + var oldColumnHeight = [self columnHeight]; + + [super setFrameSize:aSize]; + + var columnHeight = [self columnHeight]; + + if (columnHeight != oldColumnHeight) + { + var i = 0; + + for (; i < _numberOfColumns; ++i) + [_tableColumnViews[i] setFrameSize:CPSizeMake([_tableColumns[i] width], columnHeight)]; + } + + [self tile]; +} + +- (void)noteNumberOfRowsChanged +{ + var numberOfRows = [_dataSource numberOfRowsInTableView:self]; + + if (_numberOfRows != numberOfRows) + { + _numberOfRows = numberOfRows; + [self sizeToFit]; + } +} + +- (CPRect)rectOfRow:(int)aRowIndex +{ + return CPRectMake(0.0, aRowIndex * (_rowHeight + _intercellSpacing.height), CPRectGetWidth([self bounds]), _rowHeight); +} + +- (CPRect)rectOfColumn:(int)aColumnIndex +{ + return CPRectCreateCopy([_tableColumnViews[aColumnIndex] frame]); +} + +- (void)sizeToFit +{ + [self tile]; +} + +- (void)reloadData +{ + _numberOfRows = [_dataSource numberOfRowsInTableView:self]; + + [self loadTableCellsInRect:[self bounds]]; +} + +@end diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j new file mode 100644 index 0000000000..67064bb9e9 --- /dev/null +++ b/AppKit/CPTextField.j @@ -0,0 +1,419 @@ +/* + * CPTextField.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPControl.j" +import "CPStringDrawing.j" + +#include "Platform/Platform.h" +#include "Platform/DOM/CPDOMDisplayServer.h" + + +CPLineBreakByWordWrapping = 0, +CPLineBreakByCharWrapping = 1, +CPLineBreakByClipping = 2, +CPLineBreakByTruncatingHead = 3, +CPLineBreakByTruncatingTail = 4, +CPLineBreakByTruncatingMiddle = 5; + +CPTextFieldSquareBezel = 0; +CPTextFieldRoundedBezel = 1; + +var TOP_PADDING = 4.0, + BOTTOM_PADDING = 3.0; + HORIZONTAL_PADDING = 3.0; + +#if PLATFORM(DOM) +var CPTextFieldDOMInputElement = nil; +#endif + +var _CPTextFieldSquareBezelColor = nil; + +@implementation CPString (CPTextFieldAdditions) + +- (CPString)string +{ + return self; +} + +@end + +@implementation CPTextField : CPControl +{ + BOOL _isBordered; + BOOL _isBezeled; + CPTextFieldBezelStyle _bezelStyle; + + BOOL _isEditable; + BOOL _isSelectable; + + id _value; + + CPLineBreakMode _lineBreakMode; +#if PLATFORM(DOM) + DOMElement _DOMTextElement; +#endif +} + +#if PLATFORM(DOM) ++ (DOMElement)_inputElement +{ + if (!CPTextFieldDOMInputElement) + { + CPTextFieldDOMInputElement = document.createElement("input"); + CPTextFieldDOMInputElement.style.position = "absolute"; + CPTextFieldDOMInputElement.style.top = "0px"; + CPTextFieldDOMInputElement.style.left = "0px"; + CPTextFieldDOMInputElement.style.width = "100%" + CPTextFieldDOMInputElement.style.height = "100%"; + CPTextFieldDOMInputElement.style.border = "0px"; + CPTextFieldDOMInputElement.style.padding = "0px"; + CPTextFieldDOMInputElement.style.whiteSpace = "pre"; + CPTextFieldDOMInputElement.style.background = "transparent"; + CPTextFieldDOMInputElement.style.outline = "none"; + CPTextFieldDOMInputElement.style.paddingLeft = HORIZONTAL_PADDING - 1.0 + "px"; + CPTextFieldDOMInputElement.style.paddingTop = TOP_PADDING - 2.0 + "px"; + } + + return CPTextFieldDOMInputElement; +} +#endif + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _value = @""; + +#if PLATFORM(DOM) + _DOMTextElement = document.createElement("div"); + _DOMTextElement.style.position = "absolute"; + _DOMTextElement.style.top = TOP_PADDING + "px"; + _DOMTextElement.style.left = HORIZONTAL_PADDING + "px"; + _DOMTextElement.style.width = MAX(0.0, CGRectGetWidth(aFrame) - 2.0 * HORIZONTAL_PADDING) + "px"; + _DOMTextElement.style.height = MAX(0.0, CGRectGetHeight(aFrame) - TOP_PADDING - BOTTOM_PADDING) + "px"; + _DOMTextElement.style.whiteSpace = "pre"; + _DOMTextElement.style.cursor = "default"; + _DOMTextElement.style.zIndex = 100; + + _DOMElement.appendChild(_DOMTextElement); +#endif + [self setAlignment:CPLeftTextAlignment]; + } + + return self; +} + +// Setting the Bezel Style + +- (void)setBezeled:(BOOL)shouldBeBezeled +{ + if (_isBezeled == shouldBeBezeled) + return; + + _isBezeled = shouldBeBezeled; + + [self _updateBackground]; +} + +- (BOOL)isBezeled +{ + return _isBezeled; +} + +- (void)setBezelStyle:(CPTextFieldBezelStyle)aBezelStyle +{ + if (_bezelStyle == aBezelStyle) + return; + + _bezelStyle = aBezelStyle; + + [self _updateBackground]; +} + +- (CPTextFieldBezelStyle)bezelStyle +{ + return _bezelStyle; +} + +- (void)setBordered:(BOOL)shouldBeBordered +{ + if (_isBordered == shouldBeBordered) + return; + + _isBordered = shouldBeBordered; + + [self _updateBackground]; +} + +- (BOOL)isBordered +{ + return _isBordered; +} + +- (void)_updateBackground +{ + if (_isBordered && _bezelStyle == CPTextFieldSquareBezel && _isBezeled) + { + if (!_CPTextFieldSquareBezelColor) + { + var bundle = [CPBundle bundleForClass:[CPTextField class]]; + + _CPTextFieldSquareBezelColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTextField/CPTextFieldBezelSquare0.png"] size:CGSizeMake(2.0, 3.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTextField/CPTextFieldBezelSquare1.png"] size:CGSizeMake(1.0, 3.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTextField/CPTextFieldBezelSquare2.png"] size:CGSizeMake(2.0, 3.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTextField/CPTextFieldBezelSquare3.png"] size:CGSizeMake(2.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTextField/CPTextFieldBezelSquare4.png"] size:CGSizeMake(1.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTextField/CPTextFieldBezelSquare5.png"] size:CGSizeMake(2.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTextField/CPTextFieldBezelSquare6.png"] size:CGSizeMake(2.0, 2.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTextField/CPTextFieldBezelSquare7.png"] size:CGSizeMake(1.0, 2.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTextField/CPTextFieldBezelSquare8.png"] size:CGSizeMake(2.0, 2.0)] + ]]]; + } + + [self setBackgroundColor:_CPTextFieldSquareBezelColor]; + } + else + [self setBackgroundColor:nil]; +} + +- (BOOL)acceptsFirstResponder +{ + return _isEditable; +} + +- (BOOL)becomeFirstResponder +{ + var string = [self stringValue]; + + [self setStringValue:@""]; + + var element = [[self class] _inputElement]; + + element.value = string; + element.style.color = _DOMElement.style.color; + element.style.font = _DOMElement.style.font; + element.style.zIndex = 1000; + element.style.width = CGRectGetWidth([self bounds]) - 3.0 + "px"; +// element.style.left = _DOMTextElement.style.left; +// element.style.top = _DOMTextElement.style.top; + + _DOMElement.appendChild(element); + window.setTimeout(function() { element.focus(); }, 0.0); +// element.onblur = function() { objj_debug_print_backtrace(); } + //element.select(); + + element.onkeypress = function(aDOMEvent) + { + aDOMEvent = aDOMEvent || window.event; + + if (aDOMEvent.keyCode == 13) + { + if(aDOMEvent.preventDefault) + aDOMEvent.preventDefault(); + else if(aDOMEvent.stopPropagation) + aDOMEvent.stopPropagation(); + + element.blur(); + + [self sendAction:[self action] to:[self target]]; + [[self window] makeFirstResponder:nil]; + } + }; + + [[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES]; + + return YES; +} + +- (BOOL)resignFirstResponder +{ + var element = [[self class] _inputElement]; + + _DOMElement.removeChild(element); + [self setStringValue:element.value]; + + return YES; +} + +- (void)setEditable:(BOOL)shouldBeEditable +{ + _isEditable = shouldBeEditable; +} + +- (BOOL)isEditable +{ + return _isEditable; +} + +- (void)setFrameSize:(CGSize)aSize +{ + [super setFrameSize:aSize]; + + CPDOMDisplayServerSetStyleSize(_DOMTextElement, _frame.size.width - 2.0 * HORIZONTAL_PADDING, _frame.size.height - TOP_PADDING - BOTTOM_PADDING); +} + +- (BOOL)isSelectable +{ + return _isSelectable; +} + +- (void)setSelectable:(BOOL)aFlag +{ + _isSelectable = aFlag; +} + +- (void)setAlignment:(int)anAlignment +{ + if ([self alignment] == anAlignment) + return; + + [super setAlignment:anAlignment]; + +#if PLATFORM(DOM) + switch ([self alignment]) + { + case CPLeftTextAlignment: _DOMTextElement.style.textAlign = "left"; + break; + case CPRightTextAlignment: _DOMTextElement.style.textAlign = "right"; + break; + case CPCenterTextAlignment: _DOMTextElement.style.textAlign = "center"; + break; + case CPJustifiedTextAlignment: _DOMTextElement.style.textAlign = "justify"; + break; + case CPNaturalTextAlignment: _DOMTextElement.style.textAlign = ""; + break; + } +#endif +} + +- (void)setLineBreakMode:(CPLineBreakMode)aLineBreakMode +{ + _lineBreakMode = aLineBreakMode; + +#if PLATFORM(DOM) + switch (aLineBreakMode) + { + case CPLineBreakByTruncatingTail: _DOMTextElement.style.textOverflow = "ellipsis"; + _DOMTextElement.style.whiteSpace = "nowrap"; + _DOMTextElement.style.overflow = "hidden"; + break; + + case CPLineBreakByWordWrapping: _DOMTextElement.style.whiteSpace = "normal"; + _DOMTextElement.style.overflow = "hidden"; + _DOMTextElement.style.textOverflow = "clip"; + break; + } +#endif +} + +- (CPString)stringValue +{ + // All of this needs to be better. +#if PLATFORM(DOM) + if ([[self window] firstResponder] == self) + return [[self class] _inputElement].value; +#endif + + return [_value string]; +} + +- (void)setStringValue:(CPString)aStringValue +{ + _value = aStringValue; + +#if PLATFORM(DOM) + var cssString = _value ? [_value cssString] : @""; + + if (CPFeatureIsCompatible(CPJavascriptInnerTextFeature)) + _DOMTextElement.innerText = cssString; + else if (CPFeatureIsCompatible(CPJavascriptTextContentFeature)) + _DOMTextElement.textContent = cssString; +#endif +} + +- (void)sizeToFit +{ +#if PLATFORM(DOM) + var size = [_value ? _value : @" " sizeWithFont:[self font]]; + + [self setFrameSize:CGSizeMake(size.width + 2 * HORIZONTAL_PADDING, size.height + TOP_PADDING + BOTTOM_PADDING)]; +#endif +} + +@end + +var CPTextFieldIsSelectableKey = @"CPTextFieldIsSelectableKey", + CPTextFieldLineBreakModeKey = @"CPTextFieldLineBreakModeKey", + CPTextFieldStringValueKey = @"CPTextFieldStringValueKey"; + +@implementation CPTextField (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ +#if PLATFORM(DOM) + _DOMTextElement = document.createElement("div"); +#endif + + self = [super initWithCoder:aCoder]; + + if (self) + { + var bounds = [self bounds]; + + _value = @""; + +#if PLATFORM(DOM) + _DOMTextElement.style.position = "absolute"; + _DOMTextElement.style.top = TOP_PADDING + "px"; + _DOMTextElement.style.left = HORIZONTAL_PADDING + "px"; + _DOMTextElement.style.width = MAX(0.0, CGRectGetWidth(bounds) - 2.0 * HORIZONTAL_PADDING) + "px"; + _DOMTextElement.style.height = MAX(0.0, CGRectGetHeight(bounds) - TOP_PADDING - BOTTOM_PADDING) + "px"; + _DOMTextElement.style.whiteSpace = "pre"; + _DOMTextElement.style.cursor = "default"; + + _DOMElement.appendChild(_DOMTextElement); +#endif + + [self setSelectable:[aCoder decodeBoolForKey:CPTextFieldIsSelectableKey]]; + [self setLineBreakMode:[aCoder decodeIntForKey:CPTextFieldLineBreakModeKey]]; + + [self setStringValue:[aCoder decodeObjectForKey:CPTextFieldStringValueKey]]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [super encodeWithCoder:aCoder]; + + [aCoder encodeBool:_isSelectable forKey:CPTextFieldIsSelectableKey]; + [aCoder encodeInt:_lineBreakMode forKey:CPTextFieldLineBreakModeKey]; + + [aCoder encodeObject:_value forKey:CPTextFieldStringValueKey]; +} + +@end diff --git a/AppKit/CPToolbar.j b/AppKit/CPToolbar.j new file mode 100644 index 0000000000..ba07ae10bb --- /dev/null +++ b/AppKit/CPToolbar.j @@ -0,0 +1,585 @@ +/* + * CPToolbar.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import "CPPopUpButton.j" +import "CPToolbarItem.j" + +CPToolbarDisplayModeDefault = 0; +CPToolbarDisplayModeIconAndLabel = 1; +CPToolbarDisplayModeIconOnly = 2; +CPToolbarDisplayModeLabelOnly = 3; + +var CPToolbarsByIdentifier = nil; +var CPToolbarConfigurationsByIdentifier = nil; + +var _CPToolbarItemVisibilityPriorityCompare = function(lhs, rhs) +{ + var lhsVisibilityPriority = [lhs visibilityPriority], + rhsVisibilityPriority = [rhs visibilityPriority]; + + if (lhsVisibilityPriority == rhsVisibilityPriority) + return CPOrderedSame; + + if (lhsVisibilityPriority > rhsVisibilityPriority) + return CPOrderedAscending; + + return CPOrderedDescending; +} + +@implementation CPToolbar : CPObject +{ + CPString _identifier; + CPToolbarDisplayMode _displayMode; + BOOL _showsBaselineSeparator; + BOOL _allowsUserCustomization; + BOOL _isVisible; + BOOL _needsReloadOfItems; + + id _delegate; + CPView _toolbarView; + + CPArray _itemIdentifiers; + CPArray _allowedItemIdentifiers; + + CPArray _items; + CPArray _labels; + + CPMutableDictionary _itemIndexes; +} + ++ (void)initialize +{ + if (self != [CPToolbar class]) + return; + + CPToolbarsByIdentifier = [CPDictionary dictionary]; + CPToolbarConfigurationsByIdentifier = [CPDictionary dictionary]; +} + +- (id)initWithIdentifier:(CPString)anIdentifier +{ + self = [super init]; + + if (self) + { + _items = []; + _labels = []; + + _identifier = anIdentifier; + _isVisible = YES; + + var toolbarsSharingIdentifier = [CPToolbarsByIdentifier objectForKey:_identifier]; + + if (!toolbarsSharingIdentifier) + { + toolbarsSharingIdentifier = [] + [CPToolbarsByIdentifier setObject:toolbarsSharingIdentifier forKey:_identifier]; + } + + [toolbarsSharingIdentifier addObject:self]; + } + + return self; +} + +- (void)setDisplayMode:(CPToolbarDisplayMode)aDisplayMode +{ + +} + +- (CPString)identifier +{ + return _identifier; +} + +- (id)delegate +{ + return _delegate; +} + +- (BOOL)isVisible +{ + return _isVisible; +} + +- (void)setVisible:(BOOL)aFlag +{ + if (_isVisible == aFlag) + return; + + _isVisible = aFlag; + + [_window _setToolbarVisible:_isVisible]; + [self _reloadToolbarItems]; +} + +- (void)setDelegate:(id)aDelegate +{ + if (_delegate == aDelegate) + return; + + _delegate = aDelegate; + + // When _delegate is nil, this will be cleared out. + _itemIdentifiers = nil; + _allowedItemIdentifiers = [_delegate toolbarAllowedItemIdentifiers:self]; + + [self _reloadToolbarItems]; +} + +- (void)_loadConfiguration +{ + +} + +- (CPView)_toolbarView +{ + if (!_toolbarView) + { + _toolbarView = [[_CPToolbarView alloc] initWithFrame:CPRectMake(0.0, 0.0, 1200.0, 59.0)]; + [_toolbarView setAutoresizingMask:CPViewWidthSizable]; + + [_toolbarView setToolbar:self]; + } + + return _toolbarView; +} + +- (void)_reloadToolbarItems +{ + if (![_toolbarView superview] || !_delegate) + return; + + var count = [_itemIdentifiers count]; + + if (!count) + { + _itemIdentifiers = [[_delegate toolbarDefaultItemIdentifiers:self] mutableCopy]; + + count = [_itemIdentifiers count]; + } + + [[_toolbarView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; + + _items = []; + _labels = []; + + var index = 0; + + for (; index < count; ++index) + { + var identifier = _itemIdentifiers[index], + item = [CPToolbarItem _standardItemWithItemIdentifier:identifier]; + + if (!item) + item = [[_delegate toolbar:self itemForItemIdentifier:identifier willBeInsertedIntoToolbar:YES] copy]; + + if (item == nil) + [CPException raise:CPInvalidArgumentException + format:@"_delegate %@ returned nil toolbar item returned for identifier %@", _delegate, identifier]; + + [_items addObject:item]; + } + + // Store items sorted by priority. We want items to be removed first at the end of the array, + // items to be removed last at the front. + + _itemsSortedByVisibilityPriority = [_items sortedArrayUsingFunction:_CPToolbarItemVisibilityPriorityCompare context:NULL]; + + [_toolbarView reloadToolbarItems]; +} + +- (void)items +{ + return _items; +} + +- (CPArray)visibleItems +{ + return [_toolbarView visibleItems]; +} + +- (int)indexOfItem:(CPToolbarItem)anItem +{ + var info = [_itemIndexes objectForKey:[anItem hash]]; + + if (!info) + return CPNotFound; + + return info.index; +} + +- (CPArray)itemsSortedByVisibilityPriority +{ + return _itemsSortedByVisibilityPriority; +} + +@end + + +var _CPToolbarViewBackgroundColor = nil, + _CPToolbarViewExtraItemsImage = nil, + _CPToolbarViewExtraItemsAlternateImage = nil; + +var TOOLBAR_TOP_MARGIN = 5.0, + TOOLBAR_ITEM_MARGIN = 10.0, + TOOLBAR_EXTRA_ITEMS_WIDTH = 20.0; + +var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth) +{ + return { index:anIndex, view:aView, label:aLabel, minWidth:aMinWidth }; +} + +@implementation _CPToolbarView : CPView +{ + CPToolbar _toolbar; + + CPIndexSet _flexibleWidthIndexes; + CPIndexSet _visibleFlexibleWidthIndexes; + + CPMutableDictionary _itemInfos; + + CPArray _visibleItems; + CPArray _invisibleItems; + + CPPopUpButton _additionalItemsButton; +} + ++ (void)initialize +{ + if (self != [_CPToolbarView class]) + return; + + var bundle = [CPBundle bundleForClass:self]; + + _CPToolbarViewBackgroundColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPToolbarView/_CPToolbarViewBackground.png"] size:CGSizeMake(1.0, 59.0)]]; + + _CPToolbarViewExtraItemsImage = [[CPImage alloc] initWithContentsOfFile: [bundle pathForResource:"_CPToolbarView/_CPToolbarViewExtraItemsImage.png"] size: CPSizeMake(10.0, 15.0)]; + + _CPToolbarViewExtraItemsAlternateImage = [[CPImage alloc] initWithContentsOfFile: [bundle pathForResource:"_CPToolbarView/_CPToolbarViewExtraItemsAlternateImage.png"] size:CGSizeMake(10.0, 15.0)]; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _minWidth = 0; + + [self setBackgroundColor:_CPToolbarViewBackgroundColor]; + + _additionalItemsButton = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 15.0) pullsDown:YES]; + + [_additionalItemsButton setImagePosition:CPImageOnly]; + [[_additionalItemsButton menu] setShowsStateColumn:NO]; + + [_additionalItemsButton setAlternateImage:_CPToolbarViewExtraItemsAlternateImage]; + } + + return self; +} + +- (void)setToolbar:(CPToolbar)aToolbar +{ + _toolbar = aToolbar; +} + +- (CPToolbar)toolbar +{ + return _toolbar; +} + +// This *should* be roughly O(3N) = O(N) +- (void)resizeSubviewsWithOldSize:(CGSize)aSize +{ + [self layoutSubviews]; +} + +- (void)layoutSubviews +{ + // We begin by recalculating the visible items. + var items = [_toolbar items], + width = CGRectGetWidth([self bounds]), + minWidth = _minWidth, + flexibleItemIndexes = [CPIndexSet indexSet], + // FIXME: This should be a CPSet. + invisibleItemsSortedByPriority = []; + + _visibleItems = items; + + // We only have hidden items if our actual width is smaller than our + // minimum width for hiding items. + if (width < minWidth) + { + width -= TOOLBAR_EXTRA_ITEMS_WIDTH; + + _visibleItems = [_visibleItems copy]; + + var itemsSortedByVisibilityPriority = [_toolbar itemsSortedByVisibilityPriority], + count = itemsSortedByVisibilityPriority.length; + + // While our items take up too much space... + while (minWidth > width) + { + var item = itemsSortedByVisibilityPriority[count--]; + + minWidth -= [self minWidthForItem:item] + TOOLBAR_ITEM_MARGIN; + + [_visibleItems removeObjectIdenticalTo:item]; + [invisibleItemsSortedByPriority addObject:item]; + + [[self viewForItem:item] setHidden:YES]; + [[self labelForItem:item] setHidden:YES]; + } + } + + // Determine all the items that have flexible width. + var index = _visibleItems.length; + + while (index--) + { + var item = _visibleItems[index]; + + if ([item minSize].width != [item maxSize].width) + [flexibleItemIndexes addIndex:index]; + + [[self viewForItem:item] setHidden:NO]; + [[self labelForItem:item] setHidden:NO]; + } + + var remainingSpace = width - minWidth, + proportionate = 0.0; + + // Continue to dstribute space proportionately while we have it, + // and there are flexible items left that want it. (Those with max + // widths may eventually not want it anymore). + while (remainingSpace && [flexibleItemIndexes count]) + { + // Divy out the space. + proportionate += remainingSpace / [flexibleItemIndexes count]; + + // Reset the remaining space to 0 + remainingSpace = 0.0; + + var index = CPNotFound; + + while ((index = [flexibleItemIndexes indexGreaterThanIndex:index]) != CPNotFound) + { + var item = _visibleItems[index]; + view = [self viewForItem:item], + frame = [view frame], + proposedWidth = [item minSize].width + proportionate, + constrainedWidth = MIN(proposedWidth, [item maxSize].width); + + if (constrainedWidth < proposedWidth) + { + [flexibleItemIndexes removeIndex:index]; + + remainingSpace += proposedWidth - constrainedWidth; + } + + [view setFrameSize:CGSizeMake(constrainedWidth, CGRectGetHeight(frame))]; + } + } + + // Now that all the visible items are the correct width, position them accordingly. + var count = _visibleItems.length, + x = TOOLBAR_ITEM_MARGIN; + + for (index = 0; index < count; ++index) + { + var item = _visibleItems[index], + view = [self viewForItem:item], + + viewFrame = [view frame], + viewWidth = CGRectGetWidth(viewFrame), + + label = [self labelForItem:item], + labelFrame = [label frame], + labelWidth = CGRectGetWidth(labelFrame), + + itemWidth = MAX([self minWidthForItem:item], viewWidth); + + [view setFrameOrigin:CGPointMake(x + (itemWidth - viewWidth) / 2.0, TOOLBAR_TOP_MARGIN)]; + [label setFrameOrigin:CGPointMake(x + (itemWidth - labelWidth) / 2.0, TOOLBAR_TOP_MARGIN + CGRectGetHeight(viewFrame))]; + + x += itemWidth + TOOLBAR_ITEM_MARGIN; + } + + if ([invisibleItemsSortedByPriority count]) + { + var index = 0, + count = [items count]; + + _invisibleItems = []; + + for (; index < count; ++index) + { + var item = items[index]; + + if ([invisibleItemsSortedByPriority indexOfObjectIdenticalTo:item] != CPNotFound) + [_invisibleItems addObject:item]; + } + + [_additionalItemsButton setFrameOrigin:CGPointMake(width + 5.0, (CGRectGetHeight([self bounds]) - CGRectGetHeight([_additionalItemsButton frame])) / 2.0)]; + + [self addSubview:_additionalItemsButton]; + + [_additionalItemsButton removeAllItems]; + + var index = 0, + count = [_invisibleItems count]; + + [_additionalItemsButton addItemWithTitle:@"Additional Items"]; + [[_additionalItemsButton itemArray][0] setImage:_CPToolbarViewExtraItemsImage]; + + for (; index < count; ++index) + { + var item = _invisibleItems[index]; + + [_additionalItemsButton addItemWithTitle:[item label]]; + + var menuItem = [_additionalItemsButton itemArray][index + 1]; + + [menuItem setImage:[item image]]; + + [menuItem setTarget:[item target]]; + [menuItem setAction:[item action]]; + } + } + else + [_additionalItemsButton removeFromSuperview]; + +} + +- (int)indexOfItem:(CPToolbarItem)anItem +{ + var info = [_itemInfos objectForKey:[anItem hash]]; + + if (!info) + return CPNotFound; + + return info.index; +} + +- (CPView)viewForItem:(CPToolbarItem)anItem +{ + var info = [_itemInfos objectForKey:[anItem hash]]; + + if (!info) + return nil; + + return info.view; +} + +- (CPTextField)labelForItem:(CPToolbarItem)anItem +{ + var info = [_itemInfos objectForKey:[anItem hash]]; + + if (!info) + return nil; + + return info.label; +} + +- (float)minWidthForItem:(CPToolbarItem)anItem +{ + var info = [_itemInfos objectForKey:[anItem hash]]; + + if (!info) + return 0; + + return info.minWidth; +} + +- (void)reloadToolbarItems +{ + // Get rid of all our current subviews. + var subviews = [self subviews], + count = subviews.length; + + while (count--) + [subviews removeObjectAtIndex:count]; + + // Populate with new subviews. + var items = [_toolbar items], + index = 0; + + count = items.length; + + _itemInfos = [CPDictionary dictionary]; + _minWidth = TOOLBAR_ITEM_MARGIN; + + for (; index < count; ++index) + { + var item = items[index], + view = [item view]; + + // If this item doesn't have a custom view, create a standard one. + if (!view) + { + view = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 32.0, 32.0)]; + + [view setBordered:NO]; + + [view setImage:[item image]]; + [view setAlternateImage:[item alternateImage]]; + + [view setTarget:[item target]]; + [view setAction:[item action]]; + + [view setImagePosition:CPImageOnly]; + } + + [self addSubview:view]; + + // Create a lable for this item. + var label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; + + [label setStringValue:[item label]]; + [label setFont:[CPFont systemFontOfSize:11.0]]; + [label sizeToFit]; + + [label setTarget:[item target]]; + [label setAction:[item action]]; + + [self addSubview:label]; + + var minSize = [item minSize], + minWidth = MAX(minSize.width, CGRectGetWidth([label frame])); + + [_itemInfos setObject:_CPToolbarItemInfoMake(index, view, label, minWidth) forKey:[item hash]]; + + _minWidth += minWidth + TOOLBAR_ITEM_MARGIN; + + // If the minSize is different than the maxSize, then this item has flexible width. + //if (minSize.width != [item maxSize].width) + // [_flexibleWidthIndexes addIndex:index]; + } + + [self layoutSubviews]; +} + +@end diff --git a/AppKit/CPToolbarItem.j b/AppKit/CPToolbarItem.j new file mode 100644 index 0000000000..1a3dc9926a --- /dev/null +++ b/AppKit/CPToolbarItem.j @@ -0,0 +1,300 @@ +/* + * CPToolbarItem.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + +import +import + + +CPToolbarItemVisibilityPriorityStandard = 0; +CPToolbarItemVisibilityPriorityLow = -1000; +CPToolbarItemVisibilityPriorityHigh = 1000; +CPToolbarItemVisibilityPriorityUser = 2000; + +CPToolbarSeparatorItemIdentifier = @"CPToolbarSeparatorItemIdentifier"; +CPToolbarSpaceItemIdentifier = @"CPToolbarSpaceItemIdentifier"; +CPToolbarFlexibleSpaceItemIdentifier = @"CPToolbarFlexibleSpaceItemIdentifier"; +CPToolbarShowColorsItemIdentifier = @"CPToolbarShowColorsItemIdentifier"; +CPToolbarShowFontsItemIdentifier = @"CPToolbarShowFontsItemIdentifier"; +CPToolbarCustomizeToolbarItemIdentifier = @"CPToolbarCustomizeToolbarItemIdentifier"; +CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier"; + +@implementation CPToolbarItem : CPObject +{ + CPString _itemIdentifier; + + CPToolbar _toolbar; + + CPString _label; + CPString _paletteLabel; + CPString _toolTip; + int _tag; + id _target; + SEL _action; + BOOL _isEnabled; + CPImage _image; + CPImage _alternateImage; + CPView _view; + CGSize _minSize; + CGSize _maxSize; + + int _visibilityPriority; +} + +// Creating a Toolbar Item + +- (id)initWithItemIdentifier:(CPString)anItemIdentifier +{ + self = [super init]; + + if (self) + { + _itemIdentifier = anItemIdentifier; + + _minSize = CGSizeMakeZero(); + _maxSize = CGSizeMakeZero(); + + _visibilityPriority = CPToolbarItemVisibilityPriorityStandard; + } + + return self; +} + +// Managing Attributes + +- (CPString)itemIdentifier +{ + return _itemIdentifier; +} + +- (CPToolbar)toolbar +{ + return _toolbar; +} + +- (CPString)label +{ + return _label; +} + +- (void)setLabel:(CPString)aLabel +{ + _label = aLabel; +} + +- (CPString)paletteLabel +{ + return _paletteLabel; +} + +- (void)setPaletteLabel:(CPString)aPaletteLabel +{ + _paletteLabel = aPaletteLabel; +} + +- (CPString)toolTip +{ + return _toolTip; +} + +- (void)setToolTip:(CPString)aToolTip +{ + _toolTip = aToolTip; +} + +- (int)tag +{ + return _tag; +} + +- (void)setTag:(int)aTag +{ + _tag = aTag; +} + +- (id)target +{ + return _target; +} + +- (void)setTarget:(id)aTarget +{ + _target = aTarget; + + [_view setTarget:aTarget]; +} + +- (SEL)action +{ + return _action; +} + +- (void)setAction:(SEL)anAction +{ + _action = anAction; + + [_view setAction:anAction]; +} + +- (BOOL)isEnabled +{ + return _isEnabled; +} + +- (void)setEnabled:(BOOL)aFlag +{ + _isEnabled = aFlag; +} + +- (CPImage)image +{ + return _image; +} + +- (void)setImage:(CPImage)anImage +{ + _image = anImage; + + [_view setImage:anImage]; +} + +- (void)setAlternateImage:(CPImage)anImage +{ + _alternateImage = anImage; + + [_view setAlternateImage:anImage]; +} + +- (CPImage)alternateImage +{ + return _alternateImage; +} + +- (CPView)view +{ + return _view; +} + +- (void)setView:(CPView)aView +{ + _view = aView; +} + +- (CGSize)minSize +{ + return _minSize; +} + +- (void)setMinSize:(CGSize)aMinSize +{ + _minSize = CGSizeCreateCopy(aMinSize); +} + +- (CGSize)maxSize +{ + return _maxSize; +} + +- (void)setMaxSize:(CGSize)aMaxSize +{ + _maxSize = CGSizeCreateCopy(aMaxSize); +} + +// Visibility Priority + +- (int)visibilityPriority +{ + return _visibilityPriority; +} + +- (void)setVisibilityPriority:(int)aVisibilityPriority +{ + _visibilityPriority = aVisibilityPriority; +} + +@end + +@implementation CPToolbarItem (CPCopying) + +- (id)copy +{ + var copy = [[[self class] alloc] initWithItemIdentifier:_itemIdentifier]; + + [copy setLabel:_label]; + [copy setPaletteLabel:_paletteLabel]; + [copy setToolTip:_toolTip]; + + [copy setTag:_tag]; + [copy setTarget:_target]; + [copy setAction:_action]; + + [copy setEnabled:_isEnabled]; + [copy setImage:_image]; + [copy setAlternateImage:_alternateImage]; + + if (_view) + [copy setView:[CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:_view]]]; + + [copy setMinSize:_minSize]; + [copy setMaxSize:_maxSize]; + + [copy setVisibilityPriority:_visibilityPriority]; + + return copy; +} + +@end + +// Standard toolbar identifiers + +@implementation CPToolbarItem (Standard) + ++ (CPToolbarItem)_standardItemWithItemIdentifier:(CPString)anItemIdentifier +{ + var item = [[CPToolbarItem alloc] initWithItemIdentifier:anItemIdentifier]; + + switch (anItemIdentifier) + { + case CPToolbarSeparatorItemIdentifier: return nil; + + case CPToolbarSpaceItemIdentifier: [item setMinSize:CGSizeMake(32.0, 32.0)]; + [item setMaxSize:CGSizeMake(32.0, 32.0)]; + + return item; + + case CPToolbarFlexibleSpaceItemIdentifier: [item setMinSize:CGSizeMake(32.0, 32.0)]; + [item setMaxSize:CGSizeMake(10000.0, 32.0)]; + + return item; + + case CPToolbarShowColorsItemIdentifier: return nil; + case CPToolbarShowFontsItemIdentifier: return nil; + case CPToolbarCustomizeToolbarItemIdentifier: return nil; + case CPToolbarPrintItemIdentifier: return nil; + } + + return nil; +} + +@end diff --git a/AppKit/CPView.j b/AppKit/CPView.j new file mode 100644 index 0000000000..51cf412de8 --- /dev/null +++ b/AppKit/CPView.j @@ -0,0 +1,1380 @@ +/* + * CPView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + +import "CGAffineTransform.j" +import "CGGeometry.j" + +import "CPColor.j" +import "CPDOMDisplayServer.j" +import "CPGeometry.j" +import "CPResponder.j" +import "CPGraphicsContext.j" + +#include "Platform/Platform.h" +#include "CoreGraphics/CGAffineTransform.h" +#include "CoreGraphics/CGGeometry.h" +#include "Platform/DOM/CPDOMDisplayServer.h" + + +CPViewNotSizable = 0; +CPViewMinXMargin = 1; +CPViewWidthSizable = 2; +CPViewMaxXMargin = 4; +CPViewMinYMargin = 8; +CPViewHeightSizable = 16; +CPViewMaxYMargin = 32; + +CPViewBoundsDidChangeNotification = @"CPViewBoundsDidChangeNotification"; +CPViewFrameDidChangeNotification = @"CPViewFrameDidChangeNotification"; + +var _DOMOriginUpdateMask = 1 << 0, + _DOMSizeUpdateMask = 1 << 1; + +var _CPViewNotificationCenter = nil; + +#if PLATFORM(DOM) +var DOMCanvasElementZIndex = -1, + DOMBackgroundElementZIndex = -2, + DOMElementPrototype = nil, + + BackgroundTrivialColor = 0, + BackgroundVerticalThreePartImage = 1, + BackgroundHorizontalThreePartImage = 2, + BackgroundNinePartImage = 3; +#endif + +@implementation CPView : CPResponder +{ + CPWindow _window; + + CPView _superview; + CPArray _subviews; + + CPGraphicsContext _graphicsContext; + + CGRect _frame; + CGRect _bounds; + CGAffineTransform _boundsTransform; + CGAffineTransform _inverseBoundsTransform; + + CPArray _registeredDraggedTypes; + + BOOL _isHidden; + BOOL _hitTests; + + BOOL _postsFrameChangedNotifications; + BOOL _postsBoundsChangedNotifications; + BOOL _inhibitFrameAndBoundsChangedNotifications; + + CPString _displayHash; + +#if PLATFORM(DOM) + DOMElement _DOMElement; + CPArray _DOMImageParts; + CPArray _DOMImageSizes; + + unsigned _backgroundType; + + DOMElement _DOMGraphicsElement; +#endif + + float _opacity; + CPColor _backgroundColor; + + BOOL _autoresizesSubviews; + unsigned _autoresizingMask; + + CALayer _layer; + BOOL _wantsLayer; + + // Full Screen State + BOOL _isInFullScreenMode; + + _CPViewFullScreenModeState _fullScreenModeState; +} + ++ (void)initialize +{ + if (self != [CPView class]) + return; + +#if PLATFORM(DOM) + DOMElementPrototype = document.createElement("div"); + + var style = DOMElementPrototype.style; + + style.overflow = "hidden"; + style.position = "absolute"; + style.visibility = "visible"; + style.zIndex = 0; +#endif + + _CPViewNotificationCenter = [CPNotificationCenter defaultCenter]; +} + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super init]; + + if (self) + { + var width = _CGRectGetWidth(aFrame), + height = _CGRectGetHeight(aFrame); + + _subviews = []; + + _frame = _CGRectMakeCopy(aFrame); + _bounds = _CGRectMake(0.0, 0.0, width, height); + + _registeredDraggedTypes = []; + + _autoresizingMask = CPViewNotSizable; + _autoresizesSubviews = YES; + + _opacity = 1.0; + _isHidden = NO; + _hitTests = YES; + + _displayHash = [self hash]; + +#if PLATFORM(DOM) + _DOMElement = DOMElementPrototype.cloneNode(false); + + CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, _CGRectGetMinX(aFrame), _CGRectGetMinY(aFrame)); + CPDOMDisplayServerSetStyleSize(_DOMElement, width, height); + + _DOMImageParts = []; + _DOMImageSizes = []; +#endif + } + + return self; +} + +- (CPView)superview +{ + return _superview; +} + +- (CPArray)subviews +{ + return _subviews; +} + +- (CPWindow)window +{ + return _window; +} + +- (void)addSubview:(CPView)aSubview +{ + [self _insertSubview:aSubview atIndex:CPNotFound]; +} + +- (void)addSubview:(CPView)aSubview positioned:(CPWindowOrderingMode)anOrderingMode relativeTo:(CPView)anotherView +{ + var index = anotherView ? [_subviews indexOfObjectIdenticalTo:anotherView] : CPNotFound; + + // In other words, if no view, then either all the way at the bottom or all the way at the top. + if (index == CPNotFound) + index = (anOrderingMode == CPWindowAbove) ? [_subviews count] : 0; + + // else, if we have a view, above if above. + else if (anOrderingMode == CPWindowAbove) + ++index; + + [self _insertSubview:aSubview atIndex:index]; +} + +- (void)_insertSubview:(CPView)aSubview atIndex:(int)anIndex +{ + // We will have to adjust the z-index of all views starting at this index. + var count = _subviews.length; + + // If this is already one of our subviews, remove it. + if (aSubview._superview == self) + { + var index = [_subviews indexOfObjectIdenticalTo:aSubview]; + + if (index == anIndex || index == count - 1 && anIndex == count) + return; + + [_subviews removeObjectAtIndex:index]; + +#if PLATFORM(DOM) + CPDOMDisplayServerRemoveChild(_DOMElement, aSubview._DOMElement); +#endif + + if (anIndex > index) + --anIndex; + } + else + { + // Remove the view from its previous superview. + [aSubview removeFromSuperview]; + + // Set the subview's window to our own. + [aSubview _setWindow:_window]; + + // Notify the subview that it will be moving. + [aSubview viewWillMoveToSuperview:self]; + + // Set ourselves as the superview. + aSubview._superview = self; + } + + if (anIndex == CPNotFound || anIndex >= count) + { + _subviews.push(aSubview); + +#if PLATFORM(DOM) + // Attach the actual node. + CPDOMDisplayServerAppendChild(_DOMElement, aSubview._DOMElement); +#endif + } + else + { + _subviews.splice(anIndex, 0, aSubview); + +#if PLATFORM(DOM) + // Attach the actual node. + CPDOMDisplayServerInsertBefore(_DOMElement, aSubview._DOMElement, _subviews[anIndex + 1]._DOMElement); +#endif + } + + [aSubview setNextResponder:self]; + [aSubview viewDidMoveToSuperview]; + + [self didAddSubview:aSubview]; +} + +- (void)didAddSubview:(CPView)aSubview +{ +} + +- (void)removeFromSuperview +{ + if (!_superview) + return; + + [_superview willRemoveSubview:self]; + + [[_superview subviews] removeObject:self]; + +#if PLATFORM(DOM) + CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement); +#endif + _superview = nil; + + [self _setWindow:nil]; +} + +- (void)replaceSubview:(CPView)aSubview with:(CPView)aView +{ + if (aSubview._superview != self) + return; + + var index = [_subviews indexOfObjectIdenticalTo:aSubview]; + + [aSubview removeFromSuperview]; + + [aView _insertSubview:aView atIndex:index]; +} + +- (void)_setWindow:(CPWindow)aWindow +{ + // FIXME: check _window == aWindow? If not, comment why! + if ([_window firstResponder] == self) + [_window makeFirstResponder:nil]; + + // Notify the view and its subviews + [self viewWillMoveToWindow:aWindow]; + [_subviews makeObjectsPerformSelector:@selector(_setWindow:) withObject:aWindow]; + + _window = aWindow; + + [self viewDidMoveToWindow]; +} + +- (BOOL)isDescendantOf:(CPView)aView +{ + var view = self; + + do + { + if (view == aView) + return YES; + } while(view = [view superview]) + + return NO; +} + +- (void)viewDidMoveToSuperview +{ + if (_graphicsContext) + [self setNeedsDisplay:YES]; +} + +- (void)viewDidMoveToWindow +{ +} + +- (void)viewWillMoveToSuperview:(CPView)aView +{ +} + +- (void)viewWillMoveToWindow:(CPWindow)aWindow +{ +} + +- (void)willRemoveSubview:(CPView)aView +{ +} + +- (CPMenuItem)enclosingMenuItem +{ + var view = self; + + while (![view isKindOfClass:[_CPMenuItemView class]]) + view = [view superview]; + + if (view) + return view._menuItem; + + return nil; +/* var view = self, + enclosingMenuItem = _enclosingMenuItem; + + while (!enclosingMenuItem && (view = view._enclosingMenuItem)) + view = [view superview]; + + return enclosingMenuItem;*/ +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (void)setFrame:(CPRect)aFrame +{ + if (_CGRectEqualToRect(_frame, aFrame)) + return; + + _inhibitFrameAndBoundsChangedNotifications = YES; + + [self setFrameOrigin:aFrame.origin]; + [self setFrameSize:aFrame.size]; + + _inhibitFrameAndBoundsChangedNotifications = NO; + + if (_postsFrameChangedNotifications) + [_CPViewNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self]; +} + +- (CGRect)frame +{ + return _CGRectMakeCopy(_frame); +} + +- (void)setFrameOrigin:(CPPoint)aPoint +{ + var origin = _frame.origin; + + if (!aPoint || _CGPointEqualToPoint(origin, aPoint)) + return; + + origin.x = aPoint.x; + origin.y = aPoint.y; + + if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications) + [_CPViewNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self]; + +#if PLATFORM(DOM) + CPDOMDisplayServerSetStyleLeftTop(_DOMElement, _superview ? _superview._boundsTransform : NULL, origin.x, origin.y); +#endif +} + +- (void)setFrameSize:(CGSize)aSize +{ + var size = _frame.size; + + if (!aSize || _CGSizeEqualToSize(size, aSize)) + return; + + var oldSize = _CGSizeMakeCopy(size); + + size.width = aSize.width; + size.height = aSize.height; + + if (YES) + { + _bounds.size.width = aSize.width; + _bounds.size.height = aSize.height; + } + + if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications) + [_CPViewNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self]; + + if (_layer) + [_layer _owningViewBoundsChanged]; + + if (_autoresizesSubviews) + [self resizeSubviewsWithOldSize:oldSize]; + +#if PLATFORM(DOM) + CPDOMDisplayServerSetStyleSize(_DOMElement, size.width, size.height); + + if (_backgroundType == BackgroundTrivialColor) + return; + + var images = [[_backgroundColor patternImage] imageSlices]; + + if (_backgroundType == BackgroundVerticalThreePartImage) + { + CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], size.width, size.height - _DOMImageSizes[0].height - _DOMImageSizes[2].height); + } + + else if (_backgroundType == BackgroundHorizontalThreePartImage) + { + CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], size.width - _DOMImageSizes[0].width - _DOMImageSizes[2].width, size.height); + } + + else if (_backgroundType == BackgroundNinePartImage) + { + var width = size.width - _DOMImageSizes[0].width - _DOMImageSizes[2].width, + height = size.height - _DOMImageSizes[0].height - _DOMImageSizes[6].height; + + CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], width, _DOMImageSizes[0].height); + CPDOMDisplayServerSetStyleSize(_DOMImageParts[3], _DOMImageSizes[3].width, height); + CPDOMDisplayServerSetStyleSize(_DOMImageParts[4], width, height); + CPDOMDisplayServerSetStyleSize(_DOMImageParts[5], _DOMImageSizes[5].width, height); + CPDOMDisplayServerSetStyleSize(_DOMImageParts[7], width, _DOMImageSizes[7].height); + } +#endif +} + +- (void)setBounds:(CGRect)bounds +{ + if (_CGRectEqualToRect(_bounds, bounds)) + return; + + _inhibitFrameAndBoundsChangedNotifications = YES; + + [self setBoundsOrigin:bounds.origin]; + [self setBoundsSize:bounds.size]; + + _inhibitFrameAndBoundsChangedNotifications = NO; + + if (_postsBoundsChangedNotifications) + [_CPViewNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self]; +} + +- (CGRect)bounds +{ + return _CGRectMakeCopy(_bounds); +} + +- (void)setBoundsOrigin:(CGPoint)aPoint +{ + var origin = _bounds.origin; + + if (_CGPointEqualToPoint(origin, aPoint)) + return; + + origin.x = aPoint.x; + origin.y = aPoint.y; + + if (origin.x != 0 || origin.y != 0) + { + _boundsTransform = _CGAffineTransformMakeTranslation(-origin.x, -origin.y); + _inverseBoundsTransform = CGAffineTransformInvert(_boundsTransform); + } + else + { + _boundsTransform = nil; + _inverseBoundsTransform = nil; + } + +#if PLATFORM(DOM) + var index = _subviews.length; + + while (index--) + { + var view = _subviews[index], + origin = view._frame.origin; + + CPDOMDisplayServerSetStyleLeftTop(view._DOMElement, _boundsTransform, origin.x, origin.y); + } +#endif + + if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications) + [_CPViewNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self]; +} + +- (void)setBoundsSize:(CGSize)aSize +{ + var size = _bounds.size; + + if (_CGSizeEqualToSize(size, aSize)) + return; + + var frameSize = _frame.size; + + if (!_CGSizeEqualToSize(size, frameSize)) + { + var origin = _bounds.origin; + + origin.x /= size.width / frameSize.width; + origin.y /= size.height / frameSize.height; + } + + size.width = aSize.width; + size.height = aSize.height; + + if (!_CGSizeEqualToSize(size, frameSize)) + { + var origin = _bounds.origin; + + origin.x *= size.width / frameSize.width; + origin.y *= size.height / frameSize.height; + } + + if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications) + [_CPViewNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self]; +} + +- (void)resizeWithOldSuperviewSize:(CPSize)aSize +{ + var mask = _autoresizingMask; + + if(mask == CPViewNotSizable) + return; + + var frame = _superview._frame, + newFrame = _CGRectMakeCopy(_frame), + dX = (_CGRectGetWidth(frame) - aSize.width) / + (((mask & CPViewMinXMargin) ? 1 : 0) + (mask & CPViewWidthSizable ? 1 : 0) + (mask & CPViewMaxXMargin ? 1 : 0)), + dY = (_CGRectGetHeight(frame) - aSize.height) / + ((mask & CPViewMinYMargin ? 1 : 0) + (mask & CPViewHeightSizable ? 1 : 0) + (mask & CPViewMaxYMargin ? 1 : 0)); + + if (mask & CPViewMinXMargin) + newFrame.origin.x += dX; + if (mask & CPViewWidthSizable) + newFrame.size.width += dX; + + if (mask & CPViewMinYMargin) + newFrame.origin.y += dY; + if (mask & CPViewHeightSizable) + newFrame.size.height += dY; + + [self setFrame:newFrame]; +} + +- (void)resizeSubviewsWithOldSize:(CPSize)aSize +{ + var count = _subviews.length; + + while (count--) + [_subviews[count] resizeWithOldSuperviewSize:aSize]; +} + +- (void)setAutoresizesSubviews:(BOOL)aFlag +{ + _autoresizesSubviews = aFlag; +} + +- (BOOL)autoresizesSubviews +{ + return _autoresizesSubviews; +} + +- (void)setAutoresizingMask:(unsigned)aMask +{ + _autoresizingMask = aMask; +} + +- (unsigned)autoresizingMask +{ + return _autoresizingMask; +} + +// Fullscreen Mode + +- (BOOL)enterFullScreenMode:(CPScreen)aScreen withOptions:(CPDictionary)options +{ + _fullScreenModeState = _CPViewFullScreenModeStateMake(self); + + var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds] styleMask:CPBorderlessWindowMask]; + + [fullScreenWindow setLevel:CPScreenSaverWindowLevel]; + [fullScreenWindow setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + + var contentView = [fullScreenWindow contentView]; + + [contentView setBackgroundColor:[CPColor blackColor]]; + [contentView addSubview:self]; + + [self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + [self setFrame:CGRectMakeCopy([contentView bounds])]; + + [fullScreenWindow makeKeyAndOrderFront:self]; + + [fullScreenWindow makeFirstResponder:self]; + + _isInFullScreenMode = YES; + + return YES; +} + +- (void)exitFullScreenModeWithOptions:(CPDictionary)options +{ + if (!_isInFullScreenMode) + return; + + _isInFullScreenMode = NO; + + [self setFrame:_fullScreenModeState.frame]; + [self setAutoresizingMask:_fullScreenModeState.autoresizingMask]; + [_fullScreenModeState.superview _insertSubview:self atIndex:_fullScreenModeState.index]; + + [[self window] orderOut:self]; +} + +- (BOOL)isInFullScreenMode +{ + return _isInFullScreenMode; +} + +- (void)setHidden:(BOOL)aFlag +{ + if(_isHidden == aFlag) + return; +// FIXME: Should we return to visibility? This breaks in FireFox, Opera, and IE. +// _DOMElement.style.visibility = (_isHidden = aFlag) ? "hidden" : "visible"; + _isHidden = aFlag; +#if PLATFORM(DOM) + _DOMElement.style.display = _isHidden ? "none" : "block"; +#endif +} + +- (BOOL)isHidden +{ + return _isHidden; +} + +- (void)setAlphaValue:(float)anAlphaValue +{ + if (_opacity == anAlphaValue) + return; + + _opacity = anAlphaValue; + +#if PLATFORM(DOM) + _DOMElement.style.opacity = anAlphaValue; + + if (anAlphaValue == 1.0) + try { _DOMElement.style.removeAttribute("filter") } catch (anException) { } + else + _DOMElement.style.filter = "alpha(opacity=" + anAlphaValue * 100 + ")"; +#endif +} + +- (float)alphaValue +{ + return _opacity; +} + +- (void)setOpacity:(float)anOpacity +{ + [self setAlphaValue:anOpacity]; +} + +- (float)opacity +{ + return _opacity; +} + +- (BOOL)isHiddenOrHasHiddenAncestor +{ + var view = self; + + while (![view isHidden]) + view = [view superview]; + + return view != nil; +} + +- (BOOL)acceptsFirstMouse:(CPEvent)anEvent +{ + return YES; +} + +- (BOOL)hitTests +{ + return YES; +} + +- (void)setHitTests:(BOOL)shouldHitTest +{ + _hitTests = shouldHitTest; +} + +- (CPView)hitTest:(CPPoint)aPoint +{ + if(_isHidden || !_hitTests || !CPRectContainsPoint(_frame, aPoint)) + return nil; + + var view = nil, + i = _subviews.length, + adjustedPoint = _CGPointMake(aPoint.x - _CGRectGetMinX(_frame), aPoint.y - _CGRectGetMinY(_frame)); + + if (_inverseBoundsTransform) + adjustedPoint = _CGPointApplyAffineTransform(adjustedPoint, _inverseBoundsTransform); + + while (i--) + if (view = [_subviews[i] hitTest:adjustedPoint]) + return view; + + return self; +} + +- (BOOL)mouseDownCanMoveWindow +{ + return ![self isOpaque]; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + if ([self mouseDownCanMoveWindow]) + [super mouseDown:anEvent]; +} + +- (void)setBackgroundColor:(CPColor)aColor +{ + if (_backgroundColor == aColor) + return; + + _backgroundColor = aColor; + +#if PLATFORM(DOM) + var patternImage = [_backgroundColor patternImage], + amount = 0; + + if ([patternImage isThreePartImage]) + { + _backgroundType = [patternImage isVertical] ? BackgroundVerticalThreePartImage : BackgroundHorizontalThreePartImage; + + amount = 3 - _DOMImageParts.length; + } + else if ([patternImage isNinePartImage]) + { + _backgroundType = BackgroundNinePartImage; + + amount = 9 - _DOMImageParts.length; + } + else + { + _backgroundType = BackgroundTrivialColor; + + amount = 0 - _DOMImageParts.length; + } + + if (amount > 0) + while (amount--) + { + var DOMElement = DOMElementPrototype.cloneNode(false); + + DOMElement.style.zIndex = -1000; + + _DOMImageParts.push(DOMElement); + _DOMElement.appendChild(DOMElement); + } + else + { + amount = -amount; + + while (amount--) + _DOMElement.removeChild(_DOMImageParts.pop()); + } + + if (_backgroundType == BackgroundTrivialColor) + + // Opera doesn't like DOM properties set to nil. + // https://trac.280north.com/ticket/7 + _DOMElement.style.background = _backgroundColor ? [_backgroundColor cssString] : ""; + + else + { + var slices = [patternImage imageSlices], + count = slices.length, + frameSize = _frame.size; + + while (count--) + { + var image = slices[count], + size = _DOMImageSizes[count] = image ? [image size] : _CGSizeMakeZero(); + + CPDOMDisplayServerSetStyleSize(_DOMImageParts[count], size.width, size.height); + + _DOMImageParts[count].style.background = image ? "url(\"" + [image filename] + "\")" : ""; + } + + if (_backgroundType == BackgroundNinePartImage) + { + var width = frameSize.width - _DOMImageSizes[0].width - _DOMImageSizes[2].width, + height = frameSize.height - _DOMImageSizes[0].height - _DOMImageSizes[6].height; + + CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], width, _DOMImageSizes[0].height); + CPDOMDisplayServerSetStyleSize(_DOMImageParts[3], _DOMImageSizes[3].width, height); + CPDOMDisplayServerSetStyleSize(_DOMImageParts[4], width, height); + CPDOMDisplayServerSetStyleSize(_DOMImageParts[5], _DOMImageSizes[5].width, height); + CPDOMDisplayServerSetStyleSize(_DOMImageParts[7], width, _DOMImageSizes[7].height); + + CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[0], NULL, 0.0, 0.0); + CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[1], NULL, _DOMImageSizes[0].width, 0.0); + CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[2], NULL, 0.0, 0.0); + CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[3], NULL, 0.0, _DOMImageSizes[1].height); + CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[4], NULL, _DOMImageSizes[0].width, _DOMImageSizes[0].height); + CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[5], NULL, 0.0, _DOMImageSizes[1].height); + CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[6], NULL, 0.0, 0.0); + CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[7], NULL, _DOMImageSizes[6].width, 0.0); + CPDOMDisplayServerSetStyleRightBottom(_DOMImageParts[8], NULL, 0.0, 0.0); + } + else if (_backgroundType == BackgroundVerticalThreePartImage) + { + CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], frameSize.width, frameSize.height - _DOMImageSizes[0].height - _DOMImageSizes[2].height); + + CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[0], NULL, 0.0, 0.0); + CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[1], NULL, 0.0, _DOMImageSizes[0].height); + CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[2], NULL, 0.0, 0.0); + } + else if (_backgroundType == BackgroundHorizontalThreePartImage) + { + CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], frameSize.width - _DOMImageSizes[0].width - _DOMImageSizes[2].width, frameSize.height); + + CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[0], NULL, 0.0, 0.0); + CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[1], NULL, _DOMImageSizes[0].width, 0.0); + CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[2], NULL, 0.0, 0.0); + } + } +#endif +} + +- (CPColor)backgroundColor +{ + return _backgroundColor; +} + +// Converting Coordinates + +- (CGPoint)convertPoint:(CGPoint)aPoint fromView:(CPView)aView +{ + return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(aView, self)); +} + +- (CGPoint)convertPoint:(CGPoint)aPoint toView:(CPView)aView +{ + return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(self, aView)); +} + +- (CGSize)convertSize:(CGSize)aSize fromView:(CPView)aView +{ + return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(aView, self)); +} + +- (CGSize)convertSize:(CGSize)aSize toView:(CPView)aView +{ + return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(self, aView)); +} + +- (CGRect)convertRect:(CGRect)aRect fromView:(CPView)aView +{ + return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(aView, self)); +} + +- (CGRect)convertRect:(CGRect)aRect toView:(CPView)aView +{ + return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(self, aView)); +} + +- (void)setPostsFrameChangedNotifications:(BOOL)shouldPostFrameChangedNotifications +{ + if (_postsFrameChangedNotifications == shouldPostFrameChangedNotifications) + return; + + _postsFrameChangedNotifications = shouldPostFrameChangedNotifications; + + if (_postsFrameChangedNotifications) + [_CPViewNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self]; +} + +- (BOOL)postsFrameChangedNotifications +{ + return _postsFrameChangedNotifications; +} + +- (void)setPostsBoundsChangedNotifications:(BOOL)shouldPostBoundsChangedNotifications +{ + if (_postsBoundsChangedNotifications == shouldPostBoundsChangedNotifications) + return; + + _postsBoundsChangedNotifications = shouldPostBoundsChangedNotifications; + + if (_postsBoundsChangedNotifications) + [_CPViewNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self]; +} + +- (BOOL)postsBoundsChangedNotifications +{ + return _postsBoundsChangedNotifications; +} + +- (void)dragImage:(CPImage)anImage at:(CPPoint)aLocation offset:(CPSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack +{ + [_window dragImage:anImage at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack]; +} + +- (void)dragView:(CPView)aView at:(CPPoint)aLocation offset:(CPSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack +{ + [_window dragView:aView at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack]; +} + +- (void)registerForDraggedTypes:(CPArray)pasteboardTypes +{ + _registeredDraggedTypes = [pasteboardTypes copy]; +} + +- (CPArray)registeredDraggedTypes +{ + return _registeredDraggedTypes; +} + +- (void)unregisterDraggedTypes +{ + _registeredDraggedTypes = nil; +} + +// + +- (void)drawRect:(CPRect)aRect +{ + +} + +// Focus + +- (void)lockFocus +{ + // If we don't yet have a graphics context, then we must first create a + // canvas element, then use its 2d context. + if (!_graphicsContext) + { + var context = CGBitmapGraphicsContextCreate(); + +#if PLATFORM(DOM) + _DOMGraphicsElement = context.DOMElement; + + _DOMGraphicsElement.style.position = "absolute"; + _DOMGraphicsElement.style.top = "0px"; + _DOMGraphicsElement.style.left = "0px"; + _DOMGraphicsElement.style.zIndex = DOMCanvasElementZIndex; + + _DOMGraphicsElement.width = CPRectGetWidth(_frame); + _DOMGraphicsElement.height = CPRectGetHeight(_frame); + + _DOMGraphicsElement.style.width = CPRectGetWidth(_frame) + "px"; + _DOMGraphicsElement.style.height = CPRectGetHeight(_frame) + "px"; + + _DOMElement.appendChild(_DOMGraphicsElement); +#endif + _graphicsContext = [CPGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES]; + } + + [CPGraphicsContext setCurrentContext:_graphicsContext]; + + CGContextSaveGState([_graphicsContext graphicsPort]); +} + +- (void)unlockFocus +{ + var graphicsPort = [_graphicsContext graphicsPort]; + + CGContextRestoreGState(graphicsPort); + + [CPGraphicsContext setCurrentContext:nil]; +} + +// Displaying + +- (void)setNeedsDisplay:(BOOL)aFlag +{ + if (aFlag) + [self display]; +} + +- (void)setNeedsDisplayInRect:(CPRect)aRect +{ + [self displayRect:aRect]; +} + +- (void)displayIfNeeded +{ +} + +- (void)display +{ + [self displayRect:_bounds]; +} + +- (void)displayRect:(CPRect)aRect +{ + [self lockFocus]; + [self drawRect:aRect]; + [self unlockFocus]; +} + +- (BOOL)isOpaque +{ + return NO; +} + +- (CGRect)visibleRect +{ + if (!_superview) + return _bounds; + + return CGRectIntersection([self convertRect:[_superview visibleRect] fromView:_superview], _bounds); +} + +// Scrolling + +- (CPScrollView)_enclosingClipView +{ + var superview = _superview, + clipViewClass = [CPClipView class]; + + while(superview && ![superview isKindOfClass:clipViewClass]) + superview = superview._superview; + + return superview; +} + +- (void)scrollPoint:(CGPoint)aPoint +{ + var clipView = [self _enclosingClipView]; + + if (!clipView) + return; + + [clipView scrollToPoint:[self convertPoint:aPoint toView:clipView]]; +} + +- (BOOL)scrollRectToVisible:(CGRect)aRect +{ + var visibleRect = [self visibleRect]; + + // Make sure we have a rect that exists. + aRect = CGRectIntersection(aRect, _bounds); + + // If aRect is empty or is already visible then no scrolling required. + if (_CGRectIsEmpty(aRect) || CGRectContainsRect(visibleRect, aRect)) + return NO; + + var enclosingClipView = [self _enclosingClipView]; + + // If we're not in a clip view, then there isn't much we can do. + if (!enclosingClipView) + return NO; + + var scrollPoint = _CGPointMakeCopy(visibleRect.origin); + + // One of the following has to be true since our current visible rect didn't contain aRect. + if (_CGRectGetMinX(aRect) <= _CGRectGetMinX(visibleRect)) + scrollPoint.x = _CGRectGetMinX(aRect); + else if (_CGRectGetMaxX(aRect) > _CGRectGetMaxX(visibleRect)) + scrollPoint.x += _CGRectGetMaxX(aRect) - _CGRectGetMaxX(visibleRect); + + if (_CGRectGetMinY(aRect) <= _CGRectGetMinY(visibleRect)) + scrollPoint.y = CGRectGetMinY(aRect); + else if (_CGRectGetMaxY(aRect) > _CGRectGetMaxY(visibleRect)) + scrollPoint.y += _CGRectGetMaxY(aRect) - _CGRectGetMaxY(visibleRect); + + [enclosingClipView scrollToPoint:CGPointMake(scrollPoint.x, scrollPoint.y)]; + + return YES; +} + +- (BOOL)autoscroll:(CPEvent)anEvent +{ + // FIXME: Implement. + return NO; +} + +- (CGRect)adjustScroll:(CGRect)proposedVisibleRect +{ + return proposedVisibleRect; +} + +- (void)scrollRect:(CGRect)aRect by:(float)anAmount +{ + +} + +- (CPScrollView)enclosingScrollView +{ + var superview = _superview, + scrollViewClass = [CPScrollView class]; + + while(superview && ![superview isKindOfClass:scrollViewClass]) + superview = superview._superview; + + return superview; +} + +- (void)scrollClipView:(CPClipView)aClipView toPoint:(CGPoint)aPoint +{ + [aClipView scrollToPoint:aPoint]; +} + +- (void)reflectScrolledClipView:(CPClipView)aClipView +{ +} + +@end + +@implementation CPView (CoreAnimationAdditions) + +- (void)setLayer:(CALayer)aLayer +{ + if (_layer == aLayer) + return; + + if (_layer) + { + _layer._owningView = nil; +#if PLATFORM(DOM) + _DOMElement.removeChild(_layer._DOMElement); +#endif + } + + _layer = aLayer; + + if (_layer) + { + var bounds = CGRectMakeCopy([self bounds]); + + [_layer _setOwningView:self]; + +#if PLATFORM(DOM) + _layer._DOMElement.style.zIndex = 100; + + _DOMElement.appendChild(_layer._DOMElement); +#endif + } +} + +- (CALayer)layer +{ + return _layer; +} + +- (void)setWantsLayer:(BOOL)aFlag +{ + _wantsLayer = aFlag; +} + +- (CALayer)wantsLayer +{ + return _wantsLayer; +} + +@end + +var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", + CPViewAutoresizesSubviewsKey = @"CPViewAutoresizesSubviews", + CPViewBackgroundColorKey = @"CPViewBackgroundColor", + CPViewBoundsKey = @"CPViewBoundsKey", + CPViewFrameKey = @"CPViewFrameKey", + CPViewHitTestsKey = @"CPViewHitTestsKey", + CPViewIsHiddenKey = @"CPViewIsHiddenKey", + CPViewOpacityKey = @"CPViewOpacityKey", + CPViewSubviewsKey = @"CPViewSubviewsKey", + CPViewSuperviewKey = @"CPViewSuperviewKey", + CPViewWindowKey = @"CPViewWindowKey"; + +@implementation CPView (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + // We create the DOMElement "early" because there is a change that we + // will decode our superview before we are done decoding, at which point + // we have to have an element to place in the tree. Perhaps there is + // a more "elegant" way to do this...? +#if PLATFORM(DOM) + _DOMElement = DOMElementPrototype.cloneNode(false); +#endif + + self = [super initWithCoder:aCoder]; + + if (self) + { + _frame = [aCoder decodeRectForKey:CPViewFrameKey]; + _bounds = [aCoder decodeRectForKey:CPViewBoundsKey]; + + _window = [aCoder decodeObjectForKey:CPViewWindowKey]; + _subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey]; + _superview = [aCoder decodeObjectForKey:CPViewSuperviewKey]; + + _autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey]; + _autoresizesSubviews = [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey]; + + _hitTests = [aCoder decodeObjectForKey:CPViewHitTestsKey]; + _isHidden = [aCoder decodeObjectForKey:CPViewIsHiddenKey]; + _opacity = [aCoder decodeIntForKey:CPViewOpacityKey]; + + // DOM SETUP +#if PLATFORM(DOM) + _DOMImageParts = []; + _DOMImageSizes = []; + + CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, _CGRectGetMinX(_frame), _CGRectGetMinY(_frame)); + CPDOMDisplayServerSetStyleSize(_DOMElement, _CGRectGetWidth(_frame), _CGRectGetHeight(_frame)); + + var index = 0, + count = _subviews.length; + + for (; index < count; ++index) + { + CPDOMDisplayServerAppendChild(_DOMElement, _subviews[index]._DOMElement); + } +#endif + _displayHash = [self hash]; + + [self setBackgroundColor:[aCoder decodeObjectForKey:CPViewBackgroundColorKey]]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [super encodeWithCoder:aCoder]; + + [aCoder encodeRect:_frame forKey:CPViewFrameKey]; + [aCoder encodeRect:_bounds forKey:CPViewBoundsKey]; + + [aCoder encodeConditionalObject:_window forKey:CPViewWindowKey]; + [aCoder encodeObject:_subviews forKey:CPViewSubviewsKey]; + [aCoder encodeConditionalObject:_superview forKey:CPViewSuperviewKey]; + + [aCoder encodeInt:_autoresizingMask forKey:CPViewAutoresizingMaskKey]; + [aCoder encodeBool:_autoresizesSubviews forKey:CPViewAutoresizesSubviewsKey]; + + [aCoder encodeObject:_backgroundColor forKey:CPViewBackgroundColorKey]; + + [aCoder encodeBool:_hitTests forKey:CPViewHitTestsKey]; + [aCoder encodeBool:_isHidden forKey:CPViewIsHiddenKey]; + [aCoder encodeFloat:_opacity forKey:CPViewOpacityKey]; +} + +@end + +var _CPViewFullScreenModeStateMake = function(aView) +{ + var superview = aView._superview; + + return { autoresizingMask:aView._autoresizingMask, frame:CGRectMakeCopy(aView._frame), index:(superview ? [superview._subviews indexOfObjectIdenticalTo:aView] : 0), superview:superview }; +} + +var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) +{ + var transform = CGAffineTransformMakeIdentity(); + + if (fromView) + { + var view = fromView; + + // If we have a fromView, "climb up" the view tree until + // we hit the root node or we hit the toLayer. + while (view && view != toView) + { + var frame = view._frame; + + transform.tx += _CGRectGetMinX(frame); + transform.ty += _CGRectGetMinY(frame); + + if (view._boundsTransform) + { + _CGAffineTransformConcatTo(transform, view._boundsTransform, transform); + } + + view = view._superview; + } + + // If we hit toView, then we're done. + if (view == toView) + return transform; + } + + // FIXME: For now we can do things this way, but eventually we need to do them the "hard" way. + var view = toView; + + while (view) + { + var frame = view._frame; + + transform.tx -= _CGRectGetMinX(frame); + transform.ty -= _CGRectGetMinY(frame); + + if (view._boundsTransform) + { + _CGAffineTransformConcatTo(transform, view._inverseBoundsTransform, transform); + } + + view = view._superview; + } + +/* var views = [], + view = toView; + + while (view) + { + views.push(view); + view = view._superview; + } + + var index = views.length; + + while (index--) + { + var frame = views[index]._frame; + + transform.tx -= _CGRectGetMinX(frame); + transform.ty -= _CGRectGetMinY(frame); + }*/ + + return transform; +} diff --git a/AppKit/CPWindow.j b/AppKit/CPWindow.j new file mode 100644 index 0000000000..e639b12f42 --- /dev/null +++ b/AppKit/CPWindow.j @@ -0,0 +1,1259 @@ +/* + * CPWindow.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + +import "CGGeometry.j" +import "CPAnimation.j" +import "CPResponder.j" + +#include "Platform/Platform.h" +#include "Platform/DOM/CPDOMDisplayServer.h" + +#include "CoreGraphics/CGGeometry.h" + + +CPBorderlessWindowMask = 0; +CPTitledWindowMask = 1 << 0; +CPClosableWindowMask = 1 << 1; +CPMiniaturizableWindowMask = 1 << 2; +CPResizableWindowMask = 1 << 3; +CPTexturedBackgroundWindowMask = 1 << 8; + +CPBorderlessBridgeWindowMask = 1 << 20; +CPHUDBackgroundWindowMask = 1 << 21; + +CPWindowNotSizable = 0; +CPWindowMinXMargin = 1; +CPWindowWidthSizable = 2; +CPWindowMaxXMargin = 4; +CPWindowMinYMargin = 8; +CPWindowHeightSizable = 16; +CPWindowMaxYMargin = 32; + +CPNormalWindowLevel = 4; +CPFloatingWindowLevel = 5; +CPSubmenuWindowLevel = 6; +CPTornOffMenuWindowLevel = 6; +CPMainMenuWindowLevel = 8; +CPStatusWindowLevel = 9; +CPModalPanelWindowLevel = 10; +CPPopUpMenuWindowLevel = 11; +CPDraggingWindowLevel = 12; +CPScreenSaverWindowLevel = 13; + +CPWindowOut = 0; +CPWindowAbove = 1; +CPWindowBelow = 2; + +CPWindowWillCloseNotification = @"CPWindowWillCloseNotification"; +CPWindowDidBecomeMainNotification = @"CPWindowDidBecomeMainNotification"; +CPWindowDidResignMainNotification = @"CPWindowDidResignMainNotification"; + + +var SHADOW_MARGIN_LEFT = 20.0, + SHADOW_MARGIN_RIGHT = 19.0, + SHADOW_MARGIN_TOP = 10.0, + SHADOW_MARGIN_BOTTOM = 10.0, + SHADOW_DISTANCE = 5.0, + + _CPWindowShadowColor = nil; + +var CPWindowSaveImage = nil, + CPWindowSavingImage = nil; + +@implementation CPWindow : CPResponder +{ + int _windowNumber; + unsigned _styleMask; + CGRect _frame; + int _level; + BOOL _isVisible; + BOOL _isAnimating; + BOOL _hasShadow; + BOOL _isMovableByWindowBackground; + + BOOL _isDocumentEdited; + BOOL _isDocumentSaving; + + CPNinePartImageView _shadowView; + + CPView _windowView; + CPView _contentView; + CPView _toolbarView; + + CPView _mouseOverView; + CPView _leftMouseDownView; + CPView _rightMouseDownView; + + CPToolbar _toolbar; + CPResponder _firstResponder; + id _delegate; + + CPString _title; + + BOOL _acceptsMouseMovedEvents; + + CPWindowController _windowController; + + CGSize _minSize; + CGSize _maxSize; + + CGRect _resizeFrame; + CGPoint _mouseDraggedPoint; + + CPUndoManager _undoManager; + CPURL _representedURL; + + // Bridge Support + DOMElement _DOMElement; + CPDOMWindowBridge _bridge; + unsigned _autoresizingMask; + + BOOL _delegateRespondsToWindowWillReturnUndoManagerSelector; +} + ++ (void)initialize +{ + if (self != [CPWindow class]) + return; + + var bundle = [CPBundle bundleForClass:[CPWindow class]]; + + CPWindowResizeIndicatorImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindowResizeIndicator.png"] size:CGSizeMake(12.0, 12.0)]; + + CPWindowSavingImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(16.0, 16.0)] +} + +- (id)initWithContentRect:(CGRect)aContentRect styleMask:(unsigned int)aStyleMask +{ + return [self initWithContentRect:aContentRect styleMask:aStyleMask bridge:[CPDOMWindowBridge sharedDOMWindowBridge]]; +} + +- (id)initWithContentRect:(CGRect)aContentRect styleMask:(unsigned int)aStyleMask bridge:(CPDOMWindowBridge)aBridge +{ + self = [super init]; + + if (self) + { + // Set up our window number. + _windowNumber = [CPApp._windows count]; + CPApp._windows[_windowNumber] = self; + + _styleMask = aStyleMask; + + _frame = [self frameRectForContentRect:aContentRect]; + + _level = CPNormalWindowLevel; + _hasShadow = NO; + + _minSize = CGSizeMake(0.0, 0.0); + _maxSize = CGSizeMake(1000000.0, 1000000.0); + + if (_styleMask & CPBorderlessBridgeWindowMask) + _autoresizingMask = CPWindowWidthSizable | CPWindowHeightSizable; + + // Create our border view which is the actual root of our view hierarchy. + _windowView = [[_CPWindowView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame)) forStyleMask:_styleMask]; + + [_windowView _setWindow:self]; + [_windowView setNextResponder:self]; + + [self setMovableByWindowBackground:aStyleMask & CPHUDBackgroundWindowMask]; + + // Create a generic content view. + [self setContentView:[[CPView alloc] initWithFrame:CGRectMakeZero()]]; + + _firstResponder = self; + + _DOMElement = document.createElement("div"); + + _DOMElement.style.position = "absolute"; + _DOMElement.style.visibility = "visible"; + _DOMElement.style.zIndex = 0; + + CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, _CGRectGetMinX(_frame), _CGRectGetMinY(_frame)); + CPDOMDisplayServerSetStyleSize(_DOMElement, 1, 1); + + CPDOMDisplayServerAppendChild(_DOMElement, _windowView._DOMElement); + + [self setBridge:aBridge]; + + [self setNextResponder:CPApp]; + } + + return self; +} + +- (unsigned)styleMask +{ + return _styleMask; +} + ++ (CGRect)frameRectForContentRect:(CGRect)aContentRect styleMask:(unsigned)aStyleMask +{ + var frame = CGRectMakeCopy(aContentRect); + + return frame; +} + +- (CGRect)contentRectForFrameRect:(CGRect)aFrame +{ + // FIXME: EXTRA RECT COPY + var contentRect = CGRectMakeCopy([_windowView bounds]); + + if (_styleMask & CPHUDBackgroundWindowMask) + { + contentRect.origin.x += 7.0; + contentRect.origin.y += 30.0; + contentRect.size.width -= 14.0; + contentRect.size.height -= 40.0; + } + + else if (_styleMask & CPBorderlessBridgeWindowMask) + { + // The full width, like borderless. + } + + if ([_toolbar isVisible]) + { + var toolbarHeight = CGRectGetHeight([_toolbarView frame]); + + contentRect.origin.y += toolbarHeight; + contentRect.size.height -= toolbarHeight; + } + + return contentRect; +} + +- (CGRect)frameRectForContentRect:(CGRect)aContentRect +{ + if (_styleMask & CPBorderlessBridgeWindowMask) + return _bridge ? [_bridge visibleFrame] : CGRectMakeZero(); + + var frame = [[self class] frameRectForContentRect:aContentRect styleMask:_styleMask]; + + return frame; +} + +- (CGRect)frame +{ + return _frame; +} + +- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate +{ + if (shouldAnimate) + { + var animation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame]; + + [animation startAnimation]; + } + else + { + [self setFrameOrigin:aFrame.origin]; + [self setFrameSize:aFrame.size]; + } +} + +- (void)setFrame:(CGRect)aFrame +{ + [self setFrame:aFrame display:YES animate:NO]; +} + +- (void)setFrameOrigin:(CGPoint)anOrigin +{ + var origin = _frame.origin; + + if (_CGPointEqualToPoint(origin, anOrigin)) + return; + + origin.x = anOrigin.x; + origin.y = anOrigin.y; + +#if PLATFORM(DOM) + CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, origin.x, origin.y); +#endif +} + +- (void)setFrameSize:(CGSize)aSize +{ + aSize = _CGSizeMake(MIN(MAX(aSize.width, _minSize.width), _maxSize.width), MIN(MAX(aSize.height, _minSize.height), _maxSize.height)); + + if (_CGSizeEqualToSize(_frame.size, aSize)) + return; + + _frame.size = aSize; + + [_windowView setFrameSize:aSize]; + + if (_hasShadow) + [_shadowView setFrameSize:_CGSizeMake(SHADOW_MARGIN_LEFT + aSize.width + SHADOW_MARGIN_RIGHT, SHADOW_MARGIN_BOTTOM + aSize.height + SHADOW_MARGIN_TOP + SHADOW_DISTANCE)]; + + if (!_isAnimating && [_delegate respondsToSelector:@selector(windowDidResize:)]) + [_delegate windowDidResize:self]; +} + +- (void)trackMoveWithEvent:(CPEvent)anEvent +{ + var type = [anEvent type]; + + if (type == CPLeftMouseUp) + return; + + else if (type == CPLeftMouseDown) + _mouseDraggedPoint = [self convertBaseToBridge:[anEvent locationInWindow]]; + + else if (type == CPLeftMouseDragged) + { + var location = [self convertBaseToBridge:[anEvent locationInWindow]]; + + [self setFrameOrigin:CGPointMake(_CGRectGetMinX(_frame) + (location.x - _mouseDraggedPoint.x), _CGRectGetMinY(_frame) + (location.y - _mouseDraggedPoint.y))]; + + _mouseDraggedPoint = location; + } + + [CPApp setTarget:self selector:@selector(trackMoveWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; +} + +- (void)trackResizeWithEvent:(CPEvent)anEvent +{ + var location = [anEvent locationInWindow], + type = [anEvent type]; + + if (type == CPLeftMouseUp) + return; + + else if (type == CPLeftMouseDown) + _resizeFrame = CGRectMake(location.x, location.y, CGRectGetWidth(_frame), CGRectGetHeight(_frame)); + + else if (type == CPLeftMouseDragged) + [self setFrameSize:CGSizeMake(CGRectGetWidth(_resizeFrame) + location.x - CGRectGetMinX(_resizeFrame), CGRectGetHeight(_resizeFrame) + location.y - CGRectGetMinY(_resizeFrame))]; + + [CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; +} + +- (void)orderFront:(id)aSender +{ + [_bridge order:CPWindowAbove window:self relativeTo:nil]; +} + +- (void)orderBack:(id)aSender +{ + //[_bridge order:CPWindowBelow +} + +- (void)orderOut:(id)aSender +{ + if ([_delegate respondsToSelector:@selector(windowWillClose:)]) + [_delegate windowWillClose:self]; + + [_bridge order:CPWindowOut window:self relativeTo:nil]; + + if ([CPApp keyWindow] == self) + { + [self resignKeyWindow]; + + CPApp._keyWindow = nil; + } +} + +- (void)orderWindow:(CPWindowOrderingMode)aPlace relativeTo:(int)otherWindowNumber +{ + [_bridge order:aPlace window:self relativeTo:CPApp._windows[otherWindowNumber]]; +} + +- (void)setLevel:(int)aLevel +{ + _level = aLevel; +} + +- (int)level +{ + return _level; +} + +- (BOOL)isVisible +{ + return _isVisible; +} + +- (BOOL)showsResizeIndicator +{ + return [_windowView showsResizeIndicator]; +} + +- (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator +{ + [_windowView setShowsResizeIndicator:shouldShowResizeIndicator]; +} + +- (CGSize)resizeIndicatorOffset +{ + return _resizeIndicatorOffset; +} + +- (void)setResizeIndicatorOffset:(CGSize)aSize +{ + _resizeIndicatorOffset = CGSizeCreateCopy(aSize); + + if (_showsResizeIndicator) + { + _resizeIndicator.style.top = (CGRectGetMaxY(_windowView._frame) - 13.0 + _resizeIndicatorOffset.height) + "px"; + _resizeIndicator.style.left = (CGRectGetMaxX(_windowView._frame) - 13.0 + _resizeIndicatorOffset.width) + "px"; + } +} + +- (void)setContentView:(CPView)aView +{ + if (_contentView) + [_contentView removeFromSuperview]; + + _contentView = aView; + [_contentView setFrame:[self contentRectForFrameRect:_frame]]; + + [_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + [_windowView addSubview:_contentView]; +} + +- (CPView)contentView +{ + return _contentView; +} + +- (void)setBackgroundColor:(CPColor)aColor +{ + [_windowView setBackgroundColor:aColor]; +} + +- (CPColor)backgroundColor +{ + return [_windowView backgroundColor]; +} + +- (void)setMinSize:(CGSize)aSize +{ + if (CGSizeEqualToSize(_minSize, aSize)) + return; + + _minSize = CGSizeCreateCopy(aSize); + + var size = CGSizeMakeCopy([self frame].size), + needsFrameChange = NO; + + if (size.width < _minSize.width) + { + size.width = _minSize.width; + needsFrameChange = YES; + } + + if (size.height < _minSize.height) + { + size.height = _minSize.height; + needsFrameChange = YES; + } + + if (needsFrameChange) + [self setFrameSize:size]; +} + +- (CGSize)minSize +{ + return _minSize; +} + +- (void)setMaxSize:(CGSize)aSize +{ + if (CGSizeEqualToSize(_maxSize, aSize)) + return; + + _maxSize = CGSizeCreateCopy(aSize); + + var size = CGSizeMakeCopy([self frame].size), + needsFrameChange = NO; + + if (size.width > _maxSize.width) + { + size.width = _maxSize.width; + needsFrameChange = YES; + } + + if (size.height > _maxSize.height) + { + size.height = _maxSize.height; + needsFrameChange = YES; + } + + if (needsFrameChange) + [self setFrameSize:size]; +} + +- (CGSize)maxSize +{ + return _maxSize; +} + +- (BOOL)hasShadow +{ + return _hasShadow; +} + +- (void)setHasShadow:(BOOL)shouldHaveShadow +{ + if (_hasShadow == shouldHaveShadow) + return; + + _hasShadow = shouldHaveShadow; + + if (_hasShadow) + { + var bounds = [_windowView bounds]; + + _shadowView = [[CPView alloc] initWithFrame:CGRectMake(-SHADOW_MARGIN_LEFT, -SHADOW_MARGIN_TOP + SHADOW_DISTANCE, + SHADOW_MARGIN_LEFT + CGRectGetWidth(bounds) + SHADOW_MARGIN_RIGHT, SHADOW_MARGIN_TOP + CGRectGetHeight(bounds) + SHADOW_MARGIN_BOTTOM)]; + + if (!_CPWindowShadowColor) + { + var bundle = [CPBundle bundleForClass:[self class]]; + + _CPWindowShadowColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow0.png"] size:CGSizeMake(20.0, 19.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow1.png"] size:CGSizeMake(1.0, 19.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow2.png"] size:CGSizeMake(19.0, 19.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow3.png"] size:CGSizeMake(20.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow4.png"] size:CGSizeMake(1.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow5.png"] size:CGSizeMake(19.0, 1.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow6.png"] size:CGSizeMake(20.0, 18.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow7.png"] size:CGSizeMake(1.0, 18.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow8.png"] size:CGSizeMake(19.0, 18.0)] + ]]]; + } + + [_shadowView setBackgroundColor:_CPWindowShadowColor]; + [_shadowView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + + CPDOMDisplayServerInsertBefore(_DOMElement, _shadowView._DOMElement, _windowView._DOMElement); + } + else + { + CPDOMDisplayServerRemoveChild(_DOMElement, _shadowView._DOMElement); + + _shadowView = nil; + } +} + +- (void)setDelegate:(id)aDelegate +{ + _delegate = aDelegate; + + _delegateRespondsToWindowWillReturnUndoManagerSelector = [_delegate respondsToSelector:@selector(windowWillReturnUndoManager:)]; + + if ([_delegate respondsToSelector:@selector(windowDidBecomeMain:)]) + [defaultCenter + addObserver:_delegate + selector:@selector(windowDidBecomeMain:) + name:CPWindowDidBecomeMainNotification + object:self]; + + if ([_delegate respondsToSelector:@selector(windowDidResignMain:)]) + [defaultCenter + addObserver:_delegate + selector:@selector(windowDidResignMain:) + name:CPWindowDidResignMainNotification + object:self]; +} + +- (id)delegate +{ + return _delegate; +} + +- (void)setWindowController:(CPWindow)aWindowController +{ + _windowController = aWindowController; +} + +- (CPWindowController)windowController +{ + return _windowController; +} + +- (void)doCommandBySelector:(SEL)aSelector +{ + if ([_delegate respondsToSelector:aSelector]) + [_delegate performSelector:aSelector]; + else + [super doCommandBySelector:aSelector]; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)makeFirstResponder:(CPResponder)aResponder +{ + if (_firstResponder == aResponder) + return YES; + + if(![_firstResponder resignFirstResponder]) + return NO; + + if(!aResponder || ![aResponder acceptsFirstResponder] || ![aResponder becomeFirstResponder]) + { + _firstResponder = self; + + return NO; + } + + _firstResponder = aResponder; + + return YES; +} + +- (CPResponder)firstResponder +{ + return _firstResponder; +} + +- (BOOL)acceptsMouseMovedEvents +{ + return _acceptsMouseMovedEvents; +} + +- (void)setAcceptsMouseMovedEvents:(BOOL)shouldAcceptMouseMovedEvents +{ + _acceptsMouseMovedEvents = shouldAcceptMouseMovedEvents; +} + +// Managing Titles + +- (CPString)title +{ + return _title; +} + +- (void)setTitle:(CPString)aTitle +{ + _title = aTitle; + + [_windowView setTitle:aTitle]; + + [self _synchronizeMenuBarTitleWithWindowTitle]; +} + +- (void)setTitleWithRepresentedFilename:(CPString)aFilePath +{ + [self setRepresentedFilename:aFilePath]; + [self setTitle:[aFilePath lastPathComponent]]; +} + +- (void)setRepresentedFilename:(CPString)aFilePath +{ + // FIXME: urls vs filepaths and all. + [self setRepresentedURL:aFilePath]; +} + +- (CPString)representedFilename +{ + return _representedURL; +} + +- (void)setRepresentedURL:(CPURL)aURL +{ + _representedURL = aURL; +} + +- (CPURL)representedURL +{ + return _representedURL; +} + +// Moving + +- (void)setMovableByWindowBackground:(BOOL)shouldBeMovableByWindowBackground +{ + _isMovableByWindowBackground = shouldBeMovableByWindowBackground; +} + +- (BOOL)isMovableByWindowBackground +{ + return _isMovableByWindowBackground; +} + +- (void)center +{ + var size = [self frame].size, + bridgeSize = [_bridge contentBounds].size; + + [self setFrameOrigin:CGPointMake((bridgeSize.width - size.width) / 2.0, (bridgeSize.height - size.height) / 2.0)]; +} + +- (void)sendEvent:(CPEvent)anEvent +{ + var type = [anEvent type], + point = [anEvent locationInWindow]; + + switch (type) + { + case CPKeyUp: return [[self firstResponder] keyUp:anEvent]; + case CPKeyDown: return [[self firstResponder] keyDown:anEvent]; + + case CPScrollWheel: return [[_windowView hitTest:point] scrollWheel:anEvent]; + + case CPLeftMouseUp: if (!_leftMouseDownView) + return [[_windowView hitTest:point] mouseUp:anEvent]; + + [_leftMouseDownView mouseUp:anEvent] + + _leftMouseDownView = nil; + + return; + case CPLeftMouseDown: _leftMouseDownView = [_windowView hitTest:point]; + + if (_leftMouseDownView != _firstResponder && [_leftMouseDownView acceptsFirstResponder]) + [self makeFirstResponder:_leftMouseDownView]; + + var theWindow = [anEvent window]; + + if ([theWindow isKeyWindow] || [theWindow becomesKeyOnlyIfNeeded]) + return [_leftMouseDownView mouseDown:anEvent]; + else + { + // FIXME: delayed ordering? + [self makeKeyAndOrderFront:self]; + + if ([_leftMouseDownView acceptsFirstMouse:anEvent]) + return [_leftMouseDownView mouseDown:anEvent] + } + break; + case CPLeftMouseDragged: if (!_leftMouseDownView) + return [[_windowView hitTest:point] mouseDragged:anEvent]; + + return [_leftMouseDownView mouseDragged:anEvent]; + + case CPRightMouseUp: return [_rightMouseDownView mouseUp:anEvent]; + case CPRightMouseDown: _rightMouseDownView = [_windowView hitTest:point]; + return [_rightMouseDownView mouseDown:anEvent]; + case CPRightMouseDragged: return [_rightMouseDownView mouseDragged:anEvent]; + + case CPMouseMoved: if (!_acceptsMouseMovedEvents) + return; + + var hitTestView = [_windowView hitTest:point]; + + if (hitTestView != _mouseOverView) + { + if (_mouseOverView) + [_mouseOverView mouseExited:[CPEvent mouseEventWithType:CPMouseExited location:point + modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0]]; + + if (hitTestView) + [hitTestView mouseEntered:[CPEvent mouseEventWithType:CPMouseEntered location:point + modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0]]; + + _mouseOverView = hitTestView; + } + + [_mouseOverView mouseMoved:anEvent]; + } +} + +- (int)windowNumber +{ + return _windowNumber; +} + +- (void)becomeKeyWindow +{ + if (_firstResponder != self && [_firstResponder respondsToSelector:@selector(becomeKeyWindow)]) + [_firstResponder becomeKeyWindow]; +} + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +- (BOOL)isKeyWindow +{ + return [CPApp keyWindow] == self; +} + +- (void)makeKeyAndOrderFront:(id)aSender +{ + [self orderFront:self]; + + [self makeKeyWindow]; + [self makeMainWindow]; +} + +- (void)makeKeyWindow +{ + if (![self canBecomeKeyWindow]) + return; + + [CPApp._keyWindow resignKeyWindow]; + + CPApp._keyWindow = self; + + [self becomeKeyWindow]; +} + +- (void)resignKeyWindow +{ + if (_firstResponder != self && [_firstResponder respondsToSelector:@selector(resignKeyWindow)]) + [_firstResponder resignKeyWindow]; + + if ([_delegate respondsToSelector:@selector(windowDidResignKey:)]) + [_delegate windowDidResignKey:self]; +} + +- (void)dragImage:(CPImage)anImage at:(CGPoint)imageLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack +{ + [[CPDragServer sharedDragServer] dragImage:anImage fromWindow:self at:[self convertBaseToBridge:imageLocation] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack]; +} + +- (void)dragView:(CPView)aView at:(CGPoint)imageLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack +{ + [[CPDragServer sharedDragServer] dragView:aView fromWindow:self at:[self convertBaseToBridge:imageLocation] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack]; +} + +// Accessing Editing Status + +- (void)setDocumentEdited:(BOOL)isDocumentEdited +{ + if (_isDocumentEdited == isDocumentEdited) + return; + + _isDocumentEdited = isDocumentEdited; + + [CPMenu _setMenuBarIconImageAlphaValue:_isDocumentEdited ? 0.5 : 1.0]; +} + +- (BOOL)isDocumentEdited +{ + return _isDocumentEdited; +} + +- (void)setDocumentSaving:(BOOL)isDocumentSaving +{ + if (_isDocumentSaving == isDocumentSaving) + return; + + _isDocumentSaving = isDocumentSaving; + + [self _synchronizeSaveMenuWithDocumentSaving]; + + [_windowView windowDidChangeDocumentSaving]; +} + +- (BOOL)isDocumentSaving +{ + return _isDocumentSaving; +} + +- (void)_synchronizeSaveMenuWithDocumentSaving +{ + if (![self isMainWindow]) + return; + + var mainMenu = [CPApp mainMenu], + index = [mainMenu indexOfItemWithTitle:_isDocumentSaving ? @"Save" : @"Saving..."]; + + if (index == CPNotFound) + return; + + var item = [mainMenu itemAtIndex:index]; + + if (_isDocumentSaving) + { + CPWindowSaveImage = [item image]; + + [item setTitle:@"Saving..."]; + [item setImage:CPWindowSavingImage]; + [item setEnabled:NO]; + } + else + { + [item setTitle:@"Save"]; + [item setImage:CPWindowSaveImage]; + [item setEnabled:YES]; + } +} + +// Closing Windows + +- (void)performClose:(id)aSender +{ + if ([_delegate respondsToSelector:@selector(windowShouldClose:)] && ![_delegate windowShouldClose:self] || + [self respondsToSelector:@selector(windowShouldClose:)] && ![self windowShouldClose:self]) + return; + + [self close]; +} + +- (void)close +{ + [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillCloseNotification object:self]; + + [self orderOut:nil]; +} + +// Managing Main Status + +- (BOOL)isMainWindow +{ + return [CPApp mainWindow] == self; +} + +- (BOOL)canBecomeMainWindow +{ + // FIXME: Also check if we can resize and titlebar. + if ([self isVisible]) + return YES; + + return NO; +} + +- (void)makeMainWindow +{ + if (![self canBecomeMainWindow]) + return; + + [CPApp._mainWindow resignMainWindow]; + + CPApp._mainWindow = self; + + [self becomeMainWindow]; +} + +- (void)becomeMainWindow +{ + [self _synchronizeMenuBarTitleWithWindowTitle]; + [self _synchronizeSaveMenuWithDocumentSaving]; + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPWindowDidBecomeMainNotification + object:self]; +} + +- (void)resignMainWindow +{ + [[CPNotificationCenter defaultCenter] + postNotificationName:CPWindowDidResignMainNotification + object:self]; +} + +// Managing Toolbars + +- (CPToolbar)toolbar +{ + return _toolbar; +} + +- (void)setToolbar:(CPView)aToolbar +{ + if (_toolbar == aToolbar) + return; + + // Cleanup old toolbar + if (_toolbar) + { + [self _setToolbarVisible:NO]; + + _toolbar._window = nil; + _toolbarView = nil; + } + + if (_toolbar = aToolbar) + { + // Set up new toolbar + _toolbar = aToolbar; + _toolbar._window = self; + + if ([_toolbar isVisible]) + [self _setToolbarVisible:YES]; + + [_toolbar _reloadToolbarItems]; + } +} + +- (void)_setToolbarVisible:(BOOL)aFlag +{ + if (aFlag) + { + if (!_toolbarView) + _toolbarView = [_toolbar _toolbarView]; + + [_toolbarView setFrame:CGRectMake(0.0, 0.0, CGRectGetWidth([_windowView bounds]), CGRectGetHeight([_toolbarView frame]))]; + [_windowView addSubview:_toolbarView]; + } + else + [_toolbarView removeFromSuperview]; + + [_contentView setFrame:[self contentRectForFrameRect:[_windowView bounds]]]; +} + +// Managing Sheets + +- (void)_setAttachedSheetFrameOrigin +{ + // Position the sheet above the contentRect. + var contentRect = [[self contentView] frame], + sheetFrame = CGRectMakeCopy([_attachedSheet frame]); + + sheetFrame.origin.y = CGRectGetMinY(_frame) + CGRectGetMinY(contentRect); + sheetFrame.origin.x = CGRectGetMinX(_frame) + FLOOR((CGRectGetWidth(_frame) - CGRectGetWidth(sheetFrame)) / 2.0); + + [_attachedSheet setFrameOrigin:sheetFrame.origin]; +} + +- (void)_animateAttachedSheet +{ +/* NSWindow *sheet = [sheetContext sheet]; + NSRect sheetFrame; + + [_sheetContext autorelease]; + _sheetContext=[sheetContext retain]; + + [self _setSheetOrigin]; + sheetFrame = [sheet frame]; + + [(NSWindowBackgroundView *)[sheet _backgroundView] setBorderType:NSButtonBorder]; + [[sheet contentView] setAutoresizesSubviews:NO]; + [[sheet contentView] setAutoresizingMask:NSViewNotSizable]; + +// [(NSWindowBackgroundView *)[sheet _backgroundView] cacheImageForAnimation]; + + [_attachedSheet setFrame:CPRectMake(CGRectGetMinX(sheetFrame), CGRectGetMinY(sheetFrame), CGRectGetWidth(sheetFrame), 0.0) display:YES]; + [sheet setFrame:NSMakeRect(sheetFrame.origin.x, NSMaxY([self frame]), sheetFrame.size.width, 0) display:YES]; + [self _setSheetOriginAndFront]; + + [sheet setFrame:sheetFrame display:YES animate:YES];*/ +} + +- (void)_attachSheet:(CPWindow)aSheet modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo +{ + // Set this as our attached sheet. + _attachedSheet = aSheet; + + // If a window is ever run as a sheet, then it's sheet bit is set to YES. + aSheet._isSheet = YES; + + [self _setAttachedSheetFrameOrigin]; + + // Place this window above ourselves. + [_bridge order:CPWindowAbove window:aSheet relativeTo:self]; +} + +- (CPWindow)attachedSheet +{ + return _attachedSheet; +} + +- (BOOL)isSheet +{ + return _isSheet; +} + +// + +- (BOOL)becomesKeyOnlyIfNeeded +{ + return NO; +} + +- (BOOL)worksWhenModal +{ + return NO; +} + +@end + +@implementation CPWindow (MenuBar) + +- (void)_synchronizeMenuBarTitleWithWindowTitle +{ + // Windows with Documents automatically update the native window title and the menu bar title. + if (![_windowController document] || ![self isMainWindow]) + return; + + [CPMenu setMenuBarTitle:_title]; +} + +@end + +@implementation CPWindow (BridgeSupport) + +- (void)setBridge:(CPDOMWindowBridge)aBridge +{ + if (_bridge == aBridge) + return; + + if (_bridge) + { + [self orderOut:self]; + // FIXME: If the bridge changes, then we have to recreate all of our subviews' DOM Elements. + } + + _bridge = aBridge; + + if (_styleMask & CPBorderlessBridgeWindowMask) + [self setFrame:[aBridge contentBounds]]; +} + +- (void)resizeWithOldBridgeSize:(CGSize)aSize +{ + if (_styleMask & CPBorderlessBridgeWindowMask) + return [self setFrame:[_bridge visibleFrame]]; + + if (_autoresizingMask == CPWindowNotSizable) + return; + + var frame = [_bridge contentBounds], + newFrame = CGRectMakeCopy(_frame), + dX = (CGRectGetWidth(frame) - aSize.width) / + (((_autoresizingMask & CPWindowMinXMargin) ? 1 : 0) + (_autoresizingMask & CPWindowWidthSizable ? 1 : 0) + (_autoresizingMask & CPWindowMaxXMargin ? 1 : 0)), + dY = (CGRectGetHeight(frame) - aSize.height) / + ((_autoresizingMask & CPWindowMinYMargin ? 1 : 0) + (_autoresizingMask & CPWindowHeightSizable ? 1 : 0) + (_autoresizingMask & CPWindowMaxYMargin ? 1 : 0)); + + if (_autoresizingMask & CPWindowMinXMargin) + newFrame.origin.x += dX; + if (_autoresizingMask & CPWindowWidthSizable) + newFrame.size.width += dX; + + if (_autoresizingMask & CPWindowMinYMargin) + newFrame.origin.y += dY; + if (_autoresizingMask & CPWindowHeightSizable) + newFrame.size.height += dY; + + [self setFrame:newFrame]; +} + +- (void)setAutoresizingMask:(unsigned)anAutoresizingMask +{ + _autoresizingMask = anAutoresizingMask; +} + +- (unsigned)autoresizingMask +{ + return _autoresizingMask; +} + +- (CGPoint)convertBaseToBridge:(CGPoint)aPoint +{ + var origin = [self frame].origin; + + return CGPointMake(aPoint.x + origin.x, aPoint.y + origin.y); +} + +- (CGPoint)convertBridgeToBase:(CGPoint)aPoint +{ + var origin = [self frame].origin; + + return CGPointMake(aPoint.x - origin.x, aPoint.y - origin.y); +} + +// Undo and Redo Support + +- (CPUndoManager)undoManager +{ + if (_delegateRespondsToWindowWillReturnUndoManagerSelector) + return [_delegate windowWillReturnUndoManager:self]; + + if (!_undoManager) + _undoManager = [[CPUndoManager alloc] init]; + + return _undoManager; +} + +- (void)undo:(id)aSender +{ + [[self undoManager] undo]; +} + +- (void)redo:(id)aSender +{ + [[self undoManager] redo]; +} + +@end + +var interpolate = function(fromValue, toValue, progress) +{ + return fromValue + (toValue - fromValue) * progress; +} + +@implementation _CPWindowFrameAnimation : CPAnimation +{ + CPWindow _window; + + CGRect _startFrame; + CGRect _targetFrame; +} + +- (id)initWithWindow:(CPWindow)aWindow targetFrame:(CGRect)aTargetFrame +{ + self = [super initWithDuration:0.2 animationCurve:CPAnimationLinear]; + + if (self) + { + _window = aWindow; + + _targetFrame = CGRectMakeCopy(aTargetFrame); + _startFrame = CGRectMakeCopy([_window frame]); + } + + return self; +} + +- (void)startAnimation +{ + [super startAnimation]; + + _window._isAnimating = YES; +} + +- (void)setCurrentProgress:(float)aProgress +{ + [super setCurrentProgress:aProgress]; + + var value = [self currentValue]; + + if (value == 1.0) + _window._isAnimating = NO; + + [_window setFrameOrigin:CGPointMake(interpolate(CGRectGetMinX(_startFrame), CGRectGetMinX(_targetFrame), value), interpolate(CGRectGetMinY(_startFrame), CGRectGetMinY(_targetFrame), value))]; + [_window setFrameSize:CGSizeMake(interpolate(CGRectGetWidth(_startFrame), CGRectGetWidth(_targetFrame), value), interpolate(CGRectGetHeight(_startFrame), CGRectGetHeight(_targetFrame), value))]; +} + +@end + +import "CPDragServer.j" +import "CPDOMWindowBridge.j" +import "_CPWindowView.j" +import "CPView.j" diff --git a/AppKit/CPWindowController.j b/AppKit/CPWindowController.j new file mode 100644 index 0000000000..e0c7b8abcc --- /dev/null +++ b/AppKit/CPWindowController.j @@ -0,0 +1,215 @@ +/* + * CPWindowController.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + +import "CPResponder.j" +import "CPWindow.j" +import "CPDocument.j" + +#include "Platform/Platform.h" + + +@implementation CPWindowController : CPResponder +{ + id _owner; + CPWindow _window; + CPDocument _document; + CPString _windowCibName; +} + +- (id)initWithWindow:(CPWindow)aWindow +{ + self = [super init]; + + if (self) + { + [self setWindow:aWindow]; + + [self setNextResponder:CPApp]; + } + + return self; +} + +- (id)initWithWindowCibName:(CPString)aWindowCibName +{ + return [self initWithWindowCibName:aWindowCibName owner:self]; +} + +- (id)initWithWindowCibName:(CPString)aWindowCibName owner:(id)anOwner +{ + self = [super init]; + + if (self) + { + _owner = anOwner; + _windowCibName = aWindowCibName; + + [self setNextResponder:CPApp]; + } + + return self; +} + +- (void)loadWindow +{ + [self windowWillLoad]; + //FIXME: ACTUALLY LOAD WINDOW!!! + [self setWindow:CPApp._keyWindow = [[CPWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:CPBorderlessBridgeWindowMask|CPTitledWindowMask|CPClosableWindowMask|CPResizableWindowMask]]; + + [self windowDidLoad]; +} + +- (CFAction)showWindow:(id)aSender +{ + var theWindow = [self window]; + + if ([theWindow respondsToSelector:@selector(becomesKeyOnlyIfNeeded)] && [theWindow becomesKeyOnlyIfNeeded]) + [theWindow orderFront:aSender]; + else + [theWindow makeKeyAndOrderFront:aSender]; +} + +- (BOOL)isWindowLoaded +{ + return _window; +} + +- (CPWindow)window +{ + if (!_window) + [self loadWindow]; + + return _window; +} + +- (void)setWindow:(CPWindow)aWindow +{ + _window = aWindow; + + [_window setWindowController:self]; + [_window setNextResponder:self]; +} + +- (void)windowDidLoad +{ + [_document windowControllerDidLoadNib:self]; + + [self synchronizeWindowTitleWithDocumentName]; +} + +- (void)windowWillLoad +{ + [_document windowControllerWillLoadNib:self]; +} + +- (void)setDocument:(CPDocument)aDocument +{ + if (_document == aDocument) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + if (_document) + { + [defaultCenter removeObserver:self + name:CPDocumentWillSaveNotification + object:_document]; + + [defaultCenter removeObserver:self + name:CPDocumentDidSaveNotification + object:_document]; + + [defaultCenter removeObserver:self + name:CPDocumentDidFailToSaveNotification + object:_document]; + } + + _document = aDocument; + + if (_document) + { + [defaultCenter addObserver:self + selector:@selector(_documentWillSave:) + name:CPDocumentWillSaveNotification + object:_document]; + + [defaultCenter addObserver:self + selector:@selector(_documentDidSave:) + name:CPDocumentDidSaveNotification + object:_document]; + + [defaultCenter addObserver:self + selector:@selector(_documentDidFailToSave:) + name:CPDocumentDidFailToSaveNotification + object:_document]; + + [self setDocumentEdited:[_document isDocumentEdited]]; + } + + [self synchronizeWindowTitleWithDocumentName]; +} + +- (void)_documentWillSave:(CPNotification)aNotification +{ + [[self window] setDocumentSaving:YES]; +} + +- (void)_documentDidSave:(CPNotification)aNotification +{ + [[self window] setDocumentSaving:NO]; +} + +- (void)_documentDidFailToSave:(CPNotification)aNotification +{ + [[self window] setDocumentSaving:NO]; +} + +- (CPDocument)document +{ + return _document; +} + +- (void)setDocumentEdited:(BOOL)isEdited +{ + [[self window] setDocumentEdited:isEdited]; +} + +// Setting and Getting Window Attributes + +- (void)synchronizeWindowTitleWithDocumentName +{ + if (!_document || !_window) + return; + + // [_window setRepresentedFilename:]; + [_window setTitle:[self windowTitleForDocumentDisplayName:[_document displayName]]]; +} + +- (CPString)windowTitleForDocumentDisplayName:(CPString)aDisplayName +{ + return aDisplayName; +} + +@end diff --git a/AppKit/Cib/CPCib.j b/AppKit/Cib/CPCib.j new file mode 100644 index 0000000000..e535eaa78f --- /dev/null +++ b/AppKit/Cib/CPCib.j @@ -0,0 +1,128 @@ +/* + * CPCib.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import + +import "CPCustomView.j" +import "_CPCibObjectData.j" + + +CPCibOwner = @"CPCibOwner", +CPCibTopLevelObjects = @"CPCibTopLevelObjects"; + +var CPCibObjectDataKey = @"CPCibObjectDataKey"; + +@implementation CPCib : CPObject +{ + CPData _data; +} + +- (id)initWithContentsOfURL:(CPURL)aURL +{ + self = [super init]; + + if (self) + _data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil error:nil]; + + return self; +} + +- (BOOL)instantiateCibWithExternalNameTable:(CPDictionary)anExternalNameTable +{ + var unarchiver = [[CPKeyedUnarchiver alloc] initForReadingWithData:_data], + objectData = [unarchiver decodeObjectForKey:CPCibObjectDataKey]; + + if (!objectData || ![objectData isKindOfClass:[_CPCibObjectData class]]) + return NO; + + var owner = [anExternalNameTable objectForKey:CPCibOwner], + topLevelObjects = [anExternalNameTable objectForKey:CPCibTopLevelObjects]; + + var objects = unarchiver._objects, + count = [objects count]; + + // The order in which objects receive the awakeFromCib message is not guaranteed. + while (count--) + { + var object = objects[count]; + + if ([object isMemberOfClass:[CPView class]]) + return object; + } + /* +// [objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects]; +// [objectData cibInstantiateWithOwner:owner topLevelObjects:topLevelObjects]; +// alert([objectData description]); +// alert([unarchiver._archive description]); + + NSMutableArray *t; + NSEnumerator *e; + id o; + id owner; + id rootObject; +#if 0 + NSLog(@"instantiateCibWithExternalNameTable=%@", table); +#endif + if(![decoded isKindOfClass:[NSIBObjectData class]]) + return NO; + owner=[table objectForKey:NSCibOwner]; + rootObject=[decoded rootObject]; +#if 0 + NSLog(@"establishConnections"); +#endif + [decoded establishConnectionsWithExternalNameTable:table]; +#if 0 + NSLog(@"awakeFromCib %d objects", [decodedObjects count]); +#endif +#if 0 + NSLog(@"objects 2=%@", decodedObjects); +#endif + + // FIXME: shouldn't be accessing private variables. + var objects = unarchiver._objects, + count = [objects count]; + + // The order in which objects receive the awakeFromCib message is not guaranteed. + while (count--) + { + var object = objects[count]; + + if ([object respondsToSelector:@selector(awakeFromCib)]) + [object awakeFromCib]; + } + +// if ([owner respondsToSelector:@selector(awakeFromCib)]) +// [owner awakeFromCib]; + // Display visible windows. + + return YES;*/ +} + +- (BOOL)instantiateCibWithOwner:(id)anOwner topLevelObjects:(CPArray)topLevelObjects +{ + [CPDictionary dictionaryWithObjectsAndKeys:anOwner, CPCibOwner, topLevelObjects, CPCibTopLevelObjects]; + return [self instantiate]; +} + +@end diff --git a/AppKit/Cib/CPCustomView.j b/AppKit/Cib/CPCustomView.j new file mode 100644 index 0000000000..035b97e20a --- /dev/null +++ b/AppKit/Cib/CPCustomView.j @@ -0,0 +1,83 @@ +/* + * CPCustomView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPView.j" + +@implementation CPCustomView : CPView +{ + CPString _className; +} + +@end + +var CPCustomViewClassNameKey = @"CPCustomViewClassNameKey"; + +@implementation CPCustomView (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + _className = [aCoder decodeObjectForKey:CPCustomViewClassNameKey]; + + var theClass = CPClassFromString(_className); + + // If we don't have this class, just use CPView. + // FIXME: Should we instead throw an exception? + if (!theClass) + theClass = [CPView class]; + + // If this is just a "CPView", don't bother with any funny business, just go ahead and create it with initWithCoder: + if (theClass == [CPView class]) + self = [[CPView alloc] initWithCoder:aCoder]; + + return self; + // If not, fall back to initWithFrame: +/* + var frame = [aCoder decodeRectForKey:CPViewFrameKey]; + + self = [[theClass alloc] initWithFrame:frame]; + + if (self) + { + _bounds = [aCoder decodeRectForKey:CPViewBoundsKey]; + + _window = [aCoder decodeObjectForKey:CPViewWindowKey]; + _subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey]; + _superview = [aCoder decodeObjectForKey:CPViewSuperviewKey]; + + _autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey]; + _autoresizesSubviews = [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey]; + + // FIXME: UGH!!!! + _index = [aCoder decodeIntForKey:FIXME_indexKey]; + + _hitTests = [aCoder decodeObjectForKey:CPViewHitTestsKey]; + _isHidden = [aCoder decodeObjectForKey:CPViewIsHiddenKey]; + _opacity = [aCoder decodeIntForKey:CPViewOpacityKey]; + + [self setBackgroundColor:[aCoder decodeObjectForKey:CPViewBackgroundColorKey]]; + + } + + return self;*/ +} + +@end diff --git a/AppKit/Cib/_CPCibLoading.j b/AppKit/Cib/_CPCibLoading.j new file mode 100644 index 0000000000..6c3a464387 --- /dev/null +++ b/AppKit/Cib/_CPCibLoading.j @@ -0,0 +1,69 @@ +/* + * _CPCibLoading.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import + + +var CPCibOwner = @"CPCibOwner"; + +@implementation CPObject (CPCibLoading) + +-(void)awakeFromCib +{ +} + +@end + +@implementation CPBundle (CPCibLoading) + ++ (BOOL)loadCibFile:(CPString)aPath externalNameTable:(CPDictionary)aNameTable +{ + return [[[CPCib alloc] initWithContentsOfFile:aPath] instantiateCibWithExternalNameTable:aNameTable]; +} + ++ (BOOL)loadCibNamed:(CPString)aName owner:(id)anOwner +{ + var bundle = [CPBundle bundleForClass:[anOwner class]]; + + /*NSString *path; + + path=[bundle pathForResource:name ofType:@"cib"]; + if(path==nil) + path=[[NSBundle mainBundle] pathForResource:name ofType:@"cib"]; + + if(path==nil) + return NO; + */ + var path = [bundle pathForResource:aName]; + + return [CPBundle loadCibFile:aPath externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]]; +} + +- (BOOL)loadCibFile:(CPString)aPath externalNameTable:(CPDictionary)aNameTable +{ + return [[[CPCib alloc] initWithContentsOfFile:aPath] instantiateCibWithExternalNameTable:aNameTable]; +} + +@end + diff --git a/AppKit/Cib/_CPCibObjectData.j b/AppKit/Cib/_CPCibObjectData.j new file mode 100644 index 0000000000..53cad72d39 --- /dev/null +++ b/AppKit/Cib/_CPCibObjectData.j @@ -0,0 +1,181 @@ +/* + * _CPCibObjectData.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import + + +@implementation _CPCibObjectData : CPObject +{ + CPArray _namesKeys; + CPArray _namesValues; + + CPArray _accessibilityConnectors; + CPArray _accessibilityOidsKeys; + CPArray _accessibilityOidsValues; + + CPArray _classesKeys; + CPArray _classesValues; + + CPArray _connections; + + id _fontManager; + + CPString _framework; + + int _nextOid; + + CPArray _objectsKeys; + CPArray _objectsValues; + + CPArray _oidKeys; + CPArray _oidValues; + + CPCustomObject _rootObject; + + CPSet _visibleWindows; +} +/* +- (CPArray)topLevelObjects +{ + var count = [_objectsValues count]; + + while (count--) + { + var eachObject = _objectsValues[count]; + + if(eachObject == _fileOwner) + { + var anObject = _objectsKeys[count]; + + if (anObject != _fileOwner) + topLevelObjects.push(anObject); + } + } + + return topLevelObjects; +} +*/ + +@end + +var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey", + _CPCibObjectDataNamesValuesKey = @"_CPCibObjectDataNamesValuesKey", + + _CPCibObjectDataAccessibilityConnectorsKey = @"_CPCibObjectDataAccessibilityConnectors", + _CPCibObjectDataAccessibilityOidsKeysKey = @"_CPCibObjectDataAccessibilityOidsKeys", + _CPCibObjectDataAccessibilityOidsValuesKey = @"_CPCibObjectDataAccessibilityOidsValues", + + _CPCibObjectDataClassesKeysKey = @"_CPCibObjectDataClassesKeysKey", + _CPCibObjectDataClassesValuesKey = @"_CPCibObjectDataClassesValuesKey", + + _CPCibObjectDataConnectionsKey = @"_CPCibObjectDataConnectionsKey", + + _CPCibObjectDataFontManagerKey = @"_CPCibObjectDataFontManagerKey", + + _CPCibObjectDataFrameworkKey = @"_CPCibObjectDataFrameworkKey", + + _CPCibObjectDataNextOidKey = @"_CPCibObjectDataNextOidKey", + + _CPCibObjectDataObjectsKeysKey = @"_CPCibObjectDataObjectsKeysKey", + _CPCibObjectDataObjectsValuesKey = @"_CPCibObjectDataObjectsValuesKey", + + _CPCibObjectDataOidKeysKey = @"_CPCibObjectDataOidKeysKey", + _CPCibObjectDataOidValuesKey = @"_CPCibObjectDataOidValuesKey", + + _CPCibObjectDataRootObjectKey = @"_CPCibObjectDataRootObjectKey", + _CPCibObjectDataVisibleWindowsKey = @"_CPCibObjectDataVisibleWindowsKey"; + +@implementation _CPCibObjectData (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + { + _namesKeys = [aCoder decodeObjectForKey:_CPCibObjectDataNamesKeysKey]; + _namesValues = [aCoder decodeObjectForKey:_CPCibObjectDataNamesValuesKey]; + + //CPArray _accessibilityConnectors; + //CPArray _accessibilityOidsKeys; + //CPArray _accessibilityOidsValues; + + _classesKeys = [aCoder decodeObjectForKey:_CPCibObjectDataClassesKeysKey]; + _classesValues = [aCoder decodeObjectForKey:_CPCibObjectDataClassesValuesKey]; + + _connections = [aCoder decodeObjectForKey:_CPCibObjectDataConnectionsKey]; + + //id _fontManager; + + _framework = [aCoder decodeObjectForKey:_CPCibObjectDataFrameworkKey]; + + _nextOid = [aCoder decodeIntForKey:_CPCibObjectDataNextOidKey]; + + _objectsKeys = [aCoder decodeObjectForKey:_CPCibObjectDataObjectsKeysKey]; + _objectsValues = [aCoder decodeObjectForKey:_CPCibObjectDataObjectsValuesKey]; + + _oidKeys = [aCoder decodeObjectForKey:_CPCibObjectDataOidKeysKey]; + _oidValues = [aCoder decodeObjectForKey:_CPCibObjectDataOidValuesKey]; + + _rootObject = [aCoder decodeObjectForKey:_CPCibObjectDataRootObjectKey]; + + // CPSet _visibleWindows; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_namesKeys forKey:_CPCibObjectDataNamesKeysKey]; + [aCoder encodeObject:_namesValues forKey:_CPCibObjectDataNamesValuesKey]; + + //CPArray _accessibilityConnectors; + //CPArray _accessibilityOidsKeys; + //CPArray _accessibilityOidsValues; + + [aCoder encodeObject:_classesKeys forKey:_CPCibObjectDataClassesKeysKey]; + [aCoder encodeObject:_classesValues forKey:_CPCibObjectDataClassesValuesKey]; + + [aCoder encodeObject:_connections forKey:_CPCibObjectDataConnectionsKey]; + + //id _fontManager; + + [aCoder encodeObject:_framework forKey:_CPCibObjectDataFrameworkKey]; + + [aCoder encodeInt:_nextOid forKey:_CPCibObjectDataNextOidKey]; + + [aCoder encodeObject:_objectsKeys forKey:_CPCibObjectDataObjectsKeysKey]; + [aCoder encodeObject:_objectsValues forKey:_CPCibObjectDataObjectsValuesKey]; + + [aCoder encodeObject:_oidKeys forKey:_CPCibObjectDataOidKeysKey]; + [aCoder encodeObject:_oidValues forKey:_CPCibObjectDataOidValuesKey]; + + [aCoder encodeObject:_rootObject forKey:_CPCibObjectDataRootObjectKey]; +// CPCustomObject _fileOwner; + +// CPSet _visibleWindows; +} + +@end diff --git a/AppKit/CoreAnimation/CAAnimation.j b/AppKit/CoreAnimation/CAAnimation.j new file mode 100644 index 0000000000..d46f99e1c5 --- /dev/null +++ b/AppKit/CoreAnimation/CAAnimation.j @@ -0,0 +1,193 @@ +/* + * CAAnimation.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + +import "CAMediaTimingFunction.j" + + +@implementation CAAnimation : CPObject +{ + BOOL _isRemovedOnCompletion; +} + ++ (id)animation +{ + return [[self alloc] init]; +} + +- (id)init +{ + self = [super init]; + + if (self) + _isRemovedOnCompletion = YES; + + return self; +} + +- (void)shouldArchiveValueForKey:(CPString)aKey +{ + return YES; +} + ++ (id)defaultValueForKey:(CPString)aKey +{ + return nil; +} + +- (void)setRemovedOnCompletion:(BOOL)isRemovedOnCompletion +{ + _isRemovedOnCompletion = isRemovedOnCompletion; +} + +- (BOOL)removedOnCompletion +{ + return _isRemovedOnCompletion; +} + +- (BOOL)isRemovedOnCompletion +{ + return _isRemovedOnCompletion; +} + +- (CAMediaTimingFunction)timingFunction +{ + // Linear Pacing + return nil; +} + +- (void)setDelegate:(id)aDelegate +{ + _delegate = aDelegate; +} + +- (id)delegate +{ + return _delegate; +} + +- (void)runActionForKey:(CPString)aKey object:(id)anObject arguments:(CPDictionary)arguments +{ + [anObject addAnimation:self forKey:aKey]; +} + +@end + +@implementation CAPropertyAnimation : CAAnimation +{ + CPString _keyPath; + + BOOL _isCumulative; + BOOL _isAdditive; +} + ++ (id)animationWithKeyPath:(CPString)aKeyPath +{ + var animation = [self animation]; + + [animation setKeypath:aKeyPath]; + + return animation; +} + +- (void)setKeyPath:(CPString)aKeyPath +{ + _keyPath = aKeyPath; +} + +- (CPString)keyPath +{ + return _keyPath; +} + +- (void)setCumulative:(BOOL)isCumulative +{ + _isCumulative = isCumulative; +} + +- (BOOL)cumulative +{ + return _isCumulative; +} + +- (BOOL)isCumulative +{ + return _isCumulative; +} + +- (void)setAdditive:(BOOL)isAdditive +{ + _isAdditive = isAdditive; +} + +- (BOOL)additive +{ + return _isAdditive; +} + +- (BOOL)isAdditive +{ + return _isAdditive; +} + +@end + +@implementation CABasicAnimation : CAPropertyAnimation +{ + id _fromValue; + id _toValue; + id _byValue; +} + +- (void)setFromValue:(id)aValue +{ + _fromValue = aValue; +} + +- (id)fromValue +{ + return _fromValue; +} + +- (void)setToValue:(id)aValue +{ + _toValue = aValue; +} + +- (id)toValue +{ + return _toValue; +} + +- (void)setByValue:(id)aValue +{ + _byValue = aValue; +} + +- (id)byValue +{ + return _byValue; +} + +@end diff --git a/AppKit/CoreAnimation/CABackingStore.j b/AppKit/CoreAnimation/CABackingStore.j new file mode 100644 index 0000000000..b70bd44cf7 --- /dev/null +++ b/AppKit/CoreAnimation/CABackingStore.j @@ -0,0 +1,72 @@ +/* + * CABackingStore.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CGGeometry.j" +import "CPCompatibility.j" + +#define PIXEL(pixels) pixels + "px"; + +function CABackingStoreGetContext(aBackingStore) +{ + return aBackingStore.context; +} + +if (CPFeatureIsCompatible(CPHTMLCanvasFeature)) +{ + +CABackingStoreCreate = function() +{ + var DOMElement = document.createElement("canvas"); + + DOMElement.style.position = "absolute"; + + // FIXME: Consolidate drawImage to support this. + return { context:DOMElement.getContext("2d"), buffer:DOMElement, _image:DOMElement }; +} + +CABackingStoreSetSize = function(aBackingStore, aSize) +{ + var buffer = aBackingStore.buffer; + + buffer.width = aSize.width; + buffer.height = aSize.height; + buffer.style.width = PIXEL(aSize.width); + buffer.style.height = PIXEL(aSize.height); +} +} +else +{ + +CABackingStoreCreate = function() +{ + var context = CGBitmapGraphicsContextCreate(); + + context.buffer = ""; + + return { context:context }; +} + +CABackingStoreSetSize = function(aBackingStore, aSize) +{ +} + +} \ No newline at end of file diff --git a/AppKit/CoreAnimation/CAFlashLayer.j b/AppKit/CoreAnimation/CAFlashLayer.j new file mode 100644 index 0000000000..e7a5bdfbac --- /dev/null +++ b/AppKit/CoreAnimation/CAFlashLayer.j @@ -0,0 +1,50 @@ +/* + * CAFlashLayer.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CALayer.j" +import "CPFlashMovie.j" + + +@implementation CAFlashLayer : CALayer +{ + CPFlashMovie _flashMovie; +} + +- (void)setFlashMovie:(CPFlashMovie)aFlashMovie +{ + if (_flashMovie == aFlashMovie) + return; + + _flashMovie = aFlashMovie; + + _DOMElement.innerHTML = ""; +} + +- (CPFlashMovie)flashMovie +{ + return _flashMovie; +} + +@end diff --git a/AppKit/CoreAnimation/CALayer.j b/AppKit/CoreAnimation/CALayer.j new file mode 100644 index 0000000000..531934f579 --- /dev/null +++ b/AppKit/CoreAnimation/CALayer.j @@ -0,0 +1,980 @@ +/* + * CALayer.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import + +import +import + +#include "../CoreGraphics/CGGeometry.h" +#include "../CoreGraphics/CGAffineTransform.j" + + +#define DOM(aLayer) aLayer._DOMElement + +var CALayerGeometryBoundsMask = 1, + CALayerGeometryPositionMask = 2, + CALayerGeometryAnchorPointMask = 4, + CALayerGeometryAffineTransformMask = 8, + CALayerGeometryParentSublayerTransformMask = 16; +var USE_BUFFER = NO; + +var CALayerFrameOriginUpdateMask = 1, + CALayerFrameSizeUpdateMask = 2, + CALayerZPositionUpdateMask = 4, + CALayerDisplayUpdateMask = 8, + CALayerCompositeUpdateMask = 16, + CALayerDOMUpdateMask = CALayerZPositionUpdateMask | CALayerFrameOriginUpdateMask | CALayerFrameSizeUpdateMask; + +var CALayerRegisteredRunLoopUpdates = nil; + +@implementation CALayer : CPObject +{ + // Modifying the Layer Geometry + + CGRect _frame; + CGRect _bounds; + CGPoint _position; + unsigned _zPosition; + CGPoint _anchorPoint; + + CGAffineTransform _affineTransform; + CGAffineTransform _sublayerTransform; + CGAffineTransform _sublayerTransformForSublayers; + + CGRect _backingStoreFrame; + CGRect _standardBackingStoreFrame; + + BOOL _hasSublayerTransform; + BOOL _hasCustomBackingStoreFrame; + + // Style Attributes + + float _opacity; + BOOL _isHidden; + CPColor _backgroundColor; + + // Managing Layer Hierarchy + + CALayer _superlayer; + CPMutableArray _sublayers; + + // Updating Layer Display + + unsigned _runLoopUpdateMask; + BOOL _needsDisplayOnBoundsChange; + + // Modifyin the Delegate + + id _delegate; + + BOOL _delegateRespondsToDisplayLayerSelector; + BOOL _delegateRespondsToDrawLayerInContextSelector; + + // DOM Implementation + + DOMElement _DOMElement; + DOMElement _DOMContentsElement; + id _contents; + CGContext _context; + CPView _owningView; + + CGAffineTransform _transformToLayer; + CGAffineTransform _transformFromLayer; +} + ++ (CALayer)layer +{ + return [[[self class] alloc] init]; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + _frame = CGRectMakeZero(); + + _backingStoreFrame = CGRectMakeZero(); + _standardBackingStoreFrame = CGRectMakeZero(); + + _bounds = CGRectMakeZero(); + _position = CGPointMakeZero(); + _zPosition = 0.0; + _anchorPoint = CGPointMake(0.5, 0.5); + _affineTransform = CGAffineTransformMakeIdentity(); + _sublayerTransform = CGAffineTransformMakeIdentity(); + + _transformToLayer = CGAffineTransformMakeIdentity(); // FIXME? does it matter? + _transformFromLayer = CGAffineTransformMakeIdentity(); + + _opacity = 1.0; + _isHidden = NO; + _masksToBounds = NO; + + _sublayers = []; + + _DOMElement = document.createElement("div"); + + _DOMElement.style.overflow = "visible"; + _DOMElement.style.position = "absolute"; + _DOMElement.style.visibility = "visible"; + _DOMElement.style.top = "0px"; + _DOMElement.style.left = "0px"; + _DOMElement.style.zIndex = 0; + _DOMElement.style.width = "0px"; + _DOMElement.style.height = "0px"; + } + + return self; +} + +// Modifying the Layer Geometry + +- (void)setBounds:(CGRect)aBounds +{ + if (CGRectEqualToRect(_bounds, aBounds)) + return; + + var oldOrigin = _bounds.origin; + + _bounds = _CGRectMakeCopy(aBounds); + + if (_hasSublayerTransform) + _CALayerUpdateSublayerTransformForSublayers(self); + + // _hasSublayerTransform == true will handle this for us. + /*else if (!CGPointEqualToPoint(_bounds.origin, oldOrigin)) + { + var index = _sublayers.length; + + // FIXME: This should climb the layer tree down. + while (index--) + _CALayerRecalculateGeometry(_sublayers[index], CALayerGeometryPositionMask); + }*/ + + _CALayerRecalculateGeometry(self, CALayerGeometryBoundsMask); +} + +- (CGRect)bounds +{ + return _bounds; +} + +- (void)setPosition:(CGPoint)aPosition +{ + if (CGPointEqualToPoint(_position, aPosition)) + return; + + _position = _CGPointMakeCopy(aPosition); + + _CALayerRecalculateGeometry(self, CALayerGeometryPositionMask); +} + +- (CGPoint)position +{ + return _position; +} + +- (void)setZPosition:(int)aZPosition +{ + if (_zPosition == aZPosition) + return; + + _zPosition = aZPosition; + + [self registerRunLoopUpdateWithMask:CALayerZPositionUpdateMask]; +} + +- (void)setAnchorPoint:(CGPoint)anAnchorPoint +{ + anAnchorPoint = _CGPointMakeCopy(anAnchorPoint); + anAnchorPoint.x = MIN(1.0, MAX(0.0, anAnchorPoint.x)); + anAnchorPoint.y = MIN(1.0, MAX(0.0, anAnchorPoint.y)); + + if (CGPointEqualToPoint(_anchorPoint, anAnchorPoint)) + return; + + _anchorPoint = anAnchorPoint; + + if (_hasSublayerTransform) + _CALayerUpdateSublayerTransformForSublayers(self); + + if (_owningView) + _position = CGPointMake(_CGRectGetWidth(_bounds) * _anchorPoint.x, _CGRectGetHeight(_bounds) * _anchorPoint.y); + + _CALayerRecalculateGeometry(self, CALayerGeometryAnchorPointMask); +} + +- (CGPoint)anchorPoint +{ + return _anchorPoint; +} + +- (void)setAffineTransform:(CGAffineTransform)anAffineTransform +{ + if (CGAffineTransformEqualToTransform(_affineTransform, anAffineTransform)) + return; + + _affineTransform = _CGAffineTransformMakeCopy(anAffineTransform); + + _CALayerRecalculateGeometry(self, CALayerGeometryAffineTransformMask); +} + +- (CGAffineTransform)affineTransform +{ + return _affineTransform; +} + +- (void)setSublayerTransform:(CGAffineTransform)anAffineTransform +{ + if (CGAffineTransformEqualToTransform(_sublayerTransform, anAffineTransform)) + return; + + var hadSublayerTransform = _hasSublayerTransform; + + _sublayerTransform = _CGAffineTransformMakeCopy(anAffineTransform); + _hasSublayerTransform = !_CGAffineTransformIsIdentity(_sublayerTransform); + + if (_hasSublayerTransform) + { + _CALayerUpdateSublayerTransformForSublayers(self); + + var index = _sublayers.length; + + // FIXME: This should climb the layer tree down. + while (index--) + _CALayerRecalculateGeometry(_sublayers[index], CALayerGeometryParentSublayerTransformMask); + } +} + +- (CGAffineTransform)sublayerTransform +{ + return _sublayerTransform; +} + +- (CGAffineTransform)transformToLayer +{ + return _transformToLayer; +} + +- (void)setFrame:(CGRect)aFrame +{ + alert("FIXME IMPLEMENT"); +} + +// The frame defines the bounding box of the layer: the smallest +// possible rectangle that could fit this layer after transform +// properties are applied in superlayer coordinates. +- (CGRect)frame +{ + if (!_frame) + _frame = [self convertRect:_bounds toLayer:_superlayer]; + + return _frame; +} + +// The Backing Store Frame specifies the frame of the actual backing +// store used to contain this layer. Naturally, by default it is the +// same as the frame, however, users can specify their own custom +// Backing Store Frame in order to speed up certain operations, such as +// live transformation. +- (CGRect)backingStoreFrame +{ + return _backingStoreFrame; +} + +- (void)setBackingStoreFrame:(CGRect)aFrame +{ + _hasCustomBackingStoreFrame = (aFrame != nil); + + if (aFrame == nil) + aFrame = CGRectMakeCopy(_standardBackingStoreFrame); + else + { + if (_superlayer) + { + aFrame = [_superlayer convertRect:aFrame toLayer:nil]; + + var bounds = [_superlayer bounds], + frame = [_superlayer convertRect:bounds toLayer:nil]; + + aFrame.origin.x -= _CGRectGetMinX(frame); + aFrame.origin.y -= _CGRectGetMinY(frame); + } + else + aFrame = CGRectMakeCopy(aFrame); + } + + if (!CGPointEqualToPoint(_backingStoreFrame.origin, aFrame.origin)) + [self registerRunLoopUpdateWithMask:CALayerFrameOriginUpdateMask]; + + if (!CGSizeEqualToSize(_backingStoreFrame.size, aFrame.size)) + [self registerRunLoopUpdateWithMask:CALayerFrameSizeUpdateMask]; + + _backingStoreFrame = aFrame; +} + +// Providing Layer Content + +- (CGImage)contents +{ + return _contents; +} + +- (void)setContents:(CGImage)contents +{ + if (_contents == contents) + return; + + _contents = contents; + + [self composite]; +} + +- (void)composite +{ + if (USE_BUFFER && !_contents || !_context) + return; + + CGContextClearRect(_context, _CGRectMake(0.0, 0.0, _CGRectGetWidth(_backingStoreFrame), _CGRectGetHeight(_backingStoreFrame))); + + // Recomposite + var transform = _transformFromLayer; + + if (_superlayer) + { + var superlayerTransform = _CALayerGetTransform(_superlayer, nil), + superlayerOrigin = CGPointApplyAffineTransform(_superlayer._bounds.origin, superlayerTransform); + + transform = CGAffineTransformConcat(transform, superlayerTransform); + + transform.tx -= superlayerOrigin.x; + transform.ty -= superlayerOrigin.y; + } + + transform.tx -= _CGRectGetMinX(_backingStoreFrame); + transform.ty -= _CGRectGetMinY(_backingStoreFrame); + + CGContextSaveGState(_context); + CGContextConcatCTM(_context, transform);//_transformFromView); + if (USE_BUFFER) + { +// CGContextDrawImage(_context, _bounds, _contents.context); + _context.drawImage(_contents.buffer, _CGRectGetMinX(_bounds), _CGRectGetMinY(_bounds));//, _CGRectGetWidth(_standardBackingStoreFrame), _CGRectGetHeight(_standardBackingStoreFrame)); + } + else + [self drawInContext:_context]; + CGContextRestoreGState(_context); +} + +- (void)display +{ + if (!_context) + { + _context = CGBitmapGraphicsContextCreate(); + + _DOMContentsElement = _context.DOMElement; + + _DOMContentsElement.style.zIndex = -100; + + _DOMContentsElement.style.overflow = "hidden"; + _DOMContentsElement.style.position = "absolute"; + _DOMContentsElement.style.visibility = "visible"; + + _DOMContentsElement.width = ROUND(_CGRectGetWidth(_backingStoreFrame)); + _DOMContentsElement.height = ROUND(_CGRectGetHeight(_backingStoreFrame)); + + _DOMContentsElement.style.top = "0px"; + _DOMContentsElement.style.left = "0px"; + _DOMContentsElement.style.width = ROUND(_CGRectGetWidth(_backingStoreFrame)) + "px"; + _DOMContentsElement.style.height = ROUND(_CGRectGetHeight(_backingStoreFrame)) + "px"; + + _DOMElement.appendChild(_DOMContentsElement); + } + + if (USE_BUFFER) + { + if (_delegateRespondsToDisplayLayerSelector) + return [self displayInLayer:self]; + + if (_CGRectGetWidth(_backingStoreFrame) == 0.0 || _CGRectGetHeight(_backingStoreFrame) == 0.0) + return; + + if (!_contents) + _contents = CABackingStoreCreate(); + + CABackingStoreSetSize(_contents, _bounds.size); + + [self drawInContext:CABackingStoreGetContext(_contents)]; + } + + [self composite]; +} + +- (void)drawInContext:(CGContext)aContext +{ //if (!window.loop || window.nodisplay) CPLog.error("htiasd"); + if (_backgroundColor) + { + CGContextSetFillColor(aContext, _backgroundColor); + CGContextFillRect(aContext, _bounds); + } + + if (_delegateRespondsToDrawLayerInContextSelector) + [_delegate drawLayer:self inContext:aContext]; +} + + +// Style Attributes + +- (float)opacity +{ + return _opacity; +} + +- (void)setOpacity:(float)anOpacity +{ + if (_opacity == anOpacity) + return; + + _opacity = anOpacity; + + _DOMElement.style.opacity = anOpacity; + _DOMElement.style.filter = "alpha(opacity=" + anOpacity * 100 + ")"; +} + +- (void)setHidden:(BOOL)isHidden +{ + _isHidden = isHidden; + _DOMElement.style.display = isHidden ? "none" : "block"; +} + +- (BOOL)hidden +{ + return _isHidden; +} + +- (BOOL)isHidden +{ + return _isHidden; +} + +- (void)setMasksToBounds:(BOOL)masksToBounds +{ + if (_masksToBounds == masksToBounds) + return; + + _masksToBounds = masksToBounds; + _DOMElement.style.overflow = _masksToBounds ? "hidden" : "visible"; +} + +- (void)setBackgroundColor:(CPColor)aColor +{ + _backgroundColor = aColor; + + [self setNeedsDisplay]; +} + +- (CPColor)backgroundColor +{ + return _backgroundColor; +} + +// Managing Layer Hierarchy + +- (CPArray)sublayers +{ + return _sublayers; +} + +- (CALayer)superlayer +{ + return _superlayer; +} + +#define ADJUST_CONTENTS_ZINDEX(aLayer)\ +if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)\ + _DOMContentsElement.style.zIndex -= 100.0;\ + +- (void)addSublayer:(CALayer)aLayer +{ + [self insertSublayer:aLayer atIndex:_sublayers.length]; + return; + ADJUST_CONTENTS_ZINDEX(aLayer); + + [_sublayers addObject:aLayer]; + _DOMElement.appendChild(DOM(aLayer)); +} + +- (void)removeFromSuperlayer +{ + if (_owningView) + [_owningView setLayer:nil]; + + if (!_superlayer) + return; + + _superlayer._DOMElement.removeChild(_DOMElement); + [_superlayer._sublayers removeObject:self]; + + _superlayer = nil; +} + +- (void)insertSublayer:(CALayer)aLayer atIndex:(unsigned)anIndex +{ + if (!aLayer) + return; + + var superlayer = [aLayer superlayer]; + + if (superlayer == self) + { + var index = [_sublayers indexOfObjectIdenticalTo:aLayer]; + + if (index == anIndex) + return; + + [_sublayers removeObjectAtIndex:index]; + + if (index < anIndex) + --anIndex; + } + else if (superlayer != nil) + [aLayer removeFromSuperlayer]; + + ADJUST_CONTENTS_ZINDEX(aLayer); + + [_sublayers insertObject:aLayer atIndex:anIndex]; + + if (anIndex >= _sublayers.length - 1) + _DOMElement.appendChild(DOM(aLayer)); + else + _DOMElement.insertBefore(DOM(aLayer), _sublayers[anIndex + 1]._DOMElement); + + aLayer._superlayer = self; + + if (self != superlayer) + _CALayerRecalculateGeometry(aLayer, 0xFFFFFFF); +} + +- (void)insertSublayer:(CALayer)aLayer below:(CALayer)aSublayer +{ + var index = aSublayer ? [_sublayers indexOfObjectIdenticalTo:aSublayer] : 0; + + [self insertSublayer:aLayer atIndex:index == CPNotFound ? _sublayers.length : index]; +} + +- (void)insertSublayer:(CALayer)aLayer after:(CALayer)aSublayer +{ + var index = aSublayer ? [_sublayers indexOfObjectIdenticalTo:aSublayer] : _sublayers.length; + + [_sublayers insertObject:aLayer atIndex:index == CPNotFound ? _sublayers.length : index + 1]; +} + +- (void)replaceSublayer:(CALayer)aSublayer with:(CALayer)aLayer +{ + if (aSublayer == aLayer) + return; + + // FIXME: EXCEPTION + if (aSublayer._superlayer != self) + { + alert("EXCEPTION"); + return; + } + + ADJUST_CONTENTS_ZINDEX(aLayer); + + [_sublayers replaceObjectAtIndex:[_sublayers indexOfObjectIdenticalTo:aSublayer] withObject:aLayer]; + _DOMElement.replaceChild(DOM(aSublayer), DOM(aLayer)); +} + +// Updating Layer Display + ++ (void)runLoopUpdateLayers +{if (window.oops) {alert(window.latest); objj_debug_print_backtrace();} + window.loop = true; + for (hash in CALayerRegisteredRunLoopUpdates) + { + var layer = CALayerRegisteredRunLoopUpdates[hash], + mask = layer._runLoopUpdateMask; + + if (mask & CALayerDOMUpdateMask) + _CALayerUpdateDOM(layer, mask); + + if (mask & CALayerDisplayUpdateMask) + [layer display]; + + else if (mask & CALayerFrameSizeUpdateMask || mask & CALayerCompositeUpdateMask) + [layer composite]; + + layer._runLoopUpdateMask = 0; + } + window.loop= false; + CALayerRegisteredRunLoopUpdates = nil; +} + +- (void)registerRunLoopUpdateWithMask:(unsigned)anUpdateMask +{ + if (CALayerRegisteredRunLoopUpdates == nil) + { + CALayerRegisteredRunLoopUpdates = {}; + + [[CPRunLoop currentRunLoop] performSelector:@selector(runLoopUpdateLayers) + target:CALayer argument:nil order:0 modes:[CPDefaultRunLoopMode]]; + } + + _runLoopUpdateMask |= anUpdateMask; + CALayerRegisteredRunLoopUpdates[[self hash]] = self; +} + +- (void)setNeedsComposite +{ + [self registerRunLoopUpdateWithMask:CALayerCompositeUpdateMask]; +} + +- (void)setNeedsDisplay +{ + [self registerRunLoopUpdateWithMask:CALayerDisplayUpdateMask]; +} + +- (void)setNeedsDisplayOnBoundsChange:(BOOL)needsDisplayOnBoundsChange +{ + _needsDisplayOnBoundsChange = needsDisplayOnBoundsChange; +} + +- (BOOL)needsDisplayOnBoundsChange +{ + return _needsDisplayOnBoundsChange; +} + +- (void)setNeedsDisplayInRect:(CGRect)aRect +{ + _dirtyRect = aRect; + [self display]; +} + +// Mapping Between Coordinate and Time Spaces + +- (CGPoint)convertPoint:(CGPoint)aPoint fromLayer:(CALayer)aLayer +{ + return CGPointApplyAffineTransform(aPoint, _CALayerGetTransform(aLayer, self)); +} + +- (CGPoint)convertPoint:(CGPoint)aPoint toLayer:(CALayer)aLayer +{ + return CGPointApplyAffineTransform(aPoint, _CALayerGetTransform(self, aLayer)); +} + +- (CGRect)convertRect:(CGRect)aRect fromLayer:(CALayer)aLayer +{ + return CGRectApplyAffineTransform(aRect, _CALayerGetTransform(aLayer, self)); +} + +- (CGRect)convertRect:(CGRect)aRect toLayer:(CALayer)aLayer +{ + return CGRectApplyAffineTransform(aRect, _CALayerGetTransform(self, aLayer)); +} + +// Hit Testing + +- (BOOL)containsPoint:(CGPoint)aPoint +{ + return _CGRectContainsPoint(_bounds, aPoint); +} + +- (CALayer)hitTest:(CGPoint)aPoint +{ + if (_isHidden) + return nil; + + var point = CGPointApplyAffineTransform(aPoint, _transformToLayer); + //alert(point.x + " " + point.y); + + if (!_CGRectContainsPoint(_bounds, point)) + return nil; + + var layer = nil, + index = _sublayers.length; + + // FIXME: this should take into account zPosition. + while (index--) + if (layer = [_sublayers[index] hitTest:point]) + return layer; + + return self; +} + +// Modifying the Delegate + +- (void)setDelegate:(id)aDelegate +{ + if (_delegate == aDelegate) + return; + + _delegate = aDelegate; + + _delegateRespondsToDisplayLayerSelector = [_delegate respondsToSelector:@selector(displayLayer:)]; + _delegateRespondsToDrawLayerInContextSelector = [_delegate respondsToSelector:@selector(drawLayer:inContext:)]; + + if (_delegateRespondsToDisplayLayerSelector || _delegateRespondsToDrawLayerInContextSelector) + [self setNeedsDisplay]; +} + +- (id)delegate +{ + return _delegate; +} + +- (void)_setOwningView:(CPView)anOwningView +{ + _owningView = anOwningView; + + if (_owningView) + { + _owningView = anOwningView; + + _bounds.size = CGSizeMakeCopy([_owningView bounds].size); + _position = CGPointMake(_CGRectGetWidth(_bounds) * _anchorPoint.x, _CGRectGetHeight(_bounds) * _anchorPoint.y); + } + + _CALayerRecalculateGeometry(self, CALayerGeometryPositionMask | CALayerGeometryBoundsMask); +} + +- (void)_owningViewBoundsChanged +{ + _bounds.size = CGSizeMakeCopy([_owningView bounds].size); + _position = CGPointMake(_CGRectGetWidth(_bounds) * _anchorPoint.x, _CGRectGetHeight(_bounds) * _anchorPoint.y); + + _CALayerRecalculateGeometry(self, CALayerGeometryPositionMask | CALayerGeometryBoundsMask); +} + +- (void)_update +{ + window.loop = true; + + var mask = _runLoopUpdateMask; + + if (mask & CALayerDOMUpdateMask) + _CALayerUpdateDOM(self, mask); + + if (mask & CALayerDisplayUpdateMask) + [self display]; + + else if (mask & CALayerFrameSizeUpdateMask || mask & CALayerCompositeUpdateMask) + [self composite]; + + _runLoopUpdateMask = 0; + + window.loop = false; +} + +@end + +function _CALayerUpdateSublayerTransformForSublayers(aLayer) +{ + var bounds = aLayer._bounds, + anchorPoint = aLayer._anchorPoint, + translateX = _CGRectGetWidth(bounds) * anchorPoint.x, + translateY = _CGRectGetHeight(bounds) * anchorPoint.y; + + aLayer._sublayerTransformForSublayers = CGAffineTransformConcat( + CGAffineTransformMakeTranslation(-translateX, -translateY), + CGAffineTransformConcat(aLayer._sublayerTransform, + CGAffineTransformMakeTranslation(translateX, translateY))); +} + +function _CALayerUpdateDOM(aLayer, aMask) +{ + var DOMElementStyle = aLayer._DOMElement.style; + + if (aMask & CALayerZPositionUpdateMask) + DOMElementStyle.zIndex = aLayer._zPosition; + + var frame = aLayer._backingStoreFrame; + + if (aMask & CALayerFrameOriginUpdateMask) + { + DOMElementStyle.top = ROUND(_CGRectGetMinY(frame)) + "px"; + DOMElementStyle.left = ROUND(_CGRectGetMinX(frame)) + "px"; + } + + if (aMask & CALayerFrameSizeUpdateMask) + { + var width = MAX(0.0, ROUND(_CGRectGetWidth(frame))), + height = MAX(0.0, ROUND(_CGRectGetHeight(frame))), + DOMContentsElement = aLayer._DOMContentsElement; + + DOMElementStyle.width = width + "px"; + DOMElementStyle.height = height + "px"; + + if (DOMContentsElement) + { + DOMContentsElement.width = width; + DOMContentsElement.height = height; + DOMContentsElement.style.width = width + "px"; + DOMContentsElement.style.height = height + "px"; + } + } +} + +function _CALayerRecalculateGeometry(aLayer, aGeometryChange) +{ + var bounds = aLayer._bounds, + superlayer = aLayer._superlayer, + width = _CGRectGetWidth(bounds), + height = _CGRectGetHeight(bounds), + position = aLayer._position, + anchorPoint = aLayer._anchorPoint, + affineTransform = aLayer._affineTransform, + backingStoreFrameSize = _CGSizeMakeCopy(aLayer._backingStoreFrame), + hasCustomBackingStoreFrame = aLayer._hasCustomBackingStoreFrame; + + // Go to anchor, transform, go back to bounds. + aLayer._transformFromLayer = CGAffineTransformConcat( + CGAffineTransformMakeTranslation(-width * anchorPoint.x - _CGRectGetMinX(aLayer._bounds), -height * anchorPoint.y - _CGRectGetMinY(aLayer._bounds)), + CGAffineTransformConcat(affineTransform, + CGAffineTransformMakeTranslation(position.x, position.y))); + + if (superlayer && superlayer._hasSublayerTransform) + { + // aLayer._transformFromLayer = CGAffineTransformConcat(aLayer._transformFromLayer, superlayer._sublayerTransformForSublayers); + _CGAffineTransformConcatTo(aLayer._transformFromLayer, superlayer._sublayerTransformForSublayers, aLayer._transformFromLayer); + } + + aLayer._transformToLayer = CGAffineTransformInvert(aLayer._transformFromLayer); + + //aLayer._transformFromLayer.tx = ROUND(aLayer._transformFromLayer.tx); + //aLayer._transformFromLayer.ty = ROUND(aLayer._transformFromLayer.ty); + + aLayer._frame = nil; + aLayer._standardBackingStoreFrame = [aLayer convertRect:bounds toLayer:nil]; + + if (superlayer) + { + var bounds = [superlayer bounds], + frame = [superlayer convertRect:bounds toLayer:nil]; + + aLayer._standardBackingStoreFrame.origin.x -= _CGRectGetMinX(frame); + aLayer._standardBackingStoreFrame.origin.y -= _CGRectGetMinY(frame); + } + + // We used to use CGRectIntegral here, but what we actually want, is the largest integral + // rect that would ever contain this box, since for any width/height, there are 2 (4) + // possible integral rects for it depending on it's position. It's OK that this is sometimes + // bigger than the "optimal" bounding integral rect since that doesn't change drawing. + + var origin = aLayer._standardBackingStoreFrame.origin, + size = aLayer._standardBackingStoreFrame.size; + + origin.x = FLOOR(origin.x); + origin.y = FLOOR(origin.y); + size.width = CEIL(size.width) + 1.0; + size.height = CEIL(size.height) + 1.0; + + // FIXME: This avoids the central issue that a position change is sometimes a display and sometimes + // a div move, and sometimes both. + + // Only use this frame if we don't currently have a custom backing store frame. + if (!hasCustomBackingStoreFrame) + { + var backingStoreFrame = CGRectMakeCopy(aLayer._standardBackingStoreFrame); + + // These values get rounded in the DOM, so don't both updating them if they're + // not going to be different after rounding. + if (ROUND(_CGRectGetMinX(backingStoreFrame)) != ROUND(_CGRectGetMinX(aLayer._backingStoreFrame)) || + ROUND(_CGRectGetMinY(backingStoreFrame)) != ROUND(_CGRectGetMinY(aLayer._backingStoreFrame))) + [aLayer registerRunLoopUpdateWithMask:CALayerFrameOriginUpdateMask]; + + // Any change in size due to a geometry change is purely due to rounding error. + if ((_CGRectGetWidth(backingStoreFrame) != ROUND(_CGRectGetWidth(aLayer._backingStoreFrame)) || + _CGRectGetHeight(backingStoreFrame) != ROUND(_CGRectGetHeight(aLayer._backingStoreFrame)))) + [aLayer registerRunLoopUpdateWithMask:CALayerFrameSizeUpdateMask]; + + aLayer._backingStoreFrame = backingStoreFrame; + } + + if (aGeometryChange & CALayerGeometryBoundsMask && aLayer._needsDisplayOnBoundsChange) + [aLayer setNeedsDisplay]; + // We need to recomposite if we have a custom backing store frame, OR + // If the change is not solely composed of position and anchor points changes. + // Anchor point and position changes simply move the object, requiring + // no re-rendering. + else if (hasCustomBackingStoreFrame || (aGeometryChange & ~(CALayerGeometryPositionMask | CALayerGeometryAnchorPointMask))) + [aLayer setNeedsComposite]; + + var sublayers = aLayer._sublayers, + index = 0, + count = sublayers.length; + + for (; index < count; ++index) + _CALayerRecalculateGeometry(sublayers[index], aGeometryChange); +} + +function _CALayerGetTransform(fromLayer, toLayer) +{ + var transform = CGAffineTransformMakeIdentity(); + + if (fromLayer) + { + var layer = fromLayer; + + // If we have a fromLayer, "climb up" the layer tree until + // we hit the root node or we hit the toLayer. + while (layer && layer != toLayer) + { + var transformFromLayer = layer._transformFromLayer; + + //transform = CGAffineTransformConcat(transform, layer._transformFromLayer); + _CGAffineTransformConcatTo(transform, transformFromLayer, transform); + + layer = layer._superlayer; + } + + // If we hit toLayer, then we're done. + if (layer == toLayer) + return transform; + } + + var layers = [], + layer = toLayer; + + while (layer) + { + layers.push(layer); + layer = layer._superlayer; + } + + var index = layers.length; + + while (index--) + { + var transformToLayer = layers[index]._transformToLayer; + + _CGAffineTransformConcatTo(transform, transformToLayer, transform); + } + + return transform; +} diff --git a/AppKit/CoreAnimation/CAMediaTimingFunction.j b/AppKit/CoreAnimation/CAMediaTimingFunction.j new file mode 100644 index 0000000000..eb97fd1403 --- /dev/null +++ b/AppKit/CoreAnimation/CAMediaTimingFunction.j @@ -0,0 +1,102 @@ +/* + * CAMediaTimingFunction.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import +import + + +kCAMediaTimingFunctionLinear = @"kCAMediaTimingFunctionLinear"; +kCAMediaTimingFunctionEaseIn = @"kCAMediaTimingFunctionEaseIn"; +kCAMediaTimingFunctionEaseOut = @"kCAMediaTimingFunctionEaseOut"; +kCAMediaTimingFunctionEaseInEaseOut = @"kCAMediaTimingFunctionEaseInEaseOut"; + +var CAMediaNamedTimingFunctions = nil; + +@implementation CAMediaTimingFunction : CPObject +{ + float _c1x; + float _c1y; + float _c2x; + float _c2y; +} + ++ (id)functionWithName:(CPString)aName +{ + if (!CAMediaNamedTimingFunctions) + { + CAMediaNamedTimingFunctions = [CPDictionary dictionary]; + + [CAMediaNamedTimingFunctions setObject:[CAMediaTimingFunction functionWithControlPoints:0.0 :0.0 :1.0 :1.0] forKey:kCAMediaTimingFunctionLinear]; + [CAMediaNamedTimingFunctions setObject:[CAMediaTimingFunction functionWithControlPoints:0.42 :0.0 :1.0 :1.0] forKey:kCAMediaTimingFunctionEaseIn]; + [CAMediaNamedTimingFunctions setObject:[CAMediaTimingFunction functionWithControlPoints:0.0 :0.0 :0.58 :1.0] forKey:kCAMediaTimingFunctionEaseOut]; + [CAMediaNamedTimingFunctions setObject:[CAMediaTimingFunction functionWithControlPoints:0.42 :0.0 :0.58 :1.0] forKey:kCAMediaTimingFunctionEaseInEaseOut]; + } + + return [CAMediaNamedTimingFunctions objectForKey:aName]; +} + ++ (id)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y +{ + return [[self alloc] initWithControlPoints:c1x :c1y :c2x :c2y]; +} + +- (id)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y +{ + self = [super init]; + + if (self) + { + _c1x = c1x; + _c1y = c1y; + _c2x = c2x; + _c2y = c2y; + } + + return self; +} + +- (void)getControlPointAtIndex:(unsigned)anIndex values:(float[2])reference +{ + if (anIndex == 0) + { + reference[0] = 0; + reference[1] = 0; + } + else if (anIndex == 1) + { + reference[0] = _c1x; + reference[1] = _c1y; + } + else if (anIndex == 2) + { + reference[0] = _c2x; + reference[1] = _c2y; + } + else + { + reference[0] = 1.0; + reference[0] = 1.0; + } +} + +@end diff --git a/AppKit/CoreGraphics/CGAffineTransform.h b/AppKit/CoreGraphics/CGAffineTransform.h new file mode 100644 index 0000000000..1b3fc92662 --- /dev/null +++ b/AppKit/CoreGraphics/CGAffineTransform.h @@ -0,0 +1,62 @@ +/* + * CGAffineTransform.h + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +#include "CGGeometry.h" + +#define _CGAffineTransformMake(a_, b_, c_, d_, tx_, ty_) { a:a_, b:b_, c:c_, d:d_, tx:tx_, ty:ty_ } +#define _CGAffineTransformMakeIdentity() _CGAffineTransformMake(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) +#define _CGAffineTransformMakeCopy(aTransform) _CGAffineTransformMake(aTransform.a, aTransform.b, aTransform.c, aTransform.d, aTransform.tx, aTransform.ty) + +#define _CGAffineTransformMakeScale(sx, sy) _CGAffineTransformMake(sx, 0.0, 0.0, sy, 0.0, 0.0) +#define _CGAffineTransformMakeTranslation(tx, ty) _CGAffineTransformMake(1.0, 0.0, 0.0, 1.0, tx, ty) + +#define _CGAffineTransformTranslate(aTransform, tx, ty) _CGAffineTransformMake(aTransform.a, aTransform.b, aTransform.c, aTransform.d, aTransform.tx + aTransform.a * tx + aTransform.c * ty, aTransform.ty + aTransform.b * tx + aTransform.d * ty) +#define _CGAffineTransformScale(aTransform, sx, sy) _CGAffineTransformMake(aTransform.a * sx, aTransform.b * sx, aTransform.c * sy, aTransform.d * sy, aTransform.tx, aTransform.ty) + +#define _CGAffineTransformConcat(lhs, rhs) _CGAffineTransformMake(lhs.a * rhs.a + lhs.b * rhs.c, lhs.a * rhs.b + lhs.b * rhs.d, lhs.c * rhs.a + lhs.d * rhs.c, lhs.c * rhs.b + lhs.d * rhs.d, lhs.tx * rhs.a + lhs.ty * rhs.c + rhs.tx, lhs.tx * rhs.b + lhs.ty * rhs.d + rhs.ty) + +#define _CGPointApplyAffineTransform(aPoint, aTransform) _CGPointMake(aPoint.x * aTransform.a + aPoint.y * aTransform.c + aTransform.tx, aPoint.x * aTransform.b + aPoint.y * aTransform.d + aTransform.ty) +#define _CGSizeApplyAffineTransform(aSize, aTransform) _CGSizeMake(aSize.width * aTransform.a + aSize.height * aTransform.c, aSize.width * aTransform.b + aSize.height * aTransform.d) +#define _CGRectApplyAffineTransform(aRect, aTransform) { origin:_CGPointApplyAffineTransform(aRect.origin, aTransform), size:_CGSizeApplyAffineTransform(aRect.size, aTransform) } + +#define _CGAffineTransformIsIdentity(aTransform) (aTransform.a == 1 && aTransform.b == 0 && aTransform.c == 0 && aTransform.d == 1 && aTransform.tx == 0 && aTransform.ty == 0) +#define _CGAffineTransformEqualToTransform(lhs, rhs) (lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c && lhs.d == rhs.d && lhs.tx == rhs.tx && lhs.ty == rhs.ty) + +#define _CGStringCreateWithCGAffineTransform(aTransform) (" [[ " + aTransform.a + ", " + aTransform.b + ", 0 ], [ " + aTransform.c + ", " + aTransform.d + ", 0 ], [ " + aTransform.tx + ", " + aTransform.ty + ", 1]]") + +#define _CGAffineTransformConcatTo(lhs, rhs, to) \ +var tx = lhs.tx * rhs.a + lhs.ty * rhs.c + rhs.tx;\ +to.ty = lhs.tx * rhs.b + lhs.ty * rhs.d + rhs.ty;\ +to.tx = tx;\ +var a = lhs.a * rhs.a + lhs.b * rhs.c,\ + b = lhs.a * rhs.b + lhs.b * rhs.d,\ + c = lhs.c * rhs.a + lhs.d * rhs.c;\ +to.d = lhs.c * rhs.b + lhs.d * rhs.d;\ +to.a = a;\ +to.b = b;\ +to.c = c;\ + +/* +a b 0 cos sin 0 +c d 0 -sin cos 0 +tx ty 1 0 0 1 +*/ diff --git a/AppKit/CoreGraphics/CGAffineTransform.j b/AppKit/CoreGraphics/CGAffineTransform.j new file mode 100644 index 0000000000..2bd7e39be5 --- /dev/null +++ b/AppKit/CoreGraphics/CGAffineTransform.j @@ -0,0 +1,112 @@ +/* + * CGAffineTransform.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +#include "CGGeometry.h" +#include "CGAffineTransform.h" + +#define _function(inline) function inline { return _##inline; } + +import "CGGeometry.j" + +_function(CGAffineTransformMake(a, b, c, d, tx, ty)) +_function(CGAffineTransformMakeIdentity()) +_function(CGAffineTransformMakeCopy(anAffineTransform)) + +_function(CGAffineTransformMakeScale(sx, sy)) +_function(CGAffineTransformMakeTranslation(tx, ty)) +_function(CGAffineTransformTranslate(aTransform, tx, ty)) +_function(CGAffineTransformScale(aTransform, sx, sy)) + +_function(CGAffineTransformConcat(lhs, rhs)) +_function(CGPointApplyAffineTransform(aPoint, aTransform)) +_function(CGSizeApplyAffineTransform(aSize, aTransform)) + +_function(CGAffineTransformIsIdentity(aTransform)) +_function(CGAffineTransformEqualToTransform(lhs, rhs)) + +_function(CGStringCreateWithCGAffineTransform(aTransform)) + +// FIXME: !!!! +function CGAffineTransformCreateCopy(aTransform) +{ + return _CGAffineTransformMakeCopy(aTransform); +} + +function CGAffineTransformMakeRotation(anAngle) +{ + var sin = SIN(anAngle), + cos = COS(anAngle); + + return _CGAffineTransformMake(cos, sin, -sin, cos, 0.0, 0.0); +} + +function CGAffineTransformRotate(aTransform, anAngle) +{ + var sin = SIN(anAngle), + cos = COS(anAngle); + + return { + a:aTransform.a * cos + aTransform.c * sin, + b:aTransform.b * cos + aTransform.d * sin, + c:aTransform.c * cos - aTransform.a * sin, + d:aTransform.d * cos - aTransform.b * sin, + tx:aTransform.tx, + ty:aTransform.ty + }; +} + +function CGAffineTransformInvert(aTransform) +{ + var determinant = 1 / (aTransform.a * aTransform.d - aTransform.b * aTransform.c); + + return { + a:determinant * aTransform.d, + b:-determinant * aTransform.b, + c:-determinant * aTransform.c, + d:determinant * aTransform.a, + tx:determinant * (aTransform.c * aTransform.ty - aTransform.d * aTransform.tx), + ty:determinant * (aTransform.b * aTransform.tx - aTransform.a * aTransform.ty) + }; +} + +function CGRectApplyAffineTransform(aRect, anAffineTransform) +{ + var top = _CGRectGetMinY(aRect), + left = _CGRectGetMinX(aRect), + right = _CGRectGetMaxX(aRect), + bottom = _CGRectGetMaxY(aRect), + topLeft = CGPointApplyAffineTransform(_CGPointMake(left, top), anAffineTransform), + topRight = CGPointApplyAffineTransform(_CGPointMake(right, top), anAffineTransform), + bottomLeft = CGPointApplyAffineTransform(_CGPointMake(left, bottom), anAffineTransform), + bottomRight = CGPointApplyAffineTransform(_CGPointMake(right, bottom), anAffineTransform), + minX = MIN(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x), + maxX = MAX(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x), + minY = MIN(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y), + maxY = MAX(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); + + return _CGRectMake(minX, minY, (maxX - minX), (maxY - minY)); +} + +function CPStringFromCGAffineTransform(anAffineTransform) +{ + return '{' + anAffineTransform.a + ", " + anAffineTransform.b + ", " + anAffineTransform.c + ", " + anAffineTransform.d + ", " + anAffineTransform.tx + ", " + anAffineTransform.ty + '}'; +} diff --git a/AppKit/CoreGraphics/CGColor.j b/AppKit/CoreGraphics/CGColor.j new file mode 100644 index 0000000000..a5838e87ff --- /dev/null +++ b/AppKit/CoreGraphics/CGColor.j @@ -0,0 +1,188 @@ +/* + * CGColor.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CGPattern.j" +import "CGColorSpace.j" + +// FIXME: Move this to Objective-J.js!!! +var CFTypeGlobalCount = 0; + +function CFHashCode(aCFObject) +{ + if (!aCFObject.hash) + aCFObject.hash = ++CFTypeGlobalCount; + + return aCFObject; +} + +kCGColorWhite = "kCGColorWhite"; +kCGColorBlack = "kCGColorBlack"; +kCGColorClear = "kCGColorClear"; + +var _CGColorMap = { }; + +function CGColorGetConstantColor(aColorName) +{ + alert("FIX ME"); +} + +function CGColorRetain(aColor) +{ + return aColor; +} + +function CGColorRelease() +{ +} + +function CGColorCreate(aColorSpace, components) +{ + if (!aColorSpace || !components) + return NULL; + + var components = components.slice(); + + CGColorSpaceStandardizeComponents(aColorSpace, components); + + var UID = CFHashCode(aColorSpace) + components.join(""); + + if (_CGColorMap[UID]) + return _CGColorMap[UID]; + + return _CGColorMap[UID] = { colorspace:aColorSpace, pattern:NULL, components:components }; +} + +function CGColorCreateCopy(aColor) +{ + // Colors should be treated as immutable, so don't mutate it! + return aColor; +} + +function CGColorCreateGenericGray(gray, alpha) +{ + return CGColorCreate(0, [gray, alpha]); +} + +function CGColorCreateGenericRGB(red, green, blue, alpha) +{ + return CGColorCreate(0, [red, green, blue, alpha]); +} + +function CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha) +{ + return CGColorCreate(0, [cyan, magenta, yellow, black, alpha]); +} + +function CGColorCreateCopyWithAlpha(aColor, anAlpha) +{ + var components = aColor.components; + + if (!aColor || anAlpha == components[components.length - 1]) + return aColor; + + if (aColor.pattern) + var copy = CGColorCreateWithPattern(aColor.colorspace, aColor.pattern, components); + else + var copy = CGColorCreate(aColor.colorspace, components); + + copy.components[components.length - 1] = anAlpha; + + return copy; +} + +function CGColorCreateWithPattern(aColorSpace, aPattern, components) +{ + if (!aColorSpace || !aPattern || !components) + return NULL; + + return { colorspace:aColorSpace, pattern:aPattern, components:components.slice() }; +} + +function CGColorEqualToColor(lhs, rhs) +{ + if (lhs == rhs) + return true; + + if (!lhs || !rhs) + return false; + + var lhsComponents = lhs.components, + rhsComponents = rhs.components, + lhsComponentCount = lhsComponents.length; + + if (lhsComponentCount != rhsComponents.length) + return false; + + while (lhsComponentCount--) + if (lhsComponents[lhsComponentCount] != rhsComponents[lhsComponentCount]) + return false; + + if (lhs.pattern != rhs.pattern) + return false; + + if (CGColorSpaceEqualToColorSpace(lhs.colorspace, rhs.colorspace)) + return false; + + return true; +} + +function CGColorGetAlpha(aColor) +{ + var components = aColor.components; + + return components[components.length - 1]; +} + +function CGColorGetColorSpace(aColor) +{ + return aColor.colorspace; +} + +function CGColorGetComponents(aColor) +{ + return aColor.components; +} + +function CGColorGetNumberOfComponents(aColor) +{ + return aColor.components.length; +} + +function CGColorGetPattern(aColor) +{ + return aColor.pattern; +} + +/* var components = aColor.components; + + case : _CGCSSForColor[CFGetHash(aColor)] = "rgba(" + ROUND(components[0] * 255.0) + ',' + ROUND(components[0] * 255.0) + ',' ROUND(components[0] * 255.0) + ',' + ROUND(components[0] * 255.0); + _cssString = (hasAlpha ? "rgba(" : "rgb(") + + parseInt(_components[0] * 255.0) + ", " + + parseInt(_components[1] * 255.0) + ", " + + parseInt(_components[2] * 255.0) + + (hasAlpha ? (", " + _components[3]) : "") + ")"; + +function CFStringFromColor() +{ + +} +*/ diff --git a/AppKit/CoreGraphics/CGColorSpace.j b/AppKit/CoreGraphics/CGColorSpace.j new file mode 100644 index 0000000000..6befbcf826 --- /dev/null +++ b/AppKit/CoreGraphics/CGColorSpace.j @@ -0,0 +1,212 @@ +/* + * CGColorSpace.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +kCGColorSpaceModelUnknown = -1; +kCGColorSpaceModelMonochrome = 0; +kCGColorSpaceModelRGB = 1; +kCGColorSpaceModelCMYK = 2; +kCGColorSpaceModelLab = 3; +kCGColorSpaceModelDeviceN = 4; +kCGColorSpaceModelIndexed = 5; +kCGColorSpaceModelPattern = 6; + +kCGColorSpaceGenericGray = "CGColorSpaceGenericGray"; +kCGColorSpaceGenericRGB = "CGColorSpaceGenericRGB"; +kCGColorSpaceGenericCMYK = "CGColorSpaceGenericCMYK"; +kCGColorSpaceGenericRGBLinear = "CGColorSpaceGenericRGBLinear"; +kCGColorSpaceGenericRGBHDR = "CGColorSpaceGenericRGBHDR"; +kCGColorSpaceAdobeRGB1998 = "CGColorSpaceAdobeRGB1998"; +kCGColorSpaceSRGB = "CGColorSpaceSRGB"; + +var _CGNamedColorSpaces = {}; + +#define _CGColorSpaceCreateWithModel(aModel, aComponentCount, aBaseColorSpace) \ + { model:aModel, count:aComponentCount, base:aBaseColorSpace } + +function CGColorSpaceCreateCalibratedGray(aWhitePoint, aBlackPoint, gamma) +{ + return _CGColorSpaceCreateWithModel(kCGColorSpaceModelMonochrome, 1, NULL); +} + +function CGColorSpaceCreateCalibratedRGB(aWhitePoint, aBlackPoint, gamma) +{ + return _CGColorSpaceCreateWithModel(kCGColorSpaceModelRGB, 1, NULL); +} + +function CGColorSpaceCreateICCBased(aComponentCount, range, profile, alternate) +{ + // FIXME: Do we need to support this? + return NULL; +} + +function CGColorSpaceCreateLab(aWhitePoint, aBlackPoint, aRange) +{ + // FIXME: Do we need to support this? + return NULL; +} + +function CGColorSpaceCreateDeviceCMYK() +{ + return CGColorSpaceCreateWithName(kCGColorSpaceGenericCMYK); +} + +function CGColorSpaceCreateDeviceGray() +{ + return CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); +} + +function CGColorSpaceCreateDeviceRGB() +{ + return CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); +} + +function CGColorSpaceCreateWithPlatformColorSpace() +{ + // FIXME: This for sure we don't need. + return NULL; +} + +function CGColorSpaceCreateIndexed(aBaseColorSpace, lastIndex, colorTable) +{ + // FIXME: Do we need to support this? + return NULL; +} + +function CGColorSpaceCreatePattern(aBaseColorSpace) +{ + if (aBaseColorSpace) + return _CGColorSpaceCreateWithModel(kCGColorSpaceModelPattern, aBaseColorSpace.count, aBaseColorSpace); + + return _CGColorSpaceCreateWithModel(kCGColorSpaceModelPattern, 0, NULL); +} + +function CGColorSpaceCreateWithName(aName) +{ + var colorSpace = _CGNamedColorSpaces[aName]; + + if (colorSpace) + return colorSpace; + + switch (aName) + { + case kCGColorSpaceGenericGray: return _CGNamedColorSpaces[aName] = _CGColorSpaceCreateWithModel(kCGColorSpaceModelMonochrome, 1, NULL); + case kCGColorSpaceGenericRGB: return _CGNamedColorSpaces[aName] = _CGColorSpaceCreateWithModel(kCGColorSpaceModelRGB, 3, NULL); + case kCGColorSpaceGenericCMYK: return _CGNamedColorSpaces[aName] = _CGColorSpaceCreateWithModel(kCGColorSpaceModelCMYK, 4, NULL); + case kCGColorSpaceGenericRGBLinear: return _CGNamedColorSpaces[aName] = _CGColorSpaceCreateWithModel(kCGColorSpaceModelRGB, 3, NULL); + case kCGColorSpaceGenericRGBHDR: return _CGNamedColorSpaces[aName] = _CGColorSpaceCreateWithModel(kCGColorSpaceModelRGB, 3, NULL); + case kCGColorSpaceAdobeRGB1998: return _CGNamedColorSpaces[aName] = _CGColorSpaceCreateWithModel(kCGColorSpaceModelRGB, 3, NULL); + case kCGColorSpaceSRGB: return _CGNamedColorSpaces[aName] = _CGColorSpaceCreateWithModel(kCGColorSpaceModelRGB, 3, NULL); + } + + return NULL; +} + +// Getting Information About Color Spaces + +function CGColorSpaceCopyICCProfile(aColorSpace) +{ + return NULL; +} + +function CGColorSpaceGetNumberOfComponents(aColorSpace) +{ + return aColorSpace.count; +} + +function CGColorSpaceGetTypeID(aColorSpace) +{ +} + +function CGColorSpaceGetModel(aColorSpace) +{ + return aColorSpace.model; +} + +function CGColorSpaceGetBaseColorSpace(aColorSpace) +{ +} + +function CGColorSpaceGetColorTableCount(aColorSpace) +{ +} + +function CGColorSpaceGetColorTable(aColorSpace) +{ +} + +// Retaining and Releasing Color Spaces + +function CGColorSpaceRelease(aColorSpace) +{ +} + +function CGColorSpaceRetain(aColorSpace) +{ + return aColorSpace; +} + +// FIXME: We should refer to some default values. +#define STANDARDIZE(components, index, minimum, maximum, multiplier) \ +{ \ + if (index > components.length) \ + { \ + components[index] = maximum; \ + return; \ + } \ +\ + var component = components[index]; \ + \ + if (component < minimum) \ + components[index] = minimum; \ + else if (component > maximum) \ + components[index] = maximum; \ + else \ + components[index] = ROUND(component * multiplier) / multiplier; \ +} + +function CGColorSpaceStandardizeComponents(aColorSpace, components) +{ + var count = aColorSpace.count; + + // Standardize the alpha value. We allow the alpha value to have a + // higher precision than other components since it is not ultimately + // bound to 256 bits like RGB. + STANDARDIZE(components, count, 0, 1, 1000); + + if (aColorSpace.base) + aColorSpace = aColorSpace.base; + + switch (aColorSpace.model) + { + case kCGColorSpaceModelMonochrome: + case kCGColorSpaceModelRGB: + case kCGColorSpaceModelCMYK: + case kCGColorSpaceModelDeviceN: while (count--) + STANDARDIZE(components, count, 0, 1, 255); + break; + + // We don't currently support these color spaces. + case kCGColorSpaceModelIndexed: + case kCGColorSpaceModelLab: + case kCGColorSpaceModelPattern: break; + } +} diff --git a/AppKit/CoreGraphics/CGContext.j b/AppKit/CoreGraphics/CGContext.j new file mode 100644 index 0000000000..ed73a05391 --- /dev/null +++ b/AppKit/CoreGraphics/CGContext.j @@ -0,0 +1,438 @@ +/* + * CGContext.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +#include "CGGeometry.h" +#include "CGAffineTransform.h" + +import "CGGeometry.j" +import "CGAffineTransform.j" +import "CGPath.j" + +kCGLineCapButt = 0; +kCGLineCapRound = 1; +kCGLineCapSquare = 2; + +kCGLineJoinMiter = 0; +kCGLineJoinRound = 1; +kCGLineJoinBevel = 2; + +kCGPathFill = 0; +kCGPathEOFill = 1; +kCGPathStroke = 2; +kCGPathFillStroke = 3; +kCGPathEOFillStroke = 4; + +kCGBlendModeNormal = 0; +kCGBlendModeMultiply = 1; +kCGBlendModeScreen = 2; +kCGBlendModeOverlay = 3; +kCGBlendModeDarken = 4; +kCGBlendModeLighten = 5; +kCGBlendModeColorDodge = 6; +kCGBlendModeColorBurn = 7; +kCGBlendModeSoftLight = 8; +kCGBlendModeHardLight = 9; +kCGBlendModeDifference = 10; +kCGBlendModeExclusion = 11; +kCGBlendModeHue = 12; +kCGBlendModeSaturation = 13; +kCGBlendModeColor = 14; +kCGBlendModeLuminosity = 15; +kCGBlendModeClear = 16; +kCGBlendModeCopy = 17; +kCGBlendModeSourceIn = 18; +kCGBlendModeSourceOut = 19; +kCGBlendModeSourceAtop = 20; +kCGBlendModeDestinationOver = 21; +kCGBlendModeDestinationIn = 22; +kCGBlendModeDestinationOut = 23; +kCGBlendModeDestinationAtop = 24; +kCGBlendModeXOR = 25; +kCGBlendModePlusDarker = 26; +kCGBlendModePlusLighter = 27; + +function CGContextRelease() +{ +} + +function CGContextRetain(aContext) +{ + return aContext; +} + +if (!CPFeatureIsCompatible(CPHTMLCanvasFeature)) +{ + +function CGGStateCreate() +{ + return { strokeStyle:"#000", fillStyle:"#ccc", lineWidth:1.0, lineJoin:kCGLineJoinMiter, lineCap:kCGLineCapButt, miterLimit:10.0, globalAlpha:1.0, + blendMode:kCGBlendModeNormal, + shadowOffset:_CGSizeMakeZero(), shadowBlur:0.0, shadowColor:NULL, CTM:_CGAffineTransformMakeIdentity() }; +} + +function CGGStateCreateCopy(aGState) +{ + return { strokeStyle:aGState.strokeStyle, fillStyle:aGState.fillStyle, lineWidth:aGState.lineWidth, + lineJoin:aGState.lineJoin, lineCap:aGState.lineCap, miterLimit:aGState.miterLimit, globalAlpha:aGState.globalAlpha, + blendMode:aGState.blendMode, + shadowOffset:aGState.shadowOffset, shadowBlur:aGState.shadowBlur, shadowColor:aGState.shadowColor, CTM:_CGAffineTransformMakeCopy(aGState.CTM) }; +} + +function CGBitmapGraphicsContextCreate() +{ + return { DOMElement:document.createElement("div"), path:NULL, gState:CGGStateCreate(), gStateStack:[] }; +} + +function CGContextSaveGState(aContext) +{ + aContext.gStateStack.push(CGGStateCreateCopy(aContext.gState)); +} + +function CGContextRestoreGState(aContext) +{ + aContext.gState = aContext.gStateStack.pop(); +} + +function CGContextSetLineCap(aContext, aLineCap) +{ + aContext.gState.lineCap = aLineCap; +} + +function CGContextSetLineJoin(aContext, aLineJoin) +{ + aContext.gState.lineJoin = aLineJoin; +} + +function CGContextSetLineWidth(aContext, aLineWidth) +{ + aContext.gState.lineWidth = aLineWidth; +} + +function CGContextSetMiterLimit(aContext, aMiterLimit) +{ + aContext.gState.miterLimit = aMiterLimit; +} + +function CGContextSetBlendMode(aContext, aBlendMode) +{ + aContext.gState.blendMode = aBlendMode; +} + +function CGContextAddArc(aContext, x, y, radius, startAngle, endAngle, clockwise) +{ + CGPathAddArc(aContext.path, aContext.gState.CTM, x, y, radius, startAngle, endAngle, clockwise); +} + +function CGContextAddArcToPoint(aContext, x1, y1, x2, y2, radius) +{ + CGPathAddArcToPoint(aContext.path, aContext.gState.CTM, x1, y1, x2, y2, radius); +} + +function CGContextAddCurveToPoint(aContext, cp1x, cp1y, cp2x, cp2y, x, y) +{ + CGPathAddCurveToPoint(aContext.path, aContext.gState.CTM, cp1x, cp1y, cp2x, cp2y, x, y); +} + +function CGContextAddLines(aContext, points, count) +{ + CGPathAddLines(aContext.path, aContext.gState.CTM, points, count); +} + +function CGContextAddLineToPoint(aContext, x, y) +{ + CGPathAddLineToPoint(aContext.path, aContext.gState.CTM, x, y); +} + +function CGContextAddPath(aContext, aPath) +{ + if (!aContext || CGPathIsEmpty(aPath)) + return; + + if (!aContext.path) + aContext.path = CGPathCreateMutable(); + + CGPathAddPath(aContext.path, aContext.gState.CTM, aPath); +} + +function CGContextAddQuadCurveToPoint(aContext, cpx, cpy, x, y) +{ + CGPathAddQuadCurveToPoint(aContext.path, aContext.gState.CTM, cpx, cpy, x, y); +} + +function CGContextAddRect(aContext, aRect) +{ + CGPathAddRect(aContext.path, aContext.gState.CTM, aRect); +} + +function CGContextAddRects(aContext, rects, count) +{ + CGPathAddRects(aContext.path, aContext.gState.CTM, rects, count); +} + +function CGContextBeginPath(aContext) +{ + // This clears any previous path. + aContext.path = CGPathCreateMutable(); +} + +function CGContextClosePath(aContext) +{ + CGPathCloseSubpath(aContext.path); +} + +function CGContextMoveToPoint(aContext, x, y) +{ + if (!aContext.path) + aContext.path = CGPathCreateMutable(); + + CGPathMoveToPoint(aContext.path, aContext.gState.CTM, x, y); +} + +function CGContextFillRect(aContext, aRect) +{ + CGContextFillRects(aContext, [aRect], 1); +} + +function CGContextFillRects(aContext, rects, count) +{ + if (arguments["count"] == NULL) + var count = rects.length; + + CGContextBeginPath(aContext); + CGContextAddRects(aContext, rects, count); + CGContextClosePath(aContext); + + CGContextDrawPath(aContext, kCGPathFill); +} + +function CGContextStrokeRect(aContext, aRect) +{ + CGContextBeginPath(aContext); + CGContextAddRect(aContext, aRect); + CGContextClosePath(aContext); + + CGContextDrawPath(aContext, kCGPathStroke); +} + +function CGContextStrokeRectWithWidth(aContext, aRect, aWidth) +{ + CGContextSaveGState(aContext); + + CGContextSetLineWidth(aContext, aWidth); + CGContextStrokeRect(aContext, aRect); + + CGContextRestoreGState(aContext); +} + +function CGContextConcatCTM(aContext, aTransform) +{ + var CTM = aContext.gState.CTM; + + _CGAffineTransformConcatTo(CTM, aTransform, CTM); +} + +function CGContextGetCTM(aContext) +{ + return aContext.gState.CTM; +} + +function CGContextRotateCTM(aContext, anAngle) +{ + var gState = aContext.gState; + + gState.CTM = CGAffineTransformRotate(gState.CTM, anAngle); +} + +function CGContextScaleCTM(aContext, sx, sy) +{ + var gState = aContext.gState; + + gState.CTM = _CGAffineTransformScale(gState.CTM, sx, sy); +} + +function CGContextTranslateCTM(aContext, tx, ty) +{ + var gState = aContext.gState; + + gState.CTM = _CGAffineTransformTranslate(gState.CTM, tx, ty); +} + +function CGContextSetShadow(aContext, aSize, aBlur) +{ + var gState = aContext.gState; + + gState.shadowOffset = _CGSizeMakeCopy(aSize); + gState.shadowBlur = aBlur; + gState.shadowColor = [CPColor shadowColor]; +} + +function CGContextSetShadowWithColor(aContext, aSize, aBlur, aColor) +{ + var gState = aContext.gState; + + gState.shadowOffset = _CGSizeMakeCopy(aSize); + gState.shadowBlur = aBlur; + gState.shadowColor = aColor; +} + +} + +// GOOD. + +function CGContextEOFillPath(aContext, aMode) +{ + CGContextDrawPath(aContext, kCGPathEOFill); +} + +function CGContextFillPath(aContext) +{ + CGContextDrawPath(aContext, kCGPathFill); +} + +var KAPPA = 4.0 * ((SQRT2 - 1.0) / 3.0); + +function CGContextAddEllipseInRect(aContext, aRect) +{ + CGContextBeginPath(aContext); + + if (_CGRectGetWidth(aRect) == _CGRectGetHeight(aRect)) + CGContextAddArc(aContext, _CGRectGetMidX(aRect), _CGRectGetMidY(aRect), _CGRectGetWidth(aRect) / 2.0, 0.0, 2 * PI, YES); + else + { + var axis = _CGSizeMake(_CGRectGetWidth(aRect) / 2.0, _CGRectGetHeight(aRect) / 2.0), + center = _CGPointMake(_CGRectGetMinX(aRect) + axis.width, _CGRectGetMinY(aRect) + axis.height); + + CGContextMoveToPoint(aContext, center.x, center.y - axis.height); + + CGContextAddCurveToPoint(aContext, center.x + (KAPPA * axis.width), center.y - axis.height, center.x + axis.width, center.y - (KAPPA * axis.height), center.x + axis.width, center.y); + CGContextAddCurveToPoint(aContext, center.x + axis.width, center.y + (KAPPA * axis.height), center.x + (KAPPA * axis.width), center.y + axis.height, center.x, center.y + axis.height); + CGContextAddCurveToPoint(aContext, center.x - (KAPPA * axis.width), center.y + axis.height, center.x - axis.width, center.y + (KAPPA * axis.height), center.x - axis.width, center.y); + CGContextAddCurveToPoint(aContext, center.x - axis.width, center.y - (KAPPA * axis.height), center.x - (KAPPA * axis.width), center.y - axis.height, center.x, center.y - axis.height); + } + + CGContextClosePath(aContext); +} + + +function CGContextFillEllipseInRect(aContext, aRect) +{ + CGContextAddEllipseInRect(aContext, aRect); + CGContextFillPath(aContext); +} + +function CGContextStrokeEllipseInRect(aContext, aRect) +{ + CGContextAddEllipseInRect(aContext, aRect); + CGContextStrokePath(aContext); +} + +function CGContextStrokePath(aContext) +{ + CGContextDrawPath(aContext, kCGPathStroke); +} + +function CGContextStrokeLineSegments(aContext, points, count) +{ + var i = 0; + + if (arguments["count"] == NULL) + var count = points.length; + + CGContextBeginPath(aContext); + + for (; i < count; i += 2) + { + CGContextMoveToPoint(aContext, points[i].x, points[i].y); + CGContextAddLineToPoint(aContext, points[i + 1].x, points[i + 1].y); + } + + CGContextStrokePath(aContext); +} + +// FIXME: THIS IS WRONG!!! + +function CGContextSetFillColor(aContext, aColor) +{ + if (aColor) + aContext.gState.fillStyle = [aColor cssString]; +} + +function CGContextSetStrokeColor(aContext, aColor) +{ + if (aColor) + aContext.gState.strokeStyle = [aColor cssString]; +} + +function CGContextFillRoundedRectangleInRect(aContext, aRect, aRadius, ne, se, sw, nw) +{ + var xMin = _CGRectGetMinX(aRect), + xMax = _CGRectGetMaxX(aRect), + yMin = _CGRectGetMinY(aRect), + yMax = _CGRectGetMaxY(aRect); + + CGContextBeginPath(aContext); + CGContextMoveToPoint(aContext, xMin + aRadius, yMin); + + if (ne) + { + CGContextAddLineToPoint(aContext, xMax - aRadius, yMin); + CGContextAddCurveToPoint(aContext, xMax - aRadius, yMin, xMax, yMin, xMax, yMin + aRadius); + } + else + CGContextAddLineToPoint(aContext, xMax, yMin); + + if (se) + { + CGContextAddLineToPoint(aContext, xMax, yMax - aRadius); + CGContextAddCurveToPoint(aContext, xMax, yMax - aRadius, xMax, yMax, xMax - aRadius, yMax); + } + else + CGContextAddLineToPoint(aContext, xMax, yMax); + + if (sw) + { + CGContextAddLineToPoint(aContext, xMin + aRadius, yMax); + CGContextAddCurveToPoint(aContext, xMin + aRadius, yMax, xMin, yMax, xMin, yMax - aRadius); + } + else + CGContextAddLineToPoint(aContext, xMin, yMax); + + if (nw) + { + CGContextAddLineToPoint(aContext, xMin, yMin + aRadius); + CGContextAddCurveToPoint(aContext, xMin, yMin + aRadius, xMin, yMin, xMin + aRadius, yMin); + } else + CGContextAddLineToPoint(aContext, xMin, yMin); + + CGContextClosePath(aContext); + + CGContextFillPath(aContext); +} + +if (CPFeatureIsCompatible(CPHTMLCanvasFeature)) +{ +#include "CGContextCanvas.j" +} +else if (CPFeatureIsCompatible(CPVMLFeature)) +{ +#include "CGContextVML.j" +} diff --git a/AppKit/CoreGraphics/CGContextCanvas.j b/AppKit/CoreGraphics/CGContextCanvas.j new file mode 100644 index 0000000000..20cfc026a5 --- /dev/null +++ b/AppKit/CoreGraphics/CGContextCanvas.j @@ -0,0 +1,433 @@ +/* + * CGContextCanvas.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +var CANVAS_LINECAP_TABLE = [ "butt", "round", "square" ], + CANVAS_LINEJOIN_TABLE = [ "miter", "round", "bevel" ], + CANVAS_COMPOSITE_TABLE = [ "source-over", "source-over", "source-over", "source-over", "darker", + "lighter", "source-over", "source-over", "source-over", "source-over", + "source-over", "source-over", "source-over", "source-over", "source-over", + "source-over", "source-over", + "copy", "source-in", "source-out", "source-atop", + "destination-over", "destination-in", "destination-out", "destination-atop", + "xor", "source-over", "source-over" ]; + +#define _CGContextAddArcCanvas(aContext, x, y, radius, startAngle, endAngle, anticlockwise) aContext.arc(x, y, radius, startAngle, endAngle, anticlockwise) +#define _CGContextAddArcToPointCanvas(aContext, x1, y1, x2, y2, radius) aContext.arcTo(x1, y1, x2, y2, radius) +#define _CGContextAddCurveToPointCanvas(aContext, cp1x, cp1y, cp2x, cp2y, x, y) aContext.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) +#define _CGContextAddLineToPointCanvas(aContext, x, y) aContext.lineTo(x, y) +#define _CGContextClosePathCanvas(aContext) aContext.closePath() +#define _CGContextMoveToPointCanvas(aContext, x, y) aContext.moveTo(x, y) + +#define _CGContextAddRectCanvas(aContext, aRect) aContext.rect(_CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect)) +#define _CGContextBeginPathCanvas(aContext) aContext.beginPath() +#define _CGContextFillRectCanvas(aContext, aRect) aContext.fillRect(_CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect)) +#define _CGContextClipCanvas(aContext) aContext.clip() + +function CGContextSaveGState(aContext) +{ + aContext.save(); +} + +function CGContextRestoreGState(aContext) +{ + aContext.restore(); +} + +function CGContextSetLineCap(aContext, aLineCap) +{ + aContext.lineCap = CANVAS_LINECAP_TABLE[aLineCap]; +} + +function CGContextSetLineJoin(aContext, aLineJoin) +{ + aContext.lineJoin = CANVAS_LINEJOIN_TABLE[aLineJoin]; +} + +function CGContextSetLineWidth(aContext, aLineWidth) +{ + aContext.lineWidth = aLineWidth; +} + +function CGContextSetMiterLimit(aContext, aMiterLimit) +{ + aContext.miterLimit = aMiterLimit; +} + +function CGContextSetBlendMode(aContext, aBlendMode) +{ + aContext.globalCompositeOperation = CANVAS_COMPOSITE_TABLE[aBlendMode]; +} + +function CGContextAddArc(aContext, x, y, radius, startAngle, endAngle, clockwise) +{ + // Despite the documentation saying otherwise, the last parameter is anti-clockwise not clockwise. + // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes#Arcs + _CGContextAddArcCanvas(aContext, x, y, radius, startAngle, endAngle, !clockwise); +} + +function CGContextAddArcToPoint(aContext, x1, y1, x2, y2, radius) +{ + _CGContextAddArcToPointCanvas(aContext, x1, y1, x2, y2, radius); +} + +function CGContextAddCurveToPoint(aContext, cp1x, cp1y, cp2x, cp2y, x, y) +{ + _CGContextAddCurveToPointCanvas(aContext, cp1x, cp1y, cp2x, cp2y, x, y); +} + +function CGContextAddLineToPoint(aContext, x, y) +{ + _CGContextAddLineToPointCanvas(aContext, x, y); +} + +function CGContextAddPath(aContext, aPath) +{ + if (!aContext || CGPathIsEmpty(aPath)) + return; + + var elements = aPath.elements, + + i = 0, + count = aPath.count; + + for (; i < count; ++i) + { + var element = elements[i], + type = element.type; + + switch (type) + { + case kCGPathElementMoveToPoint: _CGContextMoveToPointCanvas(aContext, element.x, element.y); + break; + case kCGPathElementAddLineToPoint: _CGContextLineToPoint(aContext, element.x, element.y); + break; + case kCGPathElementAddQuadCurveToPoint: _CGContextAddQuadCurveToPoint(aContext, element.cpx, element.cpy, element.x, element.y); + break; + case kCGPathElementAddCurveToPoint: _CGContextAddCurveToPointCanvas(aContext, element.cp1x, element.cp1y, element.cp2x, element.cp2y, element.x, element.y); + break; + case kCGPathElementCloseSubpath: _CGContextClosePathCanvas(aContext); + break; + case kCGPathElementAddArc: _CGContextAddArcCanvas(aContext, element.x, element.y, element.radius, element.startAngle, element.endAngle, element.clockwise); + break; + case kCGPathElementAddArcTo: //_CGContextAddArcToPointCanvas(aContext, element.cp1x, element.cp1.y, element.cp2.x, element.cp2y, element.radius); + break; + } + } +} + +function CGContextAddRect(aContext, aRect) +{ + _CGContextAddRectCanvas(aContext, aRect); +} + +function CGContextAddRects(aContext, rects, count) +{ + var i = 0; + + if (arguments["count"] == NULL) + var count = rects.length; + + for (; i < count; ++i) + { + var rect = rects[i]; + _CGContextAddRectCanvas(aContext, rect); + } +} + +function CGContextBeginPath(aContext) +{ + _CGContextBeginPathCanvas(aContext); +} + +function CGContextClosePath(aContext) +{ + _CGContextClosePathCanvas(aContext); +} + +function CGContextMoveToPoint(aContext, x, y) +{ + _CGContextMoveToPointCanvas(aContext, x, y); +} + +function CGContextClearRect(aContext, aRect) +{ + aContext.clearRect(_CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect)); +} + +function CGContextDrawPath(aContext, aMode) +{ + if (aMode == kCGPathFill || aMode == kCGPathFillStroke) + aContext.fill(); + else if (aMode == kCGPathEOFill || aMode == kCGPathEOFillStroke) + alert("not implemented!!!"); + + if (aMode == kCGPathStroke || aMode == kCGPathFillStroke || aMode == kCGPathEOFillStroke) + aContext.stroke(); +} + +function CGContextFillRect(aContext, aRect) +{ + _CGContextFillRectCanvas(aContext, aRect); +} + +function CGContextFillRects(aContext, rects, count) +{ + var i = 0; + + if (arguments["count"] == NULL) + var count = rects.length; + + for (; i < count; ++i) + { + var rect = rects[i]; + _CGContextFillRectCanvas(aContext, rect); + } +} + +function CGContextStrokeRect(aContext, aRect) +{ + aContext.strokeRect(_CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect)); +} + +function CGContextClip(aContext) +{ + _CGContextClipCanvas(aContext); +} + +function CGContextClipToRect(aContext, aRect) +{ + _CGContextBeginPathCanvas(aContext); + _CGContextAddRectCanvas(aContext, aRect); + _CGContextClosePathCanvas(aContext); + + _CGContextClipCanvas(aContext); +} + +function CGContextClipToRects(aContext, rects, count) +{ + if (arguments["count"] == NULL) + var count = rects.length; + + _CGContextBeginPathCanvas(aContext); + CGContextAddRects(aContext, rects, count); + _CGContextClipCanvas(aContext); +} + +function CGContextSetAlpha(aContext, anAlpha) +{ + aContext.globalAlpha = anAlpha; +} + +function CGContextSetFillColor(aContext, aColor) +{ + aContext.fillStyle = [aColor cssString]; +} + +function CGContextSetStrokeColor(aContext, aColor) +{ + aContext.strokeStyle = [aColor cssString]; +} + +function CGContextSetShadow(aContext, aSize, aBlur) +{ + aContext.shadowOffsetX = aSize.width; + aContext.shadowOffsetY = aSize.height; + aContext.shadowBlur = aBlur; +} + +function CGContextSetShadowWithColor(aContext, aSize, aBlur, aColor) +{ + aContext.shadowOffsetX = aSize.width; + aContext.shadowOffsetY = aSize.height; + aContext.shadowBlur = aBlur; + aContext.shadowColor = [aColor cssString]; +} + +function CGContextRotateCTM(aContext, anAngle) +{ + aContext.rotate(anAngle); +} + +function CGContextScaleCTM(aContext, sx, sy) +{ + aContext.scale(sx, sy); +} + +function CGContextTranslateCTM(aContext, tx, ty) +{ + aContext.translate(tx, ty); +} + +#define scale_rotate(a, b, c, d) \ + var sign = (a * d < 0.0 || b * c > 0.0) ? -1.0 : 1.0, \ + a2 = (ATAN2(b, d) + ATAN2(-sign * c, sign * a)) / 2.0, \ + cos = COS(a2),\ + sin = SIN(a2);\ + \ + if (cos == 0)\ + {\ + sx = -c / sin;\ + sy = b / sin;\ + }\ + else if (sin == 0)\ + {\ + sx = a / cos;\ + sy = d / cos;\ + }\ + else\ + {\ + abs_cos = ABS(cos);\ + abs_sin = ABS(sin);\ + \ + sx = (abs_cos * a / cos + abs_sin * -c / sin) / (abs_cos + abs_sin);\ + sy = (abs_cos * d / cos + abs_sin * b / sin) / (abs_cos + abs_sin);\ + }\ + +#define rotate_scale(a, b, c, d) \ + var sign = (a * d < 0.0 || b * c > 0.0) ? -1.0 : 1.0;\ + a1 = (Math.atan2(sign * b, sign * a) + Math.atan2(-c, d)) / 2.0,\ + cos = COS(a1),\ + sin = SIN(a1);\ + \ + if (cos == 0)\ + {\ + sx = b / sin;\ + sy = -c / sin;\ + }\ + else if (sin == 0)\ + {\ + sx = a / cos;\ + sy = d / cos;\ + }\ + else\ + {\ + abs_cos = ABS(cos);\ + abs_sin = ABS(sin);\ + \ + sx = (abs_cos * a / cos + abs_sin * b / sin) / (abs_cos + abs_sin);\ + sy = (abs_cos * d / cos + abs_sin * -c / sin) / (abs_cos + abs_sin);\ + }\ + +function eigen(anAffineTransform) +{ + alert("IMPLEMENT ME!"); +} + +function CGContextConcatCTM(aContext, anAffineTransform) +{ + var a = anAffineTransform.a, + b = anAffineTransform.b, + c = anAffineTransform.c, + d = anAffineTransform.d, + tx = anAffineTransform.tx, + ty = anAffineTransform.ty, + sx = 1.0, + sy = 1.0, + a1 = 0.0, + a2 = 0.0; + + // Detect the simple case of just scaling. + if (b == 0.0 && c == 0.0) + { + sx = a; + sy = d; + } + + // a scale followed by a rotate + else if (a * b == -c * d) + { + scale_rotate(a, b, c, d) + } + + // rotate, then scale. + else if (a * c == -b * d) + { + rotate_scale(a, b, c, d) + } + else + { + var transpose = CGAffineTransformMake(a, c, b, d, 0.0, 0.0), // inline + u = eigen(CGAffineTransformConcat(anAffineTransform, transpose)), + v = eigen(CGAffineTransformConcat(transpose, anAffineTransform)), + U = CGAffineTransformMake(u.vector_1.x, u.vector_2.x, u.vector_1.y, u.vector_2.y, 0.0, 0.0), // inline + VT = CGAffineTransformMake(v.vector_1.x, v.vector_1.y, v.vector_2.x, v.vector_2.y, 0.0, 0.0), + S = CGAffineTransformConcat(CGAffineTransformConcat(CGAffineTransformInvert(U), anAffineTransform), CGAffineTransformInvert(VT)); + + a = VT.a; + b = VT.b; + c = VT.c; + d = VT.d; + scale_rotate(a, b, c, d) + S.a *= sx; + S.d *= sy; + a = U.a; + b = U.b; + c = U.c; + d = U.d; + rotate_scale(a, b, c, d) + sx = S.a * sx; + sy = S.d * sy; + } + + if (tx != 0 || ty != 0) + CGContextTranslateCTM(aContext, tx, ty); + if (a1 != 0.0) + CGContextRotateCTM(aContext, a1); + if (sx != 1.0 || sy != 1.0) + CGContextScaleCTM(aContext, sx, sy); + if (a2 != 0.0) + CGContextRotateCTM(aContext, a2); +} + +function CGContextDrawImage(aContext, aRect, anImage) +{ + aContext.drawImage(anImage._image, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect)); +} + +function to_string(aColor) +{ + return "rgba(" + ROUND(aColor.components[0] * 255) + ", " + ROUND(aColor.components[1] * 255) + ", " + ROUND(255 * aColor.components[2]) + ", " + aColor.components[3] + ")"; +} + +function CGContextDrawLinearGradient(aContext, aGradient, aStartPoint, anEndPoint, options) +{ + var colors = aGradient.colors, + count = colors.length, + + linearGradient = aContext.createLinearGradient(aStartPoint.x, aStartPoint.y, anEndPoint.x, anEndPoint.y); + + while (count--) + linearGradient.addColorStop(aGradient.locations[count], to_string(colors[count])); + + aContext.fillStyle = linearGradient; + aContext.fill(); +} + +function CGBitmapGraphicsContextCreate() +{ + var DOMElement = document.createElement("canvas"), + context = DOMElement.getContext("2d"); + + context.DOMElement = DOMElement; + + return context; +} diff --git a/AppKit/CoreGraphics/CGContextVML.j b/AppKit/CoreGraphics/CGContextVML.j new file mode 100644 index 0000000000..264b205871 --- /dev/null +++ b/AppKit/CoreGraphics/CGContextVML.j @@ -0,0 +1,261 @@ +/* + * CGContextVML.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +var VML_TRUTH_TABLE = [ "f", "t"], + VML_LINECAP_TABLE = [ "flat", "round", "square" ], + VML_LINEJOIN_TABLE = [ "miter", "round", "bevel" ], + VML_ELEMENT_TABLE = [ " m ", " l ", "qb", " c ", " x ", [" at ", " wa "]]; + +var _CGBitmapGraphicsContextCreate = CGBitmapGraphicsContextCreate; + +function CGBitmapGraphicsContextCreate() +{ + // The first time around, we have to set up our environment to support vml. + document.namespaces.add("cg_vml_", "urn:schemas-microsoft-com:vml"); + document.createStyleSheet().cssText = "cg_vml_\\:*{behavior:url(#default#VML)}"; + + CGBitmapGraphicsContextCreate = _CGBitmapGraphicsContextCreate; + + return _CGBitmapGraphicsContextCreate(); +} + +// FIXME: aRect is ignored. +function CGContextClearRect(aContext, aRect) +{ + if (aContext.buffer != nil) + aContext.buffer = ""; + else + aContext.DOMElement.innerHTML = ""; + + aContext.path = NULL; +} + +var W = 10.0, + H = 10.0, + Z = 10.0, + Z_2 = Z / 2.0; + +#define COORD(aCoordinate) (ROUND(Z * (aCoordinate) - Z_2)) + +function CGContextDrawImage(aContext, aRect, anImage) +{ + var string = ""; + + if (anImage.buffer != nil) + string = anImage.buffer; + else + { + var ctm = aContext.gState.CTM, + origin = CGPointApplyAffineTransform(aRect.origin, ctm), + similarity = ctm.a == ctm.d && ctm.b == -ctm.c, + vml = [""); + + string = vml.join(""); + } + + if (aContext.buffer != nil) + aContext.buffer += string; + else + aContext.DOMElement.insertAdjacentHTML("BeforeEnd", string); +} + +function CGContextDrawPath(aContext, aMode) +{ + if (!aContext || CGPathIsEmpty(aContext.path)) + return; +var opacity = 1.0; + var elements = aContext.path.elements, + + i = 0, + count = aContext.path.count, + + gState = aContext.gState, + fill = (aMode == kCGPathFill || aMode == kCGPathFillStroke) ? 1 : 0, + stroke = (aMode == kCGPathStroke || aMode == kCGPathFillStroke) ? 1 : 0, + vml = [ "= x) + { + if (start.y < y) + start.x += 0.125; + else + start.y += 0.125; + } + else + { + if (end.y <= y) + end.x += 0.125; + else + end.y += 0.125; + } + + vml.push(VML_ELEMENT_TABLE[type][clockwise], + COORD(x - radius), ',', COORD(y - radius), " ", + COORD(x + radius), ',', COORD(y + radius), " ", + COORD(start.x), ',', COORD(start.y), " ", + COORD(end.x), ',', COORD(end.y)); + break; + case kCGPathElementAddArcTo: break; + } + + // TODO: Following is broken for curves due to + // move to proper paths. + + // Figure out dimensions so we can do gradient fills + // properly + /*if(c) { + if (min.x == null || c.x < min.x) { + min.x = c.x; + } + if (max.x == null || c.x > max.x) { + max.x = c.x; + } + if (min.y == null || c.y < min.y) { + min.y = c.y; + } + if (max.y == null || c.y > max.y) { + max.y = c.y; + } + }*/ + } + + vml.push("\">"); + + if (fill) + vml.push(""); + + if (stroke) + vml.push( ""); + + var shadowColor = gState.shadowColor; + //\"", [shadowColor cssString], "\" + if (shadowColor) + { + var shadowOffset = gState.shadowOffset; + + vml.push(""); + } + + vml.push(""); + + aContext.path = NULL; + + if (aContext.buffer != nil) + aContext.buffer += vml.join(""); + else + aContext.DOMElement.insertAdjacentHTML("BeforeEnd", vml.join("")); +} + diff --git a/AppKit/CoreGraphics/CGGeometry.h b/AppKit/CoreGraphics/CGGeometry.h new file mode 100644 index 0000000000..d12c97fc11 --- /dev/null +++ b/AppKit/CoreGraphics/CGGeometry.h @@ -0,0 +1,64 @@ +/* + * CGGeometry.h + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +#define _CGPointMake(x_, y_) { x:x_, y:y_ } +#define _CGPointMakeCopy(aPoint) _CGPointMake(aPoint.x, aPoint.y) +#define _CGPointMakeZero() _CGPointMake(0.0, 0.0) + +#define _CGPointEqualToPoint(lhsPoint, rhsPoint) (lhsPoint.x == rhsPoint.x && lhsPoint.y == rhsPoint.y) +#define _CGStringFromPoint(aPoint) ("{" + aPoint.x + ", " + aPoint.y + "}") + +#define _CGSizeMake(width_, height_) { width:width_, height:height_ } +#define _CGSizeMakeCopy(aSize) _CGSizeMake(aSize.width, aSize.height) +#define _CGSizeMakeZero() _CGSizeMake(0.0, 0.0) + +#define _CGSizeEqualToSize(lhsSize, rhsSize) (lhsSize.width == rhsSize.width && lhsSize.height == rhsSize.height) +#define _CGStringFromSize(aSize) ("{" + aSize.width + ", " + aSize.height + "}") + +#define _CGRectMake(x, y, width, height) { origin: _CGPointMake(x, y), size: _CGSizeMake(width, height) } +#define _CGRectMakeZero() _CGRectMake(0.0, 0.0, 0.0, 0.0) +#define _CGRectMakeCopy(aRect) _CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height) +#define _CGRectCreateCopy(aRect) _CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height) + +#define _CGRectEqualToRect(lhsRect, rhsRect) (_CGPointEqualToPoint(lhsRect.origin, rhsRect.origin) && _CGSizeEqualToSize(lhsRect.size, rhsRect.size)) +#define _CGStringFromRect(aRect) ("{" + _CGStringFromPoint(aRect.origin) + ", " + _CGStringFromSize(aRect.size) + "}") + +#define _CGRectOffset(aRect, dX, dY) _CGRectMake(aRect.origin.x + dX, aRect.origin.y + dY, aRect.size.width, aRect.size.height) +#define _CGRectInset(aRect, dX, dY) _CGRectMake(aRect.origin.x + dX, aRect.origin.y + dY, aRect.size.width - 2 * dX, aRect.size.height - 2 * dY) + +#define _CGRectGetHeight(aRect) (aRect.size.height) +#define _CGRectGetMaxX(aRect) (aRect.origin.x + aRect.size.width) +#define _CGRectGetMaxY(aRect) (aRect.origin.y + aRect.size.height) +#define _CGRectGetMidX(aRect) (aRect.origin.x + (aRect.size.width) / 2.0) +#define _CGRectGetMidY(aRect) (aRect.origin.y + (aRect.size.height) / 2.0) +#define _CGRectGetMinX(aRect) (aRect.origin.x) +#define _CGRectGetMinY(aRect) (aRect.origin.y) +#define _CGRectGetWidth(aRect) (aRect.size.width) + +#define _CGRectIsEmpty(aRect) (aRect.size.width <= 0.0 || aRect.size.height <= 0.0) +#define _CGRectIsNull(aRect) (aRect.size.width <= 0.0 || aRect.size.height <= 0.0) + +#define _CGRectContainsPoint(aRect, aPoint) (aPoint.x >= _CGRectGetMinX(aRect) && aPoint.y >= _CGRectGetMinY(aRect) && aPoint.x < _CGRectGetMaxX(aRect) && aPoint.y < _CGRectGetMaxY(aRect)) + +// DEPRECATED +#define _CGPointCreateCopy(aPoint) _CGPointMake(aPoint.x, aPoint.y) +#define _CGSizeCreateCopy(aSize) _CGSizeMake(aSize.width, aSize.height) diff --git a/AppKit/CoreGraphics/CGGeometry.j b/AppKit/CoreGraphics/CGGeometry.j new file mode 100644 index 0000000000..0e3aced22b --- /dev/null +++ b/AppKit/CoreGraphics/CGGeometry.j @@ -0,0 +1,173 @@ +/* + * CGGeometry.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +#include "CGGeometry.h" + +#define _function(inline) function inline { return _##inline; } + +_function(CGPointMake(x, y)) +_function(CGPointMakeZero()) +_function(CGPointMakeCopy(aPoint)) +_function(CGPointCreateCopy(aPoint)) + +_function(CGPointEqualToPoint(lhsPoint, rhsPoint)) +_function(CGStringFromPoint(aPoint)) + +_function(CGSizeMake(width, height)) +_function(CGSizeMakeZero()) +_function(CGSizeMakeCopy(aSize)) +_function(CGSizeCreateCopy(aSize)) + +_function(CGSizeEqualToSize(lhsSize, rhsSize)) +_function(CGStringFromSize(aSize)) + +_function(CGRectMake(x, y, width, height)) +_function(CGRectMakeZero()) +_function(CGRectMakeCopy(aRect)) +_function(CGRectCreateCopy(aRect)) + +_function(CGRectEqualToRect(lhsRect, rhsRect)) +_function(CGStringFromRect(aRect)) + +_function(CGRectOffset(aRect, dX, dY)) +_function(CGRectInset(aRect, dX, dY)) + +_function(CGRectGetHeight(aRect)) +_function(CGRectGetMaxX(aRect)) +_function(CGRectGetMaxY(aRect)) +_function(CGRectGetMidX(aRect)) +_function(CGRectGetMidY(aRect)) +_function(CGRectGetMinX(aRect)) +_function(CGRectGetMinY(aRect)) +_function(CGRectGetWidth(aRect)) + +_function(CGRectIsEmpty(aRect)) +_function(CGRectIsNull(aRect)) + +_function(CGRectContainsPoint(aRect, aPoint)) + +function CGRectContainsRect(lhsRect, rhsRect) +{ + var union = CGRectUnion(lhsRect, rhsRect); + + return _CGRectEqualToRect(union, lhsRect); +} + +function CGRectIntersectsRect(lhsRect, rhsRect) +{ + var intersection = CGRectIntersection(lhsRect, rhsRect); + + return !_CGRectIsEmpty(intersection); +} + +function CGRectIntegral(aRect) +{ + aRect = CGRectStandardize(aRect); + + // Store these out separately, if not the GetMaxes will return incorrect values. + var x = FLOOR(_CGRectGetMinX(aRect)), + y = FLOOR(_CGRectGetMinY(aRect)); + + aRect.size.width = CEIL(_CGRectGetMaxX(aRect)) - x; + aRect.size.height = CEIL(_CGRectGetMaxY(aRect)) - y; + + aRect.origin.x = x; + aRect.origin.y = y; + + return aRect; +} + +function CGRectIntersection(lhsRect, rhsRect) +{ + var intersection = _CGRectMake( + MAX(_CGRectGetMinX(lhsRect), _CGRectGetMinX(rhsRect)), + MAX(_CGRectGetMinY(lhsRect), _CGRectGetMinY(rhsRect)), + 0, 0); + + intersection.size.width = MIN(_CGRectGetMaxX(lhsRect), _CGRectGetMaxX(rhsRect)) - _CGRectGetMinX(intersection); + intersection.size.height = MIN(_CGRectGetMaxY(lhsRect), _CGRectGetMaxY(rhsRect)) - _CGRectGetMinY(intersection); + + return _CGRectIsEmpty(intersection) ? _CGRectMakeZero() : intersection; +} + +function CGRectStandardize(aRect) +{ + var width = _CGRectGetWidth(aRect), + height = _CGRectGetHeight(aRect), + standardized = aRect; + + if (width < 0.0) + { + if (standardized == aRect) + standardized = _CGRectMakeCopy(aRect); + + standardized.origin.x += width; + standardized.size.width = -width; + } + + if (height < 0.0) + { + if (standardized == aRect) + standardized = _CGRectMakeCopy(aRect); + + standardized.origin.y += height; + standardized.size.height = -height; + } + + return standardized; +} + +function CGRectUnion(lhsRect, rhsRect) +{ + var minX = MIN(_CGRectGetMinX(lhsRect), _CGRectGetMinX(rhsRect)), + minY = MIN(_CGRectGetMinY(lhsRect), _CGRectGetMinY(rhsRect)), + maxX = MAX(_CGRectGetMaxX(lhsRect), _CGRectGetMaxX(rhsRect)), + maxY = MAX(_CGRectGetMaxY(lhsRect), _CGRectGetMaxY(rhsRect)); + + return _CGRectMake(minX, minY, maxX - minX, maxY - minY); +} + +function CGPointFromString(aString) +{ + var comma = aString.indexOf(','); + + return { x:parseInt(aString.substr(1, comma - 1)), y:parseInt(aString.substring(comma + 1, aString.length)) }; +} + +function CGSizeFromString(aString) +{ + var comma = aString.indexOf(','); + + return { width:parseInt(aString.substr(1, comma - 1)), height:parseInt(aString.substring(comma + 1, aString.length)) }; +} + +function CGRectFromString(aString) +{ + var comma = aString.indexOf(',', aString.indexOf(',') + 1); + + return { origin:CGPointFromString(aString.substr(1, comma - 1)), size:CGSizeFromString(aString.substring(comma + 2, aString.length)) }; +} + +function CGPointFromEvent(anEvent) +{ + return _CGPointMake(anEvent.clientX, anEvent.clientY); +} diff --git a/AppKit/CoreGraphics/CGGradient.j b/AppKit/CoreGraphics/CGGradient.j new file mode 100644 index 0000000000..b46bc35ab3 --- /dev/null +++ b/AppKit/CoreGraphics/CGGradient.j @@ -0,0 +1,57 @@ +/* + * CGGradient.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CGColor.j" +import "CGColorSpace.j" + +kCGGradientDrawsBeforeStartLocation = 1 << 0; +kCGGradientDrawsAfterEndLocation = 1 << 1; + +function CGGradientCreateWithColorComponents(aColorSpace, components, locations, count) +{ + if (arguments["count"] == NULL) + var count = locations.length; + + var colors = []; + + while (count--) + { + var offset = count * 4; + colors[count] = CGColorCreate(aColorSpace, components.slice(offset, offset + 4)); + } + + return CGGradientCreateWithColors(aColorSpace, colors, locations); +} + +function CGGradientCreateWithColors(aColorSpace, colors, locations) +{ + return { colorspace:aColorSpace, colors:colors, locations:locations }; +} + +function CGGradientRelease() +{ +} + +function CGGradientRetain(aGradient) +{ + return aGradient; +} \ No newline at end of file diff --git a/AppKit/CoreGraphics/CGPath.j b/AppKit/CoreGraphics/CGPath.j new file mode 100644 index 0000000000..f06095629d --- /dev/null +++ b/AppKit/CoreGraphics/CGPath.j @@ -0,0 +1,285 @@ +/* + * CGPath.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +#include "CGGeometry.h" +#include "CGAffineTransform.h" + +import "CGGeometry.j" +import "CGAffineTransform.j" + +kCGPathElementMoveToPoint = 0; +kCGPathElementAddLineToPoint = 1; +kCGPathElementAddQuadCurveToPoint = 2; +kCGPathElementAddCurveToPoint = 3; +kCGPathElementCloseSubpath = 4; + +kCGPathElementAddArc = 5; +kCGPathElementAddArcToPoint = 6; + +function CGPathCreateMutable() +{ + return { count:0, start:NULL, current:NULL, elements:[] }; +} + +function CGPathCreateMutableCopy(aPath) +{ + var path = CGPathCreateMutable(); + + CGPathAddPath(path, aPath); + + return path; +} + +function CGPathCreateCopy(aPath) +{ + return CGPathCreateMutableCopy(aPath); +} + +function CGPathRelease(aPath) +{ +} + +function CGPathRetain(aPath) +{ + return aPath; +} + +function CGPathAddArc(aPath, aTransform, x, y, aRadius, aStartAngle, anEndAngle, isClockwise) +{ + if (aTransform && !_CGAffineTransformIsIdentity(aTransform)) + { + var center = _CGPointMake(x, y), + end = _CGPointMake(COS(anEndAngle), SIN(anEndAngle)), + start = _CGPointMake(COS(aStartAngle), SIN(aStartAngle)); + + end = _CGPointApplyAffineTransform(end, aTransform); + start = _CGPointApplyAffineTransform(start, aTransform); + center = _CGPointApplyAffineTransform(center, aTransform); + + x = center.x; + y = center.y; + + var oldEndAngle = anEndAngle, + oldStartAngle = aStartAngle; + + anEndAngle = ATAN2(end.y - aTransform.ty, end.x - aTransform.tx); + aStartAngle = ATAN2(start.y - aTransform.ty, start.x - aTransform.tx); + + // Angles that equal "modulo" 2 pi return as equal after transforming them, + // so we have to make sure to make them different again if they were different + // to start out with. It's the difference between no circle and a full circle. + if (anEndAngle == aStartAngle && oldEndAngle != oldStartAngle) + if (oldStartAngle > oldEndAngle) + anEndAngle = anEndAngle - PI2; + else + aStartAngle = aStartAngle - PI2; + + aRadius = _CGSizeMake(aRadius, 0); + aRadius = _CGSizeApplyAffineTransform(aRadius, aTransform); + aRadius = SQRT(aRadius.width * aRadius.width + aRadius.height * aRadius.height); + } + + aPath.current = _CGPointMake(x + aRadius * COS(anEndAngle), y + aRadius * SIN(anEndAngle)); + aPath.elements[aPath.count++] = { type:kCGPathElementAddArc, x:x, y:y, radius:aRadius, startAngle:aStartAngle, endAngle:anEndAngle }; +} + +function CGPathAddArcToPoint(aPath, aTransform, x1, y1, x2, y2, aRadius) +{ +} + +function CGPathAddCurveToPoint(aPath, aTransform, cp1x, cp1y, cp2x, cp2y, x, y) +{ + var cp1 = _CGPointMake(cp1x, cp1y), + cp2 = _CGPointMake(cp2x, cp2y), + end = _CGPointMake(x, y); + + if (aTransform) + { + cp1 = _CGPointApplyAffineTransform(cp1, aTransform); + cp2 = _CGPointApplyAffineTransform(cp2, aTransform); + end = _CGPointApplyAffineTransform(end, aTransform); + } + + aPath.current = end; + aPath.elements[aPath.count++] = { type:kCGPathElementAddCurveToPoint, cp1x:cp1.x, cp1y:cp1.y, cp2x:cp2.x, cp2y:cp2.y, x:end.x, y:end.y }; +} + +function CGPathAddLines(aPath, aTransform, points, count) +{ + var i = 1; + + if (arguments["count"] == NULL) + var count = points.length; + + if (!aPath || count < 2) + return; + + CGPathMoveToPoint(aPath, aTransform, points[0].x, points[0].y); + + for (; i < count; ++i) + CGPathAddLineToPoint(aPath, aTransform, points[i].x, points[i].y); +} + +function CGPathAddLineToPoint(aPath, aTransform, x, y) +{ + var point = _CGPointMake(x, y); + + if (aTransform != NULL) + point = _CGPointApplyAffineTransform(point, aTransform); + + aPath.elements[aPath.count++] = { type: kCGPathElementAddLineToPoint, x:point.x, y:point.y }; + aPath.current = point; +} + +function CGPathAddPath(aPath, aTransform, anotherPath) +{ + var i = 0, + count = anotherPath.count; + + for (; i < count; ++i) + { + var element = anotherPath[i]; + + aPath.elements[aPath.count++] = { type:element.type, points:element.point.slice() }; + + if (element.type == kCGPathElementAddArc || element.type == kCGPathElementAddArcToPoint) + aPath.elements[aPath.count - 1].radius = element.radius; + } + + aPath.current = anotherPath.current; +} + +function CGPathAddQuadCurveToPoint(aPath, aTransform, cpx, cpy, x, y) +{ + var cp = _CGPointMake(cpx, cpy), + end = _CGPointMake(x, y); + + if (aTransform) + { + cp = _CGPointApplyAffineTransform(control, aTransform); + end = _CGPointApplyAffineTransform(end, aTransform); + } + + aPath.elements[aPath.count++] = { type:kCGPathElementAddQuadCurveToPoint, cpx:cp.x, cpy:cp.y, x:end.x, y:end.y } + aPath.current = end; +} + +function CGPathAddRect(aPath, aTransform, aRect) +{ + CGPathAddRects(aPath, aTransform, [aRect], 1); +} + +function CGPathAddRects(aPath, aTransform, rects, count) +{ + var i = 0; + + if (arguments["count"] == NULL) + var count = rects.length; + + for (; i < count; ++i) + { + var rect = rects[i]; + + CGPathMoveToPoint(aPath, aTransform, _CGRectGetMinX(rect), _CGRectGetMinY(rect)); + CGPathAddLineToPoint(aPath, aTransform, _CGRectGetMaxX(rect), _CGRectGetMinY(rect)); + CGPathAddLineToPoint(aPath, aTransform, _CGRectGetMaxX(rect), _CGRectGetMaxY(rect)); + CGPathAddLineToPoint(aPath, aTransform, _CGRectGetMinX(rect), _CGRectGetMaxY(rect)); + + CGPathCloseSubpath(aPath); + } +} + +function CGPathMoveToPoint(aPath, aTransform, x, y) +{ + var point = _CGPointMake(x, y), + count = aPath.count; + + if (aTransform != NULL) + point = _CGPointApplyAffineTransform(point, aTransform); + + aPath.start = point; + aPath.current = point; + + var previous = aPath.elements[count - 1]; + + if (count != 0 && previous.type == kCGPathElementMoveToPoint) + { + previous.x = point.x; + previous.y = point.y; + } + else + aPath.elements[aPath.count++] = { type:kCGPathElementMoveToPoint, x:point.x, y:point.y }; +} + +function CGPathCloseSubpath(aPath) +{ + var count = aPath.count; + + // Don't bother closing this subpath if there aren't any current elements, or the last element already closed the subpath. + if (count == 0 || aPath.elements[count - 1].type == kCGPathElementCloseSubpath) + return; + + aPath.elements[aPath.count++] = { type:kCGPathElementCloseSubpath, points:[aPath.start] }; +} + +function CGPathEqualToPath(aPath, anotherPath) +{ + if (aPath == anotherPath) + return YES; + + if (aPath.count != anotherPath.count || !_CGPointEqualToPoint(aPath.start, anotherPath.start) || !_CGPointEqualToPoint(aPath.current, anotherPath.current)) + return NO; + + var i = 0, + count = aPath.count; + + for (; i < count; ++i) + { + var element = aPath[i], + anotherElement = anotherPath[i]; + + if (element.type != anotherElement.type) + return NO; + + if ((element.type == kCGPathElementAddArc || element.type == kCGPathElementAddArcToPoint) && + element.radius != anotherElement.radius) + return NO; + + var j = element.points.length; + + while (j--) + if (!_CGPointEqualToPoint(element.points[j], anotherElement.points[j])) + return NO; + } + + return YES; +} + +function CGPathGetCurrentPoint(aPath) +{ + return _CGPointCreateCopy(aPath.current); +} + +function CGPathIsEmpty(aPath) +{ + return !aPath || aPath.count == 0; +} diff --git a/AppKit/Info.plist b/AppKit/Info.plist new file mode 100644 index 0000000000..ed261efba4 --- /dev/null +++ b/AppKit/Info.plist @@ -0,0 +1,14 @@ + + + + + CPBundleIdentifier + com.280n.AppKit + CPBundleInfoDictionaryVersion + 6.0 + CPBundleName + AppKit + CPBundlePackageType + FMWK + + diff --git a/AppKit/Platform/DOM/CPDOMDisplayServer.h b/AppKit/Platform/DOM/CPDOMDisplayServer.h new file mode 100644 index 0000000000..7afa071074 --- /dev/null +++ b/AppKit/Platform/DOM/CPDOMDisplayServer.h @@ -0,0 +1,88 @@ +/* + * CPDOMDisplayServer.h + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +#define SetStyleOrigin 0 +#define SetStyleLeftTop 0 +#define SetStyleRightTop 1 +#define SetStyleLeftBottom 2 +#define SetStyleRightBottom 3 +#define SetStyleSize 4 +#define AppendChild 5 +#define InsertBefore 6 +#define RemoveChild 7 + +#define CPDOMDisplayServerSetStyleOrigin(anInstruction, aDOMElement, aTransform, x, y)\ + if (!aDOMElement.CPDOMDisplayContext)\ + aDOMElement.CPDOMDisplayContext = [];\ + var __index = aDOMElement.CPDOMDisplayContext[SetStyleOrigin];\ + if (!(__index >= 0))\ + {\ + __index = aDOMElement.CPDOMDisplayContext[SetStyleOrigin] = CPDOMDisplayServerInstructionCount;\ + CPDOMDisplayServerInstructionCount += 5;\ + }\ + CPDOMDisplayServerInstructions[__index] = anInstruction;\ + CPDOMDisplayServerInstructions[__index + 1] = aDOMElement;\ + CPDOMDisplayServerInstructions[__index + 2] = aTransform;\ + CPDOMDisplayServerInstructions[__index + 3] = x;\ + CPDOMDisplayServerInstructions[__index + 4] = y; + +#define CPDOMDisplayServerSetStyleLeftTop(aDOMElement, aTransform, aLeft, aTop) CPDOMDisplayServerSetStyleOrigin(SetStyleLeftTop, aDOMElement, aTransform, aLeft, aTop) + +#define CPDOMDisplayServerSetStyleRightTop(aDOMElement, aTransform, aRight, aTop) CPDOMDisplayServerSetStyleOrigin(SetStyleRightTop, aDOMElement, aTransform, aRight, aTop) + +#define CPDOMDisplayServerSetStyleLeftBottom(aDOMElement, aTransform, aLeft, aBottom) CPDOMDisplayServerSetStyleOrigin(SetStyleLeftBottom, aDOMElement, aTransform, aLeft, aBottom) + +#define CPDOMDisplayServerSetStyleRightBottom(aDOMElement, aTransform, aRight, aBottom) CPDOMDisplayServerSetStyleOrigin(SetStyleRightBottom, aDOMElement, aTransform, aRight, aBottom) + +#define CPDOMDisplayServerSetStyleSize(aDOMElement, aWidth, aHeight)\ + if (!aDOMElement.CPDOMDisplayContext)\ + aDOMElement.CPDOMDisplayContext = [];\ + var __index = aDOMElement.CPDOMDisplayContext[SetStyleSize];\ + if (!(__index >= 0))\ + {\ + __index = aDOMElement.CPDOMDisplayContext[SetStyleSize] = CPDOMDisplayServerInstructionCount;\ + CPDOMDisplayServerInstructionCount += 4;\ + }\ + CPDOMDisplayServerInstructions[__index] = SetStyleSize;\ + CPDOMDisplayServerInstructions[__index + 1] = aDOMElement;\ + CPDOMDisplayServerInstructions[__index + 2] = aWidth;\ + CPDOMDisplayServerInstructions[__index + 3] = aHeight; + +#define CPDOMDisplayServerAppendChild(aParentElement, aChildElement)\ + if (aChildElement.CPDOMDisplayContext) aChildElement.CPDOMDisplayContext[SetStyleOrigin] = -1;\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = AppendChild;\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aParentElement;\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aChildElement; + +#define CPDOMDisplayServerInsertBefore(aParentElement, aChildElement, aBeforeElement)\ + if (aChildElement.CPDOMDisplayContext) aChildElement.CPDOMDisplayContext[SetStyleOrigin] = -1;\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = InsertBefore;\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aParentElement;\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aChildElement;\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aBeforeElement; + +#define CPDOMDisplayServerRemoveChild(aParentElement, aChildElement)\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = RemoveChild;\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aParentElement;\ + CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aChildElement; + +//#dfeine CPDOMDisplayServerCustomAction() diff --git a/AppKit/Platform/DOM/CPDOMDisplayServer.j b/AppKit/Platform/DOM/CPDOMDisplayServer.j new file mode 100644 index 0000000000..c089458669 --- /dev/null +++ b/AppKit/Platform/DOM/CPDOMDisplayServer.j @@ -0,0 +1,129 @@ +/* + * CPDOMDisplayServer.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +#include "../../CoreGraphics/CGAffineTransform.h" +#include "CPDOMDisplayServer.h" + + +var CPDOMDisplayRunLoop = nil; + +CPDOMDisplayServerInstructions = []; +CPDOMDisplayServerInstructionCount = 0; + +@implementation CPDOMDisplayServer : CPObject +{ +} + ++ (void)start +{ + CPDOMDisplayRunLoop = [CPRunLoop currentRunLoop]; + + [CPDOMDisplayRunLoop performSelector:@selector(run) target:CPDOMDisplayServer argument:nil order:0 modes:[CPDefaultRunLoopMode]]; +} + ++ (void)run +{ + var index = 0; + + while (index < CPDOMDisplayServerInstructionCount) + { + var instruction = CPDOMDisplayServerInstructions[index++]; +try{ + switch (instruction) + { + case SetStyleLeftTop: + case SetStyleRightTop: + case SetStyleLeftBottom: + case SetStyleRightBottom: var element = CPDOMDisplayServerInstructions[index], + style = element.style, + x = (instruction == SetStyleLeftTop || instruction == SetStyleLeftBottom) ? "left" : "right", + y = (instruction == SetStyleLeftTop || instruction == SetStyleRightTop) ? "top" : "bottom"; + + CPDOMDisplayServerInstructions[index++] = nil; + + var transform = CPDOMDisplayServerInstructions[index++]; + + if (transform) + { + var point = _CGPointMake(CPDOMDisplayServerInstructions[index++], CPDOMDisplayServerInstructions[index++]), + transformed = _CGPointApplyAffineTransform(point, transform); + + style[x] = ROUND(transformed.x) + "px"; + style[y] = ROUND(transformed.y) + "px"; + + } + else + { + style[x] = ROUND(CPDOMDisplayServerInstructions[index++]) + "px"; + style[y] = ROUND(CPDOMDisplayServerInstructions[index++]) + "px"; + } + + element.CPDOMDisplayContext[SetStyleOrigin] = -1; + + break; + + case SetStyleSize: var element = CPDOMDisplayServerInstructions[index], + style = element.style; + + CPDOMDisplayServerInstructions[index++] = nil; + + element.CPDOMDisplayContext[SetStyleSize] = -1; + + style.width = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++])) + "px"; + style.height = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++])) + "px"; + + break; + + case AppendChild: CPDOMDisplayServerInstructions[index].appendChild(CPDOMDisplayServerInstructions[index + 1]); + + CPDOMDisplayServerInstructions[index++] = nil; + CPDOMDisplayServerInstructions[index++] = nil; + + break; + + case InsertBefore: CPDOMDisplayServerInstructions[index].insertBefore(CPDOMDisplayServerInstructions[index + 1], CPDOMDisplayServerInstructions[index + 2]); + + CPDOMDisplayServerInstructions[index++] = nil; + CPDOMDisplayServerInstructions[index++] = nil; + CPDOMDisplayServerInstructions[index++] = nil; + + break; + + case RemoveChild: CPDOMDisplayServerInstructions[index].removeChild(CPDOMDisplayServerInstructions[index + 1]); + + CPDOMDisplayServerInstructions[index++] = nil; + CPDOMDisplayServerInstructions[index++] = nil; + + break; + }}catch(e) { alert("here?" + instruction) } + } + + CPDOMDisplayServerInstructionCount = 0; + + [CPDOMDisplayRunLoop performSelector:@selector(run) target:CPDOMDisplayServer argument:nil order:0 modes:[CPDefaultRunLoopMode]]; +} + +@end + +[CPDOMDisplayServer start]; diff --git a/AppKit/Platform/DOM/CPDOMWindowBridge.j b/AppKit/Platform/DOM/CPDOMWindowBridge.j new file mode 100644 index 0000000000..23450d20a1 --- /dev/null +++ b/AppKit/Platform/DOM/CPDOMWindowBridge.j @@ -0,0 +1,836 @@ +/* + * CPDOMWindowBridge.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + +import "CPEvent.j" +import "CPCompatibility.j" + +import "CPDOMWindowLayer.j" + +#import "../../CoreGraphics/CGGeometry.h" + + +CPSharedDOMWindowBridge = nil; + +var ExcludedDOMElements = []; + +@implementation CPDOMWindowBridge : CPObject +{ + CPArray _orderedWindows; + CPWindow _mouseDownWindow; + + DOMWindow _DOMWindow; + DOMElement _DOMBodyElement; + DOMElement _DOMFocusElement; + + CPArray _windowLevels; + CPDictionary _windowLayers; + + CPRect _frame; + CPRect _contentBounds; + + BOOL _mouseIsDown; + CPTimeInterval _lastMouseUp; + CPTimeInterval _lastMouseDown; + + CPPoint _currentMousePosition; + + JSObject _charCodes; + unsigned _keyCode; + + BOOL _DOMEventMode; + + // Native Pasteboard Support + DOMElement _DOMPasteboardElement; + CPEvent _pasteboardKeyDownEvent; + + CPString _overriddenEventType; +} + ++ (id)sharedDOMWindowBridge +{ + if (!CPSharedDOMWindowBridge) + CPSharedDOMWindowBridge = [[CPDOMWindowBridge alloc] _initWithDOMWindow:window]; + + return CPSharedDOMWindowBridge; +} + +- (id)initWithFrame:(CPRect)aFrame +{ + alert("unimplemented"); +} + +- (id)_initWithDOMWindow:(DOMWindow)aDOMWindow +{ + self = [super init]; + + if (self) + { + _DOMWindow = aDOMWindow; + + _windowLevels = []; + _windowLayers = [CPDictionary dictionary]; + + // Do this before getting the frame of the window, because if not it will be wrong in IE. + _DOMBodyElement = document.getElementsByTagName("body")[0]; + _DOMBodyElement.innerHTML = ""; // Get rid of anything that might be lingering in the body element. + _DOMBodyElement.style.overflow = "hidden"; + + if (document.documentElement) + document.documentElement.style.overflow = "hidden"; + + _frame = CPDOMWindowGetFrame(_DOMWindow); + _contentBounds = CGRectMake(0.0, 0.0, CPRectGetWidth(_frame), CPRectGetHeight(_frame)); + + _DOMFocusElement = document.createElement("input"); + _DOMFocusElement.style.position = "absolute"; + _DOMFocusElement.style.zIndex = "-1000"; + _DOMFocusElement.style.opacity = "0"; + _DOMFocusElement.style.filter = "alpha(opacity=0)"; + _DOMBodyElement.appendChild(_DOMFocusElement); + + // Create Native Pasteboard handler. + _DOMPasteboardElement = document.createElement("input"); + _DOMPasteboardElement.style.position = "absolute"; + _DOMPasteboardElement.style.top = "-10000px"; + _DOMPasteboardElement.style.zIndex = "99"; + + _DOMBodyElement.appendChild(_DOMPasteboardElement); + + // Make sure the pastboard element is blurred. + _DOMPasteboardElement.blur(); + + _charCodes = {}; + + // + var theClass = [self class], + + keyEventSelector = @selector(_bridgeKeyEvent:), + keyEventImplementation = class_getMethodImplementation(theClass, keyEventSelector), + keyEventCallback = function (anEvent) { keyEventImplementation(self, nil, anEvent); }, + + mouseEventSelector = @selector(_bridgeMouseEvent:), + mouseEventImplementation = class_getMethodImplementation(theClass, mouseEventSelector), + mouseEventCallback = function (anEvent) { mouseEventImplementation(self, nil, anEvent); }, + + scrollEventSelector = @selector(_bridgeScrollEvent:), + scrollEventImplementation = class_getMethodImplementation(theClass, scrollEventSelector), + scrollEventCallback = function (anEvent) { scrollEventImplementation(self, nil, anEvent); }, + + resizeEventSelector = @selector(_bridgeResizeEvent:), + resizeEventImplementation = class_getMethodImplementation(theClass, resizeEventSelector), + resizeEventCallback = function (anEvent) { resizeEventImplementation(self, nil, anEvent); }, + + theDocument = _DOMWindow.document; + + if (document.addEventListener) + { + _DOMWindow.addEventListener("resize", resizeEventCallback, NO); + + theDocument.addEventListener(CPDOMEventMouseUp, mouseEventCallback, NO); + theDocument.addEventListener(CPDOMEventMouseDown, mouseEventCallback, NO); + theDocument.addEventListener(CPDOMEventMouseMoved, mouseEventCallback, NO); + + theDocument.addEventListener(CPDOMEventKeyUp, keyEventCallback, NO); + theDocument.addEventListener(CPDOMEventKeyDown, keyEventCallback, NO); + theDocument.addEventListener(CPDOMEventKeyPress, keyEventCallback, NO); + + //FIXME: does firefox really need a different value? + _DOMWindow.addEventListener("DOMMouseScroll", scrollEventCallback, NO); + _DOMWindow.addEventListener(CPDOMEventScrollWheel, scrollEventCallback, NO); + } + else if(document.attachEvent) + { + _DOMWindow.attachEvent("onresize", resizeEventCallback); + + theDocument.attachEvent("on" + CPDOMEventMouseUp, mouseEventCallback); + theDocument.attachEvent("on" + CPDOMEventMouseDown, mouseEventCallback); + theDocument.attachEvent("on" + CPDOMEventMouseMoved, mouseEventCallback); + theDocument.attachEvent("on" + CPDOMEventDoubleClick, mouseEventCallback); + + theDocument.attachEvent("on" + CPDOMEventKeyUp, keyEventCallback); + theDocument.attachEvent("on" + CPDOMEventKeyDown, keyEventCallback); + theDocument.attachEvent("on" + CPDOMEventKeyPress, keyEventCallback); + + _DOMWindow.onmousewheel = scrollEventCallback; + theDocument.onmousewheel = scrollEventCallback; + + theDocument.body.ondrag = function () { return NO; }; + theDocument.body.onselectstart = function () { return window.event.srcElement == _DOMPasteboardElement; }; + } + + ExcludedDOMElements["INPUT"] = YES; + ExcludedDOMElements["SELECT"] = YES; + ExcludedDOMElements["TEXTAREA"] = YES; + ExcludedDOMElements["OPTION"] = YES; + } + + return self; +} + +- (CPRect)frame +{ + return CGRectMakeCopy(_frame); +} + +- (CGRect)visibleFrame +{ + var frame = [self frame]; + + frame.origin = CGPointMakeZero(); + + if ([CPMenu menuBarVisible]) + { + var menuBarHeight = [[CPApp mainMenu] menuBarHeight]; + + frame.origin.y += menuBarHeight; + frame.size.height -= menuBarHeight; + } + + return frame; +} + +- (CPRect)contentBounds +{ + return CPRectCreateCopy(_contentBounds); +} + +- (CPDOMWindowLayer)layerAtLevel:(int)aLevel create:(BOOL)aFlag +{ + var layer = [_windowLayers objectForKey:aLevel]; + + // If the layer doesn't currently exist, and the create flag is true, + // create the layer. + if (!layer && aFlag) + { + layer = [[CPDOMWindowLayer alloc] initWithLevel:aLevel]; + + [_windowLayers setObject:layer forKey:aLevel]; + + // Find the nearest layer. This is similar to a binary search, + // only we know we won't find the value. + var low = 0, + high = _windowLevels.length - 1, + middle; + + while (low <= high) + { + middle = FLOOR((low + high) / 2); + + if (_windowLevels[middle] > aLevel) + high = middle - 1; + else + low = middle + 1; + } + + [_windowLevels insertObject:aLevel atIndex:_windowLevels[middle] > aLevel ? middle : middle + 1]; + layer._DOMElement.style.zIndex = aLevel; + _DOMBodyElement.appendChild(layer._DOMElement); + } + + return layer; +} + +- (void)order:(CPWindowOrderingMode)aPlace window:(CPWindow)aWindow relativeTo:(CPWindow)otherWindow +{ + // Grab the appropriate level for the layer, and create it if + // necessary (if we are not simply removing the window). + var layer = [self layerAtLevel:[aWindow level] create:aPlace != CPWindowOut]; + + // Ignore otherWindow, simply remove this window from it's level. + // If layer is nil, this will be a no-op. + if (aPlace == CPWindowOut) + return [layer removeWindow:aWindow]; + + // Place the window at the appropriate index. + [layer insertWindow:aWindow atIndex:(otherWindow ? (aPlace == CPWindowAbove ? otherWindow._index + 1 : otherWindow._index) : CPNotFound)]; +} + +- (CPView)_dragHitTest:(CPPoint)aPoint pasteboard:(CPPasteboard)aPasteboard +{ + var view = nil, + levels = _windowLevels, + layers = _windowLayers, + levelCount = levels.length; + + while (levelCount-- && !view) + { + // Skip any windows above or at the dragging level. + if (levels[levelCount] >= CPDraggingWindowLevel) + continue; + + var windows = [layers objectForKey:levels[levelCount]]._windows, + windowCount = windows.length; + + while (windowCount--) + { + var theWindow = windows[windowCount], + frame = theWindow._frame; + + if (CPRectContainsPoint(frame, aPoint)) + if (view = [theWindow._windowView _dragHitTest:CGPointMake(aPoint.x - frame.origin.x, aPoint.y - frame.origin.y) pasteboard:aPasteboard]) + return view; + else + return nil; + } + } + + return view; +} + +- (void)_propagateCurrentDOMEvent:(BOOL)aFlag +{ + StopDOMEventPropagation = !aFlag; +} + +- (CPWindow)hitTest:(CPPoint)location +{ + var levels = _windowLevels, + layers = _windowLayers, + levelCount = levels.length, + theWindow = nil; + + while (levelCount-- && !theWindow) + { + var windows = [layers objectForKey:levels[levelCount]]._windows, + windowCount = windows.length; + + while (windowCount-- && !theWindow) + if (CPRectContainsPoint(windows[windowCount]._frame, location)) + theWindow = windows[windowCount]; + } + + return theWindow; +} + +@end + +var CPDOMWindowGetFrame = function(_DOMWindow) +{ + var frame = nil;//CGRectMakeZero(); + + // We will rarely be able to get all this information, but we do the best we can: + if (_DOMWindow.outerWidth) + frame = CGRectMake(0, 0, _DOMWindow.outerWidth, _DOMWindow.outerHeight); + + else /*if(self.outerWidth)*/ + frame = CGRectMake(0, 0, -1, -1); + + if (window.screenTop) + frame.origin = CGPointMake(_DOMWindow.screenLeft, _DOMWindow.screenTop, 0); + + else if (window.screenX) + frame.origin = CGPointMake(_DOMWindow.screenX, _DOMWindow.screenY, 0); + + // Safari, Mozilla, Firefox, and Opera + if (_DOMWindow.innerWidth) + frame.size = CGSizeMake(_DOMWindow.innerWidth, _DOMWindow.innerHeight); + + // Internet Explorer 6 in Strict Mode + else if (document.documentElement && document.documentElement.clientWidth) + frame.size = CGSizeMake(_DOMWindow.document.documentElement.clientWidth, _DOMWindow.document.documentElement.clientHeight); + + // Internet Explorer X + else + frame.size = CGSizeMake(_DOMWindow.document.body.clientWidth, _DOMWindow.document.body.clientHeight); + + return frame; +} + +//right now we hard code q, w, r and t as keys to propogate +//these aren't normal keycodes, they are with modifier key codes +//might be mac only, we should investigate futher later. +var KeyCodesToPropagate = { '113':1, '119':1, '114':1, '116':1, '108':1, '102':1 }; +var KeyCodesWithoutKeyPressEvents = { '8':1, '9':1, '37':1, '38':1, '39':1, '40':1, '46':1 }; + +var CTRL_KEY_CODE = 17; + +@implementation CPDOMWindowBridge (Events) + +- (void)_bridgeMouseEvent:(DOMEvent)aDOMEvent +{ + var theType = _overriddenEventType || aDOMEvent.type; + + // IE's event order is down, up, up, dblclick, so we have create these events artificially. + if (theType == CPDOMEventDoubleClick) + { + _overriddenEventType = CPDOMEventMouseDown; + [self _bridgeMouseEvent:aDOMEvent]; + + _overriddenEventType = CPDOMEventMouseUp; + [self _bridgeMouseEvent:aDOMEvent]; + + _overriddenEventType = nil; + + return; + } + + try + { + var event, + location = _CGPointMake(aDOMEvent.clientX, aDOMEvent.clientY), + timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), + sourceElement = (aDOMEvent.target || aDOMEvent.srcElement), + windowNumber = 0, + modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | + (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | + (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | + (aDOMEvent.metaKey ? CPCommandKeyMask : 0); + + StopDOMEventPropagation = YES; + + if (_mouseDownWindow) + windowNumber = [_mouseDownWindow windowNumber]; + else + { + var theWindow = [self hitTest:location]; + + if (aDOMEvent.type == CPDOMEventMouseDown && theWindow) + _mouseDownWindow = theWindow; + + windowNumber = [theWindow windowNumber]; + } + + if (windowNumber) + { + var windowFrame = CPApp._windows[windowNumber]._frame; + + location.x -= _CGRectGetMinX(windowFrame); + location.y -= _CGRectGetMinY(windowFrame); + } + + switch (theType) + { + case CPDOMEventMouseUp: if(_mouseIsDown) + { + event = [CPEvent mouseEventWithType:CPLeftMouseUp location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 + clickCount:CPDOMEventGetClickCount(_lastMouseUp, timestamp, location) pressure:0]; + + _mouseIsDown = NO; + _lastMouseUp = event; + _mouseDownWindow = nil; + } + + if(_DOMEventMode) + { + _DOMEventMode = NO; + return; + } + + break; + + case CPDOMEventMouseDown: if (ExcludedDOMElements[sourceElement.tagName] && sourceElement != _DOMFocusElement) + { + _DOMEventMode = YES; + _mouseIsDown = YES; + + //fake a down and up event so that event tracking mode will work correctly + [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseDown location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 + clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]]; + + [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseUp location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 + clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]]; + + return; + } + + event = [CPEvent mouseEventWithType:CPLeftMouseDown location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 + clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]; + + _mouseIsDown = YES; + _lastMouseDown = event; + + break; + + case CPDOMEventMouseMoved: if (_DOMEventMode) + return; + + event = [CPEvent mouseEventWithType:_mouseIsDown ? CPLeftMouseDragged : CPMouseMoved + location:location modifierFlags:modifierFlags timestamp:timestamp + windowNumber:windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0]; + + _currentMousePosition = _CGPointMake(aDOMEvent.clientX, aDOMEvent.clientY); + + break; + } + + if (event) + { + event._DOMEvent = aDOMEvent; + + [CPApp sendEvent:event]; + } + + if (StopDOMEventPropagation) + CPDOMEventStop(aDOMEvent); + + [[CPRunLoop currentRunLoop] performSelectors]; + } + catch (anException) + { + objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); + } +} + +- (void)_bridgeKeyEvent:(DOMEvent)aDOMEvent +{ + try + { + var event, + timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), + sourceElement = (aDOMEvent.target || aDOMEvent.srcElement), + windowNumber = [[CPApp keyWindow] windowNumber], + modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | + (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | + (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | + (aDOMEvent.metaKey ? CPCommandKeyMask : 0); + + if (ExcludedDOMElements[sourceElement.tagName] && sourceElement != _DOMFocusElement && sourceElement != _DOMPasteboardElement) + return; + + StopDOMEventPropagation = YES; + + if(KeyCodesToPropagate[aDOMEvent.keyCode]) + StopDOMEventPropagation = !(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)); + + /*if (aDOMEvent.keyCode == 17) + StopDOMEventPropagation = NO; + if (aDOMEvent.keyCode != 17) + alert("WILL SEND " + modifierFlags + " " + aDOMEvent.type);*/ + + var isNativePasteEvent = NO, + isNativeCopyOrCutEvent = NO; + + switch (aDOMEvent.type) + { + case CPDOMEventKeyDown: // Grab and store the keycode now since it is correct and consistent at this point. + _keyCode = aDOMEvent.keyCode; + + var characters = String.fromCharCode(_keyCode).toLowerCase(); + + // If this could be a native PASTE event, then we need to further examine it before + // sending a CPEvent. Select our element to see if anything gets pasted in it. + if (characters == "v" && (modifierFlags & CPPlatformActionKeyMask)) + { + _DOMPasteboardElement.select(); + _DOMPasteboardElement.value = ""; + + isNativePasteEvent = YES; + } + + // Normally we return now because we let keypress send the actual CPEvent keyDown event, since we don't have + // a complete set of information yet. + + // However, of this could be a native COPY event, we need to let the normal event-process take place so it + // can capture our internal Cappuccino pasteboard. + else if ((characters == "c" || characters == "x") && (modifierFlags & CPPlatformActionKeyMask)) + isNativeCopyOrCutEvent = YES; + + // Also, certain browsers (IE and Safari), have broken keyboard supportwhere they don't send keypresses for certain events. + // So, allow the keypress event to handle the event if we are not a browser with broken (remedial) key support... + else if (!CPFeatureIsCompatible(CPJavascriptRemedialKeySupport)) + return; + + // Or, if this is not one of those special keycodes, and also not a ctrl+event + else if (!KeyCodesWithoutKeyPressEvents[_keyCode] && (_keyCode == CTRL_KEY_CODE || !(modifierFlags & CPControlKeyMask))) + return; + + // If this is in fact our broke state, continue to keypress and send the keydown. + case CPDOMEventKeyPress: + // If the source of this event is our pasteboard element, then simply let it continue + // as normal, so that the paste event can successfully complete. + if ((aDOMEvent.target || aDOMEvent.srcElement) == _DOMPasteboardElement) + return; + + var keyCode = _keyCode, + charCode = aDOMEvent.keyCode || aDOMEvent.charCode, + isARepeat = (_charCodes[keyCode] != nil); + + _charCodes[keyCode] = charCode; + + var characters = String.fromCharCode(charCode), + charactersIgnoringModifiers = characters.toLowerCase(); + + event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil + characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:keyCode]; + + if (isNativePasteEvent) + { + _pasteboardKeyDownEvent = event; + + window.setTimeout(function () { [self _checkPasteboardElement] }, 0); + + return; + } + + break; + + case CPDOMEventKeyUp: var keyCode = aDOMEvent.keyCode, + charCode = _charCodes[keyCode]; + + _charCodes[keyCode] = nil; + + var characters = String.fromCharCode(charCode), + charactersIgnoringModifiers = characters.toLowerCase(); + + if (!(modifierFlags & CPShiftKeyMask)) + characters = charactersIgnoringModifiers; + + event = [CPEvent keyEventWithType:CPKeyUp location:location modifierFlags:modifierFlags + timestamp: timestamp windowNumber:windowNumber context:nil + characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:NO keyCode:keyCode]; + break; + } + + if (event) + { + event._DOMEvent = aDOMEvent; + + [CPApp sendEvent:event]; + + if (isNativeCopyOrCutEvent) + { + var pasteboard = [CPPasteboard generalPasteboard], + types = [pasteboard types]; + + // If this is a native copy event, then check if the pasteboard has anything in it. + if (types.length) + { + if ([types indexOfObjectIdenticalTo:CPStringPboardType] != CPNotFound) + _DOMPasteboardElement.value = [pasteboard stringForType:CPStringPboardType]; + else + _DOMPasteboardElement.value = [pasteboard _generateStateUID]; + + _DOMPasteboardElement.select(); + + window.setTimeout(function() { [self _clearPasteboardElement]; }, 0); + } + + return; + } + } + + if (StopDOMEventPropagation) + CPDOMEventStop(aDOMEvent); + + [[CPRunLoop currentRunLoop] performSelectors]; + } + catch (anException) + { + objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); + } +} + +- (void)_bridgeScrollEvent:(DOMEvent)aDOMEvent +{ + if(!aDOMEvent) + aDOMEvent = window.event; + + try + { + var deltaX = 0.0, + deltaY = 0.0, + windowNumber = 0, + location = CGPointMake(_currentMousePosition.x, _currentMousePosition.y), + timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), + modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | + (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | + (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | + (aDOMEvent.metaKey ? CPCommandKeyMask : 0); + + StopDOMEventPropagation = YES; + + windowNumber = [[self hitTest:location] windowNumber]; + + if (!windowNumber) + return; + + var windowFrame = CPApp._windows[windowNumber]._frame; + + location.x -= CGRectGetMinX(windowFrame); + location.y -= CGRectGetMinY(windowFrame); + + if(typeof aDOMEvent.wheelDeltaX != "undefined") + { + deltaX = aDOMEvent.wheelDeltaX / 120.0; + deltaY = aDOMEvent.wheelDeltaY / 120.0; + } + + else if (aDOMEvent.wheelDelta) + deltaY = aDOMEvent.wheelDelta / 120.0; + + else if (aDOMEvent.detail) + deltaY = -aDOMEvent.detail / 3.0; + + else + return; + + if(!CPFeatureIsCompatible(CPJavaScriptNegativeMouseWheelValues)) + { + deltaX = -deltaX; + deltaY = -deltaY; + } + + var event = [CPEvent mouseEventWithType:CPScrollWheel location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0 ]; + + event._DOMEvent = aDOMEvent; + event._deltaX = ROUND(deltaX * 1.5); + event._deltaY = ROUND(deltaY * 1.5); + + [CPApp sendEvent:event]; + + if (StopDOMEventPropagation) + CPDOMEventStop(aDOMEvent); + + [[CPRunLoop currentRunLoop] performSelectors]; + } + catch (anException) + { + objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); + } + +} + +- (void)_bridgeResizeEvent:(DOMEvent)aDOMEvent +{ + try + { + // FIXME: This is not the right way to do this. + // We should pay attention to mouse down and mouse up in conjunction with this. + //window.liveResize = YES; + + var oldSize = _frame.size; + + // window.liveResize = YES? + _frame = CPDOMWindowGetFrame(_DOMWindow); + _contentBounds.size = CGSizeCreateCopy(_frame.size); + + var levels = _windowLevels, + layers = _windowLayers, + levelCount = levels.length; + + while (levelCount--) + { + var windows = [layers objectForKey:levels[levelCount]]._windows, + windowCount = windows.length; + + while (windowCount--) + [windows[windowCount] resizeWithOldBridgeSize:oldSize]; + } + + //window.liveResize = NO; + + [[CPRunLoop currentRunLoop] performSelectors]; + + } + catch (anException) + { + objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); + } +} + +- (void)_checkPasteboardElement +{ + try + { + var value = _DOMPasteboardElement.value; + + if ([value length]) + { + var pasteboard = [CPPasteboard generalPasteboard]; + + if ([pasteboard _stateUID] != value) + { + [pasteboard declareTypes:[CPStringPboardType] owner:self]; + + [pasteboard setString:value forType:CPStringPboardType]; + } + } + + [self _clearPasteboardElement]; + + [CPApp sendEvent:_pasteboardKeyDownEvent]; + + _pasteboardKeyDownEvent = nil; + + [[CPRunLoop currentRunLoop] performSelectors]; + } + catch (anException) + { + objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); + } +} + +- (void)_clearPasteboardElement +{ + _DOMPasteboardElement.value = ""; + _DOMPasteboardElement.blur(); +} + +@end + +var CLICK_SPACE_DELTA = 5.0, + CLICK_TIME_DELTA = document.addEventListener ? 350.0 : 1000.0; + +var CPDOMEventGetClickCount = function(aComparisonEvent, aTimestamp, aLocation) +{ + if (!aComparisonEvent) + return 1; + + var comparisonLocation = [aComparisonEvent locationInWindow]; + + return (aTimestamp - [aComparisonEvent timestamp] < CLICK_TIME_DELTA && + ABS(comparisonLocation.x - aLocation.x) < CLICK_SPACE_DELTA && + ABS(comparisonLocation.y - aLocation.y) < CLICK_SPACE_DELTA) ? [aComparisonEvent clickCount] + 1 : 1; +} + +var CPDOMEventStop = function(aDOMEvent) +{ + // IE Model + aDOMEvent.cancelBubble = true; + aDOMEvent.returnValue = false; + + // W3C Model + if (aDOMEvent.preventDefault) + aDOMEvent.preventDefault(); + + if (aDOMEvent.stopPropagation) + aDOMEvent.stopPropagation(); + + if (aDOMEvent.type == CPDOMEventMouseDown) + { + CPSharedDOMWindowBridge._DOMFocusElement.focus(); + CPSharedDOMWindowBridge._DOMFocusElement.blur(); + } +} + +/* + +*/ \ No newline at end of file diff --git a/AppKit/Platform/DOM/CPDOMWindowLayer.j b/AppKit/Platform/DOM/CPDOMWindowLayer.j new file mode 100644 index 0000000000..85393aa2a9 --- /dev/null +++ b/AppKit/Platform/DOM/CPDOMWindowLayer.j @@ -0,0 +1,119 @@ +/* + * CPDOMWindowLayer.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + +#include "CPDOMDisplayServer.h" + + +@implementation CPDOMWindowLayer : CPObject +{ + int _level; + CPArray _windows; + DOMElement _DOMElement; +} + +- (id)initWithLevel:(int)aLevel +{ + self = [super init]; + + if (self) + { + _level = aLevel; + + _windows = []; + + _DOMElement = document.createElement("div"); + _DOMElement.style.position = "absolute"; + _DOMElement.style.top = "0px"; + _DOMElement.style.left = "0px"; + _DOMElement.style.width = "1px"; + _DOMElement.style.height = "1px"; + } + + return self; +} + +- (int)level +{ + return _level; +} + +- (void)removeWindow:(CPWindow)aWindow +{ + var index = aWindow._index, + count = _windows.length - 1; + + CPDOMDisplayServerRemoveChild(_DOMElement, aWindow._DOMElement); + + [_windows removeObjectAtIndex:aWindow._index]; + + for (; index < count; ++index) + { + _windows[index]._index = index; + _windows[index]._DOMElement.style.zIndex = index; + } + + aWindow._isVisible = NO; +} + +- (void)insertWindow:(CPWindow)aWindow atIndex:(unsigned)anIndex +{ + // We will have to adjust the z-index of all windows starting at this index. + var count = [_windows count], + zIndex = (anIndex == CPNotFound ? count : anIndex), + isVisible = aWindow._isVisible; + + // If the window is already a resident of this layer, remove it. + if (isVisible) + { + zIndex = MIN(zIndex, aWindow._index); + [_windows removeObjectAtIndex:aWindow._index]; + } + else + ++count; + + if (anIndex == CPNotFound || anIndex >= count) + [_windows addObject:aWindow]; + else + [_windows insertObject:aWindow atIndex:anIndex]; + + // Adjust all the affected z-indexes. + for (; zIndex < count; ++zIndex) + { + _windows[zIndex]._index = zIndex; + _windows[zIndex]._DOMElement.style.zIndex = zIndex; + } + // If the window is not already a resident of this layer, add it. + if (!isVisible) + { + CPDOMDisplayServerAppendChild(_DOMElement, aWindow._DOMElement); + + aWindow._isVisible = YES; + + if ([aWindow styleMask] & CPBorderlessBridgeWindowMask) + [aWindow setFrame:[aWindow._bridge contentBounds]]; + } +} + +@end diff --git a/AppKit/Platform/Platform.h b/AppKit/Platform/Platform.h new file mode 100644 index 0000000000..7e763be279 --- /dev/null +++ b/AppKit/Platform/Platform.h @@ -0,0 +1,25 @@ +/* + * Platform.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +#define PLATFORM(FEATURE) (defined( PLATFORM_##FEATURE ) && PLATFORM_##FEATURE) + +#define PLATFORM_DOM 1 diff --git a/AppKit/Resources/CPApplication/New.png b/AppKit/Resources/CPApplication/New.png new file mode 100644 index 0000000000..16c1817bb5 Binary files /dev/null and b/AppKit/Resources/CPApplication/New.png differ diff --git a/AppKit/Resources/CPApplication/NewHighlighted.png b/AppKit/Resources/CPApplication/NewHighlighted.png new file mode 100644 index 0000000000..4202382322 Binary files /dev/null and b/AppKit/Resources/CPApplication/NewHighlighted.png differ diff --git a/AppKit/Resources/CPApplication/Open.png b/AppKit/Resources/CPApplication/Open.png new file mode 100644 index 0000000000..b902ce80bc Binary files /dev/null and b/AppKit/Resources/CPApplication/Open.png differ diff --git a/AppKit/Resources/CPApplication/OpenHighlighted.png b/AppKit/Resources/CPApplication/OpenHighlighted.png new file mode 100644 index 0000000000..1982a6fdc3 Binary files /dev/null and b/AppKit/Resources/CPApplication/OpenHighlighted.png differ diff --git a/AppKit/Resources/CPApplication/Save.png b/AppKit/Resources/CPApplication/Save.png new file mode 100644 index 0000000000..8a1c6b78a0 Binary files /dev/null and b/AppKit/Resources/CPApplication/Save.png differ diff --git a/AppKit/Resources/CPApplication/SaveHighlighted.png b/AppKit/Resources/CPApplication/SaveHighlighted.png new file mode 100644 index 0000000000..e84e392b0c Binary files /dev/null and b/AppKit/Resources/CPApplication/SaveHighlighted.png differ diff --git a/AppKit/Resources/CPButton/CPButtonHUDRegular0.png b/AppKit/Resources/CPButton/CPButtonHUDRegular0.png new file mode 100644 index 0000000000..158e78580c Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonHUDRegular0.png differ diff --git a/AppKit/Resources/CPButton/CPButtonHUDRegular1.png b/AppKit/Resources/CPButton/CPButtonHUDRegular1.png new file mode 100644 index 0000000000..31fb7a1092 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonHUDRegular1.png differ diff --git a/AppKit/Resources/CPButton/CPButtonHUDRegular2.png b/AppKit/Resources/CPButton/CPButtonHUDRegular2.png new file mode 100644 index 0000000000..2151e8e39f Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonHUDRegular2.png differ diff --git a/AppKit/Resources/CPButton/CPButtonHUDRegularHighlighted0.png b/AppKit/Resources/CPButton/CPButtonHUDRegularHighlighted0.png new file mode 100644 index 0000000000..78d5e3ac30 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonHUDRegularHighlighted0.png differ diff --git a/AppKit/Resources/CPButton/CPButtonHUDRegularHighlighted1.png b/AppKit/Resources/CPButton/CPButtonHUDRegularHighlighted1.png new file mode 100644 index 0000000000..62afef8cf0 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonHUDRegularHighlighted1.png differ diff --git a/AppKit/Resources/CPButton/CPButtonHUDRegularHighlighted2.png b/AppKit/Resources/CPButton/CPButtonHUDRegularHighlighted2.png new file mode 100644 index 0000000000..b4a9bd6736 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonHUDRegularHighlighted2.png differ diff --git a/AppKit/Resources/CPButton/CPButtonRoundRectRegular0.png b/AppKit/Resources/CPButton/CPButtonRoundRectRegular0.png new file mode 100644 index 0000000000..bd44dd47d5 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonRoundRectRegular0.png differ diff --git a/AppKit/Resources/CPButton/CPButtonRoundRectRegular1.png b/AppKit/Resources/CPButton/CPButtonRoundRectRegular1.png new file mode 100644 index 0000000000..e874e8d120 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonRoundRectRegular1.png differ diff --git a/AppKit/Resources/CPButton/CPButtonRoundRectRegular2.png b/AppKit/Resources/CPButton/CPButtonRoundRectRegular2.png new file mode 100644 index 0000000000..9519845721 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonRoundRectRegular2.png differ diff --git a/AppKit/Resources/CPButton/CPButtonRoundRectRegularHighlighted0.png b/AppKit/Resources/CPButton/CPButtonRoundRectRegularHighlighted0.png new file mode 100644 index 0000000000..0767982f0f Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonRoundRectRegularHighlighted0.png differ diff --git a/AppKit/Resources/CPButton/CPButtonRoundRectRegularHighlighted1.png b/AppKit/Resources/CPButton/CPButtonRoundRectRegularHighlighted1.png new file mode 100644 index 0000000000..cea07927a2 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonRoundRectRegularHighlighted1.png differ diff --git a/AppKit/Resources/CPButton/CPButtonRoundRectRegularHighlighted2.png b/AppKit/Resources/CPButton/CPButtonRoundRectRegularHighlighted2.png new file mode 100644 index 0000000000..e1850b42d5 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonRoundRectRegularHighlighted2.png differ diff --git a/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegular0.png b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegular0.png new file mode 100644 index 0000000000..79fd35181d Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegular0.png differ diff --git a/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegular1.png b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegular1.png new file mode 100644 index 0000000000..7ab71cc253 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegular1.png differ diff --git a/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegular2.png b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegular2.png new file mode 100644 index 0000000000..49b44ab985 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegular2.png differ diff --git a/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegularHighlighted0.png b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegularHighlighted0.png new file mode 100644 index 0000000000..39768db32f Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegularHighlighted0.png differ diff --git a/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegularHighlighted1.png b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegularHighlighted1.png new file mode 100644 index 0000000000..0b44ef2a3b Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegularHighlighted1.png differ diff --git a/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegularHighlighted2.png b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegularHighlighted2.png new file mode 100644 index 0000000000..bc6a7bcb34 Binary files /dev/null and b/AppKit/Resources/CPButton/CPButtonTexturedRoundedRegularHighlighted2.png differ diff --git a/AppKit/Resources/CPImageView/CPImageViewBottomLeftShadow.png b/AppKit/Resources/CPImageView/CPImageViewBottomLeftShadow.png new file mode 100644 index 0000000000..654ef3cc6c Binary files /dev/null and b/AppKit/Resources/CPImageView/CPImageViewBottomLeftShadow.png differ diff --git a/AppKit/Resources/CPImageView/CPImageViewBottomRightShadow.png b/AppKit/Resources/CPImageView/CPImageViewBottomRightShadow.png new file mode 100644 index 0000000000..93780f243b Binary files /dev/null and b/AppKit/Resources/CPImageView/CPImageViewBottomRightShadow.png differ diff --git a/AppKit/Resources/CPImageView/CPImageViewBottomShadow.png b/AppKit/Resources/CPImageView/CPImageViewBottomShadow.png new file mode 100644 index 0000000000..5fc3ef0216 Binary files /dev/null and b/AppKit/Resources/CPImageView/CPImageViewBottomShadow.png differ diff --git a/AppKit/Resources/CPImageView/CPImageViewLeftShadow.png b/AppKit/Resources/CPImageView/CPImageViewLeftShadow.png new file mode 100644 index 0000000000..a67f66f47c Binary files /dev/null and b/AppKit/Resources/CPImageView/CPImageViewLeftShadow.png differ diff --git a/AppKit/Resources/CPImageView/CPImageViewRightShadow.png b/AppKit/Resources/CPImageView/CPImageViewRightShadow.png new file mode 100644 index 0000000000..1b3cf77573 Binary files /dev/null and b/AppKit/Resources/CPImageView/CPImageViewRightShadow.png differ diff --git a/AppKit/Resources/CPImageView/CPImageViewTopLeftShadow.png b/AppKit/Resources/CPImageView/CPImageViewTopLeftShadow.png new file mode 100644 index 0000000000..23305646de Binary files /dev/null and b/AppKit/Resources/CPImageView/CPImageViewTopLeftShadow.png differ diff --git a/AppKit/Resources/CPImageView/CPImageViewTopRightShadow.png b/AppKit/Resources/CPImageView/CPImageViewTopRightShadow.png new file mode 100644 index 0000000000..818d7f73d6 Binary files /dev/null and b/AppKit/Resources/CPImageView/CPImageViewTopRightShadow.png differ diff --git a/AppKit/Resources/CPImageView/CPImageViewTopShadow.png b/AppKit/Resources/CPImageView/CPImageViewTopShadow.png new file mode 100644 index 0000000000..bb61c1e99c Binary files /dev/null and b/AppKit/Resources/CPImageView/CPImageViewTopShadow.png differ diff --git a/AppKit/Resources/CPMenuItem/CPMenuItemOnState.png b/AppKit/Resources/CPMenuItem/CPMenuItemOnState.png new file mode 100644 index 0000000000..102f335949 Binary files /dev/null and b/AppKit/Resources/CPMenuItem/CPMenuItemOnState.png differ diff --git a/AppKit/Resources/CPMenuItem/CPMenuItemOnStateHighlighted.png b/AppKit/Resources/CPMenuItem/CPMenuItemOnStateHighlighted.png new file mode 100644 index 0000000000..116fe0550b Binary files /dev/null and b/AppKit/Resources/CPMenuItem/CPMenuItemOnStateHighlighted.png differ diff --git a/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBarBarRegular.png b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBarBarRegular.png new file mode 100644 index 0000000000..df417762c4 Binary files /dev/null and b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBarBarRegular.png differ diff --git a/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBarHUDBarSmall.png b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBarHUDBarSmall.png new file mode 100644 index 0000000000..670fae0a6b Binary files /dev/null and b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBarHUDBarSmall.png differ diff --git a/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderBarRegular0.png b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderBarRegular0.png new file mode 100644 index 0000000000..b7b8ecdf2f Binary files /dev/null and b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderBarRegular0.png differ diff --git a/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderBarRegular1.png b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderBarRegular1.png new file mode 100644 index 0000000000..cb835255b9 Binary files /dev/null and b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderBarRegular1.png differ diff --git a/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderBarRegular2.png b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderBarRegular2.png new file mode 100644 index 0000000000..5be2d77460 Binary files /dev/null and b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderBarRegular2.png differ diff --git a/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderHUDBarSmall0.png b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderHUDBarSmall0.png new file mode 100644 index 0000000000..113dbb5cfe Binary files /dev/null and b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderHUDBarSmall0.png differ diff --git a/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderHUDBarSmall1.png b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderHUDBarSmall1.png new file mode 100644 index 0000000000..06855916ca Binary files /dev/null and b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderHUDBarSmall1.png differ diff --git a/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderHUDBarSmall2.png b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderHUDBarSmall2.png new file mode 100644 index 0000000000..04756d6862 Binary files /dev/null and b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorBezelBorderHUDBarSmall2.png differ diff --git a/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif new file mode 100644 index 0000000000..0d2567cd01 Binary files /dev/null and b/AppKit/Resources/CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif differ diff --git a/AppKit/Resources/CPScroller/CPScrollerDecrementArrowHorizontalRegular.png b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowHorizontalRegular.png new file mode 100644 index 0000000000..9ab26e2fc2 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowHorizontalRegular.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerDecrementArrowHorizontalRegularHighlighted.png b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowHorizontalRegularHighlighted.png new file mode 100644 index 0000000000..3fa5fe90a9 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowHorizontalRegularHighlighted.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalRegular.png b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalRegular.png new file mode 100644 index 0000000000..6421e74263 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalRegular.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalRegularHighlighted.png b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalRegularHighlighted.png new file mode 100644 index 0000000000..e964cfdb27 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalRegularHighlighted.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalSmall.png b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalSmall.png new file mode 100644 index 0000000000..4fd954e864 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalSmall.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalSmallHighlighted.png b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalSmallHighlighted.png new file mode 100644 index 0000000000..71f5c96d1e Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerDecrementArrowVerticalSmallHighlighted.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerIncrementArrowHorizontalRegular.png b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowHorizontalRegular.png new file mode 100644 index 0000000000..3e42fd3ccb Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowHorizontalRegular.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerIncrementArrowHorizontalRegularHighlighted.png b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowHorizontalRegularHighlighted.png new file mode 100644 index 0000000000..91786190b5 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowHorizontalRegularHighlighted.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalRegular.png b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalRegular.png new file mode 100644 index 0000000000..fc028f3633 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalRegular.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalRegularHighlighted.png b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalRegularHighlighted.png new file mode 100644 index 0000000000..13006d9a3f Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalRegularHighlighted.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalSmall.png b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalSmall.png new file mode 100644 index 0000000000..21032b6420 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalSmall.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalSmallHighlighted.png b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalSmallHighlighted.png new file mode 100644 index 0000000000..76b77558e0 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerIncrementArrowVerticalSmallHighlighted.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobHorizontalRegular0.png b/AppKit/Resources/CPScroller/CPScrollerKnobHorizontalRegular0.png new file mode 100644 index 0000000000..52de3bafe8 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobHorizontalRegular0.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobHorizontalRegular1.png b/AppKit/Resources/CPScroller/CPScrollerKnobHorizontalRegular1.png new file mode 100644 index 0000000000..25045c1667 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobHorizontalRegular1.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobHorizontalRegular2.png b/AppKit/Resources/CPScroller/CPScrollerKnobHorizontalRegular2.png new file mode 100644 index 0000000000..913b0d9ea4 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobHorizontalRegular2.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobSlotHorizontalRegular.png b/AppKit/Resources/CPScroller/CPScrollerKnobSlotHorizontalRegular.png new file mode 100644 index 0000000000..60c5300b9d Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobSlotHorizontalRegular.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobSlotVerticalRegular.png b/AppKit/Resources/CPScroller/CPScrollerKnobSlotVerticalRegular.png new file mode 100644 index 0000000000..8dff1370b4 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobSlotVerticalRegular.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobSlotVerticalSmall.png b/AppKit/Resources/CPScroller/CPScrollerKnobSlotVerticalSmall.png new file mode 100644 index 0000000000..181b1debb6 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobSlotVerticalSmall.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobVerticalRegular0.png b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalRegular0.png new file mode 100644 index 0000000000..3ddbaca6f4 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalRegular0.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobVerticalRegular1.png b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalRegular1.png new file mode 100644 index 0000000000..c6b874e4f5 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalRegular1.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobVerticalRegular2.png b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalRegular2.png new file mode 100644 index 0000000000..2100b9992d Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalRegular2.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobVerticalSmall0.png b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalSmall0.png new file mode 100644 index 0000000000..2c1ccc17fb Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalSmall0.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobVerticalSmall1.png b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalSmall1.png new file mode 100644 index 0000000000..534c519835 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalSmall1.png differ diff --git a/AppKit/Resources/CPScroller/CPScrollerKnobVerticalSmall2.png b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalSmall2.png new file mode 100644 index 0000000000..a6aa8d5803 Binary files /dev/null and b/AppKit/Resources/CPScroller/CPScrollerKnobVerticalSmall2.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewHeavyBottom.png b/AppKit/Resources/CPShadowView/CPShadowViewHeavyBottom.png new file mode 100644 index 0000000000..8b3313d5c8 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewHeavyBottom.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewHeavyBottomLeft.png b/AppKit/Resources/CPShadowView/CPShadowViewHeavyBottomLeft.png new file mode 100644 index 0000000000..f4d8065a7f Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewHeavyBottomLeft.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewHeavyBottomRight.png b/AppKit/Resources/CPShadowView/CPShadowViewHeavyBottomRight.png new file mode 100644 index 0000000000..77718b1c74 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewHeavyBottomRight.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewHeavyLeft.png b/AppKit/Resources/CPShadowView/CPShadowViewHeavyLeft.png new file mode 100644 index 0000000000..8a3e888000 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewHeavyLeft.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewHeavyRight.png b/AppKit/Resources/CPShadowView/CPShadowViewHeavyRight.png new file mode 100644 index 0000000000..99ea883d3b Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewHeavyRight.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewHeavyTop.png b/AppKit/Resources/CPShadowView/CPShadowViewHeavyTop.png new file mode 100644 index 0000000000..25c178e1fb Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewHeavyTop.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewHeavyTopLeft.png b/AppKit/Resources/CPShadowView/CPShadowViewHeavyTopLeft.png new file mode 100644 index 0000000000..4915d52760 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewHeavyTopLeft.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewHeavyTopRight.png b/AppKit/Resources/CPShadowView/CPShadowViewHeavyTopRight.png new file mode 100644 index 0000000000..2302e9e988 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewHeavyTopRight.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewLightBottom.png b/AppKit/Resources/CPShadowView/CPShadowViewLightBottom.png new file mode 100644 index 0000000000..5fc3ef0216 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewLightBottom.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewLightBottomLeft.png b/AppKit/Resources/CPShadowView/CPShadowViewLightBottomLeft.png new file mode 100644 index 0000000000..654ef3cc6c Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewLightBottomLeft.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewLightBottomRight.png b/AppKit/Resources/CPShadowView/CPShadowViewLightBottomRight.png new file mode 100644 index 0000000000..93780f243b Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewLightBottomRight.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewLightLeft.png b/AppKit/Resources/CPShadowView/CPShadowViewLightLeft.png new file mode 100644 index 0000000000..d0f29b7846 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewLightLeft.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewLightRight.png b/AppKit/Resources/CPShadowView/CPShadowViewLightRight.png new file mode 100644 index 0000000000..1b3cf77573 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewLightRight.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewLightTop.png b/AppKit/Resources/CPShadowView/CPShadowViewLightTop.png new file mode 100644 index 0000000000..bf630ff795 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewLightTop.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewLightTopLeft.png b/AppKit/Resources/CPShadowView/CPShadowViewLightTopLeft.png new file mode 100644 index 0000000000..23305646de Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewLightTopLeft.png differ diff --git a/AppKit/Resources/CPShadowView/CPShadowViewLightTopRight.png b/AppKit/Resources/CPShadowView/CPShadowViewLightTopRight.png new file mode 100644 index 0000000000..818d7f73d6 Binary files /dev/null and b/AppKit/Resources/CPShadowView/CPShadowViewLightTopRight.png differ diff --git a/AppKit/Resources/CPSlider/CPSliderKnobRegular.png b/AppKit/Resources/CPSlider/CPSliderKnobRegular.png new file mode 100644 index 0000000000..f2c3923d42 Binary files /dev/null and b/AppKit/Resources/CPSlider/CPSliderKnobRegular.png differ diff --git a/AppKit/Resources/CPSlider/CPSliderKnobRegularPushed.png b/AppKit/Resources/CPSlider/CPSliderKnobRegularPushed.png new file mode 100644 index 0000000000..b6612ec3a2 Binary files /dev/null and b/AppKit/Resources/CPSlider/CPSliderKnobRegularPushed.png differ diff --git a/AppKit/Resources/CPSlider/CPSliderTrackHorizontalCenter.png b/AppKit/Resources/CPSlider/CPSliderTrackHorizontalCenter.png new file mode 100644 index 0000000000..db14d24efc Binary files /dev/null and b/AppKit/Resources/CPSlider/CPSliderTrackHorizontalCenter.png differ diff --git a/AppKit/Resources/CPSlider/CPSliderTrackHorizontalLeft.png b/AppKit/Resources/CPSlider/CPSliderTrackHorizontalLeft.png new file mode 100644 index 0000000000..e2a9b819a0 Binary files /dev/null and b/AppKit/Resources/CPSlider/CPSliderTrackHorizontalLeft.png differ diff --git a/AppKit/Resources/CPSlider/CPSliderTrackHorizontalRight.png b/AppKit/Resources/CPSlider/CPSliderTrackHorizontalRight.png new file mode 100644 index 0000000000..5d4f890b96 Binary files /dev/null and b/AppKit/Resources/CPSlider/CPSliderTrackHorizontalRight.png differ diff --git a/AppKit/Resources/CPTabView/CPTabViewBezelBackgroundCenter.png b/AppKit/Resources/CPTabView/CPTabViewBezelBackgroundCenter.png new file mode 100644 index 0000000000..82bbfbf991 Binary files /dev/null and b/AppKit/Resources/CPTabView/CPTabViewBezelBackgroundCenter.png differ diff --git a/AppKit/Resources/CPTabView/CPTabViewBezelBorder.png b/AppKit/Resources/CPTabView/CPTabViewBezelBorder.png new file mode 100644 index 0000000000..4fc4179058 Binary files /dev/null and b/AppKit/Resources/CPTabView/CPTabViewBezelBorder.png differ diff --git a/AppKit/Resources/CPTabView/CPTabViewBezelBorderLeft.png b/AppKit/Resources/CPTabView/CPTabViewBezelBorderLeft.png new file mode 100644 index 0000000000..a4ae8cf744 Binary files /dev/null and b/AppKit/Resources/CPTabView/CPTabViewBezelBorderLeft.png differ diff --git a/AppKit/Resources/CPTabView/CPTabViewBezelBorderRight.png b/AppKit/Resources/CPTabView/CPTabViewBezelBorderRight.png new file mode 100644 index 0000000000..a25377120b Binary files /dev/null and b/AppKit/Resources/CPTabView/CPTabViewBezelBorderRight.png differ diff --git a/AppKit/Resources/CPTabView/_CPTabLabelBackgroundCenter.png b/AppKit/Resources/CPTabView/_CPTabLabelBackgroundCenter.png new file mode 100644 index 0000000000..7436399566 Binary files /dev/null and b/AppKit/Resources/CPTabView/_CPTabLabelBackgroundCenter.png differ diff --git a/AppKit/Resources/CPTabView/_CPTabLabelBackgroundLeft.png b/AppKit/Resources/CPTabView/_CPTabLabelBackgroundLeft.png new file mode 100644 index 0000000000..5a0edfc9ec Binary files /dev/null and b/AppKit/Resources/CPTabView/_CPTabLabelBackgroundLeft.png differ diff --git a/AppKit/Resources/CPTabView/_CPTabLabelBackgroundRight.png b/AppKit/Resources/CPTabView/_CPTabLabelBackgroundRight.png new file mode 100644 index 0000000000..381d414a1e Binary files /dev/null and b/AppKit/Resources/CPTabView/_CPTabLabelBackgroundRight.png differ diff --git a/AppKit/Resources/CPTabView/_CPTabLabelSelectedCenter.png b/AppKit/Resources/CPTabView/_CPTabLabelSelectedCenter.png new file mode 100644 index 0000000000..9fe80236b6 Binary files /dev/null and b/AppKit/Resources/CPTabView/_CPTabLabelSelectedCenter.png differ diff --git a/AppKit/Resources/CPTabView/_CPTabLabelSelectedLeft.png b/AppKit/Resources/CPTabView/_CPTabLabelSelectedLeft.png new file mode 100644 index 0000000000..3c0d4a17b1 Binary files /dev/null and b/AppKit/Resources/CPTabView/_CPTabLabelSelectedLeft.png differ diff --git a/AppKit/Resources/CPTabView/_CPTabLabelSelectedRight.png b/AppKit/Resources/CPTabView/_CPTabLabelSelectedRight.png new file mode 100644 index 0000000000..3388ca886b Binary files /dev/null and b/AppKit/Resources/CPTabView/_CPTabLabelSelectedRight.png differ diff --git a/AppKit/Resources/CPTabView/_CPTabLabelsViewCenter.png b/AppKit/Resources/CPTabView/_CPTabLabelsViewCenter.png new file mode 100644 index 0000000000..19d8e74632 Binary files /dev/null and b/AppKit/Resources/CPTabView/_CPTabLabelsViewCenter.png differ diff --git a/AppKit/Resources/CPTabView/_CPTabLabelsViewLeft.png b/AppKit/Resources/CPTabView/_CPTabLabelsViewLeft.png new file mode 100644 index 0000000000..348cfb9176 Binary files /dev/null and b/AppKit/Resources/CPTabView/_CPTabLabelsViewLeft.png differ diff --git a/AppKit/Resources/CPTabView/_CPTabLabelsViewRight.png b/AppKit/Resources/CPTabView/_CPTabLabelsViewRight.png new file mode 100644 index 0000000000..b9973d4b09 Binary files /dev/null and b/AppKit/Resources/CPTabView/_CPTabLabelsViewRight.png differ diff --git a/AppKit/Resources/CPTextField/CPTextFieldBezelSquare0.png b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare0.png new file mode 100644 index 0000000000..1819bb3a8c Binary files /dev/null and b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare0.png differ diff --git a/AppKit/Resources/CPTextField/CPTextFieldBezelSquare1.png b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare1.png new file mode 100644 index 0000000000..2c77bb764b Binary files /dev/null and b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare1.png differ diff --git a/AppKit/Resources/CPTextField/CPTextFieldBezelSquare2.png b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare2.png new file mode 100644 index 0000000000..848e1da44d Binary files /dev/null and b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare2.png differ diff --git a/AppKit/Resources/CPTextField/CPTextFieldBezelSquare3.png b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare3.png new file mode 100644 index 0000000000..81cd274512 Binary files /dev/null and b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare3.png differ diff --git a/AppKit/Resources/CPTextField/CPTextFieldBezelSquare4.png b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare4.png new file mode 100644 index 0000000000..9432d30c91 Binary files /dev/null and b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare4.png differ diff --git a/AppKit/Resources/CPTextField/CPTextFieldBezelSquare5.png b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare5.png new file mode 100644 index 0000000000..21f81000ed Binary files /dev/null and b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare5.png differ diff --git a/AppKit/Resources/CPTextField/CPTextFieldBezelSquare6.png b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare6.png new file mode 100644 index 0000000000..acc67dfa27 Binary files /dev/null and b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare6.png differ diff --git a/AppKit/Resources/CPTextField/CPTextFieldBezelSquare7.png b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare7.png new file mode 100644 index 0000000000..0c228f14c0 Binary files /dev/null and b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare7.png differ diff --git a/AppKit/Resources/CPTextField/CPTextFieldBezelSquare8.png b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare8.png new file mode 100644 index 0000000000..07f2197287 Binary files /dev/null and b/AppKit/Resources/CPTextField/CPTextFieldBezelSquare8.png differ diff --git a/AppKit/Resources/CPWindow/CPWindowShadow0.png b/AppKit/Resources/CPWindow/CPWindowShadow0.png new file mode 100644 index 0000000000..d47d315954 Binary files /dev/null and b/AppKit/Resources/CPWindow/CPWindowShadow0.png differ diff --git a/AppKit/Resources/CPWindow/CPWindowShadow1.png b/AppKit/Resources/CPWindow/CPWindowShadow1.png new file mode 100644 index 0000000000..fc74aea29c Binary files /dev/null and b/AppKit/Resources/CPWindow/CPWindowShadow1.png differ diff --git a/AppKit/Resources/CPWindow/CPWindowShadow2.png b/AppKit/Resources/CPWindow/CPWindowShadow2.png new file mode 100644 index 0000000000..9764d1bddd Binary files /dev/null and b/AppKit/Resources/CPWindow/CPWindowShadow2.png differ diff --git a/AppKit/Resources/CPWindow/CPWindowShadow3.png b/AppKit/Resources/CPWindow/CPWindowShadow3.png new file mode 100644 index 0000000000..4565f2bcb1 Binary files /dev/null and b/AppKit/Resources/CPWindow/CPWindowShadow3.png differ diff --git a/AppKit/Resources/CPWindow/CPWindowShadow4.png b/AppKit/Resources/CPWindow/CPWindowShadow4.png new file mode 100644 index 0000000000..d61c0f3438 Binary files /dev/null and b/AppKit/Resources/CPWindow/CPWindowShadow4.png differ diff --git a/AppKit/Resources/CPWindow/CPWindowShadow5.png b/AppKit/Resources/CPWindow/CPWindowShadow5.png new file mode 100644 index 0000000000..f32d20b8d9 Binary files /dev/null and b/AppKit/Resources/CPWindow/CPWindowShadow5.png differ diff --git a/AppKit/Resources/CPWindow/CPWindowShadow6.png b/AppKit/Resources/CPWindow/CPWindowShadow6.png new file mode 100644 index 0000000000..5208801f11 Binary files /dev/null and b/AppKit/Resources/CPWindow/CPWindowShadow6.png differ diff --git a/AppKit/Resources/CPWindow/CPWindowShadow7.png b/AppKit/Resources/CPWindow/CPWindowShadow7.png new file mode 100644 index 0000000000..c3a00f6964 Binary files /dev/null and b/AppKit/Resources/CPWindow/CPWindowShadow7.png differ diff --git a/AppKit/Resources/CPWindow/CPWindowShadow8.png b/AppKit/Resources/CPWindow/CPWindowShadow8.png new file mode 100644 index 0000000000..5f007de9d3 Binary files /dev/null and b/AppKit/Resources/CPWindow/CPWindowShadow8.png differ diff --git a/AppKit/Resources/CPWindowResizeIndicator.png b/AppKit/Resources/CPWindowResizeIndicator.png new file mode 100644 index 0000000000..6773128a59 Binary files /dev/null and b/AppKit/Resources/CPWindowResizeIndicator.png differ diff --git a/AppKit/Resources/FIXME_ImageShadow.png b/AppKit/Resources/FIXME_ImageShadow.png new file mode 100644 index 0000000000..f544c24ac6 Binary files /dev/null and b/AppKit/Resources/FIXME_ImageShadow.png differ diff --git a/AppKit/Resources/HUDTheme/CPSliderHorizontalBarCenter.png b/AppKit/Resources/HUDTheme/CPSliderHorizontalBarCenter.png new file mode 100644 index 0000000000..8d2e5aac8c Binary files /dev/null and b/AppKit/Resources/HUDTheme/CPSliderHorizontalBarCenter.png differ diff --git a/AppKit/Resources/HUDTheme/CPSliderHorizontalBarLeft.png b/AppKit/Resources/HUDTheme/CPSliderHorizontalBarLeft.png new file mode 100644 index 0000000000..8458fe0222 Binary files /dev/null and b/AppKit/Resources/HUDTheme/CPSliderHorizontalBarLeft.png differ diff --git a/AppKit/Resources/HUDTheme/CPSliderHorizontalBarRight.png b/AppKit/Resources/HUDTheme/CPSliderHorizontalBarRight.png new file mode 100644 index 0000000000..ff4f979686 Binary files /dev/null and b/AppKit/Resources/HUDTheme/CPSliderHorizontalBarRight.png differ diff --git a/AppKit/Resources/HUDTheme/CPSliderHorizontalKnob.png b/AppKit/Resources/HUDTheme/CPSliderHorizontalKnob.png new file mode 100644 index 0000000000..6752bc2084 Binary files /dev/null and b/AppKit/Resources/HUDTheme/CPSliderHorizontalKnob.png differ diff --git a/AppKit/Resources/HUDTheme/WindowBottomCenter.png b/AppKit/Resources/HUDTheme/WindowBottomCenter.png new file mode 100644 index 0000000000..ca64f73a94 Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowBottomCenter.png differ diff --git a/AppKit/Resources/HUDTheme/WindowBottomLeft.png b/AppKit/Resources/HUDTheme/WindowBottomLeft.png new file mode 100644 index 0000000000..521b7c6f81 Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowBottomLeft.png differ diff --git a/AppKit/Resources/HUDTheme/WindowBottomRight.png b/AppKit/Resources/HUDTheme/WindowBottomRight.png new file mode 100644 index 0000000000..ac800b041f Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowBottomRight.png differ diff --git a/AppKit/Resources/HUDTheme/WindowCenter.png b/AppKit/Resources/HUDTheme/WindowCenter.png new file mode 100644 index 0000000000..5b9ae35e2c Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowCenter.png differ diff --git a/AppKit/Resources/HUDTheme/WindowCenterLeft.png b/AppKit/Resources/HUDTheme/WindowCenterLeft.png new file mode 100644 index 0000000000..4d6b63fff4 Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowCenterLeft.png differ diff --git a/AppKit/Resources/HUDTheme/WindowCenterRight.png b/AppKit/Resources/HUDTheme/WindowCenterRight.png new file mode 100644 index 0000000000..750eb71318 Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowCenterRight.png differ diff --git a/AppKit/Resources/HUDTheme/WindowClose.png b/AppKit/Resources/HUDTheme/WindowClose.png new file mode 100644 index 0000000000..0f45a70e97 Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowClose.png differ diff --git a/AppKit/Resources/HUDTheme/WindowCloseActive.png b/AppKit/Resources/HUDTheme/WindowCloseActive.png new file mode 100644 index 0000000000..94771fcbfb Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowCloseActive.png differ diff --git a/AppKit/Resources/HUDTheme/WindowTopCenter.png b/AppKit/Resources/HUDTheme/WindowTopCenter.png new file mode 100644 index 0000000000..0b89f1b010 Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowTopCenter.png differ diff --git a/AppKit/Resources/HUDTheme/WindowTopLeft.png b/AppKit/Resources/HUDTheme/WindowTopLeft.png new file mode 100644 index 0000000000..290db9241c Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowTopLeft.png differ diff --git a/AppKit/Resources/HUDTheme/WindowTopRight.png b/AppKit/Resources/HUDTheme/WindowTopRight.png new file mode 100644 index 0000000000..dbb8e39b9c Binary files /dev/null and b/AppKit/Resources/HUDTheme/WindowTopRight.png differ diff --git a/AppKit/Resources/_CPMenuBarWindow/_CPMenuBarWindowBackground.png b/AppKit/Resources/_CPMenuBarWindow/_CPMenuBarWindowBackground.png new file mode 100644 index 0000000000..39380490ab Binary files /dev/null and b/AppKit/Resources/_CPMenuBarWindow/_CPMenuBarWindowBackground.png differ diff --git a/AppKit/Resources/_CPMenuItemView/_CPMenuItemViewMenuBarArrow.png b/AppKit/Resources/_CPMenuItemView/_CPMenuItemViewMenuBarArrow.png new file mode 100644 index 0000000000..27ac7e02e8 Binary files /dev/null and b/AppKit/Resources/_CPMenuItemView/_CPMenuItemViewMenuBarArrow.png differ diff --git a/AppKit/Resources/_CPMenuItemView/_CPMenuItemViewMenuBarArrowActivated.png b/AppKit/Resources/_CPMenuItemView/_CPMenuItemViewMenuBarArrowActivated.png new file mode 100644 index 0000000000..33dc63b20c Binary files /dev/null and b/AppKit/Resources/_CPMenuItemView/_CPMenuItemViewMenuBarArrowActivated.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindow1.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow1.png new file mode 100644 index 0000000000..438f284c6f Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow1.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindow3.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow3.png new file mode 100644 index 0000000000..52efc8ff17 Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow3.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindow4.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow4.png new file mode 100644 index 0000000000..0b952a9942 Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow4.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindow5.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow5.png new file mode 100644 index 0000000000..a9a7257fff Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow5.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindow7.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow7.png new file mode 100644 index 0000000000..c577e8bbf4 Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindow7.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindowMoreAbove.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowMoreAbove.png new file mode 100644 index 0000000000..1058cb9cfd Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowMoreAbove.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindowMoreBelow.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowMoreBelow.png new file mode 100644 index 0000000000..bbaca54760 Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowMoreBelow.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded0.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded0.png new file mode 100644 index 0000000000..0892087aac Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded0.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded2.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded2.png new file mode 100644 index 0000000000..dd79b1e22b Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded2.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded6.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded6.png new file mode 100644 index 0000000000..e0846a6422 Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded6.png differ diff --git a/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded8.png b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded8.png new file mode 100644 index 0000000000..3cff81e125 Binary files /dev/null and b/AppKit/Resources/_CPMenuWindow/_CPMenuWindowRounded8.png differ diff --git a/AppKit/Resources/_CPToolbarView/_CPToolbarViewBackground.png b/AppKit/Resources/_CPToolbarView/_CPToolbarViewBackground.png new file mode 100644 index 0000000000..ce3d59280a Binary files /dev/null and b/AppKit/Resources/_CPToolbarView/_CPToolbarViewBackground.png differ diff --git a/AppKit/Resources/_CPToolbarView/_CPToolbarViewExtraItemsAlternateImage.png b/AppKit/Resources/_CPToolbarView/_CPToolbarViewExtraItemsAlternateImage.png new file mode 100644 index 0000000000..a73d137549 Binary files /dev/null and b/AppKit/Resources/_CPToolbarView/_CPToolbarViewExtraItemsAlternateImage.png differ diff --git a/AppKit/Resources/_CPToolbarView/_CPToolbarViewExtraItemsImage.png b/AppKit/Resources/_CPToolbarView/_CPToolbarViewExtraItemsImage.png new file mode 100644 index 0000000000..5182b38cc5 Binary files /dev/null and b/AppKit/Resources/_CPToolbarView/_CPToolbarViewExtraItemsImage.png differ diff --git a/AppKit/Resources/_CPWindowView/_CPWindowViewResizeIndicator.png b/AppKit/Resources/_CPWindowView/_CPWindowViewResizeIndicator.png new file mode 100644 index 0000000000..6773128a59 Binary files /dev/null and b/AppKit/Resources/_CPWindowView/_CPWindowViewResizeIndicator.png differ diff --git a/AppKit/Resources/brightness_bar.png b/AppKit/Resources/brightness_bar.png new file mode 100644 index 0000000000..95898fe036 Binary files /dev/null and b/AppKit/Resources/brightness_bar.png differ diff --git a/AppKit/Resources/color_well.png b/AppKit/Resources/color_well.png new file mode 100644 index 0000000000..54444a6d6d Binary files /dev/null and b/AppKit/Resources/color_well.png differ diff --git a/AppKit/Resources/kuler_button.png b/AppKit/Resources/kuler_button.png new file mode 100644 index 0000000000..bd766c45f3 Binary files /dev/null and b/AppKit/Resources/kuler_button.png differ diff --git a/AppKit/Resources/kuler_button_h.png b/AppKit/Resources/kuler_button_h.png new file mode 100644 index 0000000000..84b2866ed5 Binary files /dev/null and b/AppKit/Resources/kuler_button_h.png differ diff --git a/AppKit/Resources/slider_button.png b/AppKit/Resources/slider_button.png new file mode 100644 index 0000000000..aea0e9ed43 Binary files /dev/null and b/AppKit/Resources/slider_button.png differ diff --git a/AppKit/Resources/slider_button_h.png b/AppKit/Resources/slider_button_h.png new file mode 100644 index 0000000000..3309cecda3 Binary files /dev/null and b/AppKit/Resources/slider_button_h.png differ diff --git a/AppKit/Resources/wheel.png b/AppKit/Resources/wheel.png new file mode 100644 index 0000000000..939a9ebad9 Binary files /dev/null and b/AppKit/Resources/wheel.png differ diff --git a/AppKit/Resources/wheel_black.png b/AppKit/Resources/wheel_black.png new file mode 100644 index 0000000000..822ec955c5 Binary files /dev/null and b/AppKit/Resources/wheel_black.png differ diff --git a/AppKit/Resources/wheel_button.png b/AppKit/Resources/wheel_button.png new file mode 100644 index 0000000000..02f420f44a Binary files /dev/null and b/AppKit/Resources/wheel_button.png differ diff --git a/AppKit/Resources/wheel_button_h.png b/AppKit/Resources/wheel_button_h.png new file mode 100644 index 0000000000..a6fc155844 Binary files /dev/null and b/AppKit/Resources/wheel_button_h.png differ diff --git a/AppKit/_CPImageAndTitleView.j b/AppKit/_CPImageAndTitleView.j new file mode 100644 index 0000000000..9a950a0037 --- /dev/null +++ b/AppKit/_CPImageAndTitleView.j @@ -0,0 +1,399 @@ +/* + * _CPImageAndTitleView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + +import "CPColor.j" +import "CPFont.j" +import "CPImage.j" +import "CPTextField.j" +import "CPView.j" + +#include "Platform/Platform.h" +#include "Platform/DOM/CPDOMDisplayServer.h" + + +@implementation _CPImageAndTitleView : CPView +{ + CPTextAlignment _alignment; + CPColor _textColor; + CPFont _font; + + CPCellImagePosition _imagePosition; + CPImageScaling _imageScalng; + + CPImage _image; + CPString _title; + + CGRect _titleSize; + +#if PLATFORM(DOM) + DOMElement _DOMImageElement; +#endif + CPTextField _titleField; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _alignment = CPCenterTextAlignment; + _textColor = nil; + _font = [CPFont systemFontOfSize:12.0]; + + _imagePosition = CPNoImage; + _imageScaling = CPScaleNone; + + _titleSize = CGSizeMakeZero(); + } + + return self; +} + +- (void)setAlignment:(CPTextAlignment)anAlignment +{ + _alignment = anAlignment; + + [_titleField setAlignment:anAlignment]; +} + +- (CPTextAlignment)alignment +{ + return _alignment; +} + +- (void)setImagePosition:(CPCellImagePosition)anImagePosition +{ + if (_imagePosition == anImagePosition) + return; + + _imagePosition = anImagePosition; + + if (_imagePosition == CPImageOnly) + [_titleField setHidden:YES]; + else + [_titleField setHidden:NO]; + + [self tile]; +} + +- (CPCellImagePosition)imagePosition +{ + return _imagePosition; +} + +- (void)setImageScaling:(CPImageScaling)anImageScaling +{ + if (_imageScaling == anImageScaling) + return; + + _imageScaling = anImageScaling; + + [self tile]; +} + +- (void)imageScaling +{ + return _imageScaling; +} + +- (void)setTextAlignment:(CPTextAlignment)aTextAlignment +{ + if (_alignment == aTextAlignment) + return; + + _alignment = aTextAlignment; + [_titleField setTextAlignment:aTextAlignment]; +} + +- (CPTextAlignment)textAlignment +{ + return _alignment; +} + +- (void)setTextColor:(CPColor)aTextColor +{ + if (_textColor == aTextColor) + return; + + _textColor = aTextColor; + [_titleField setTextColor:aTextColor]; +} + +- (CPColor)textColor +{ + return _textColor; +} + +- (void)setFont:(CPFont)aFont +{ + if (_font == aFont) + return; + + _font = aFont; + [_titleField setFont:aFont]; + + [self updateTitleSize]; + + [self tile]; +} + +- (CPFont)font +{ + return _font; +} + +- (void)setImage:(CPImage)anImage +{ + if (_image == anImage) + return; + + var oldSize = [_image size], + newSize = [anImage size]; + + _image = anImage; + + if (anImage) + { +#if PLATFORM(DOM) + if (!_DOMImageElement) + { + _DOMImageElement = document.createElement("img"); + + _DOMImageElement.style.top = "0px"; + _DOMImageElement.style.left = "0px"; + _DOMImageElement.style.position = "absolute"; + _DOMImageElement.style.zIndex = 100; + + _DOMElement.appendChild(_DOMImageElement); + } + + _DOMImageElement.src = [_image filename]; +#endif + + if (oldSize && CGSizeEqualToSize(newSize, oldSize)) + return; + +#if PLATFORM(DOM) + _DOMImageElement.width = newSize.width; + _DOMImageElement.height = newSize.height; + _DOMImageElement.style.width = newSize.width + "px"; + _DOMImageElement.style.height = newSize.height + "px"; +#endif + } + else + { +#if PLATFORM(DOM) + _DOMElement.removeChild(_DOMImageElement); + + _DOMImageElement = NULL; +#endif + } + + [self tile]; +} + +- (CPImage)image +{ + return _image; +} + +- (void)setTitle:(CPString)aTitle +{ + if (_title == aTitle) + return; + + _title = aTitle; + + if ([_title length]) + { + if (!_titleField) + { + _titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; + + [_titleField setEditable:NO]; + + [_titleField setFont:_font]; + [_titleField setAlignment:_alignment]; + [_titleField setTextColor:_textColor]; + [_titleField setHidden:_imagePosition == CPImageOnly]; + + [self addSubview:_titleField]; + } + + [_titleField setStringValue:_title]; + + [self updateTitleSize]; + } + else + { + [_titleField removeFromSuperview]; + + _titleField = nil; + } + + [self tile]; +} + +- (CPString)title +{ + return _title; +} + +- (void)updateTitleSize +{ + if (!_titleField) + return; + + var size = _titleSize; + + [_titleField sizeToFit]; + + _titleSize = CGSizeMakeCopy([_titleField frame].size); + + [_titleField setFrameSize:size]; +} + +- (void)tile +{ + var size = [self bounds].size, + centerX = size.width / 2.0, + centerY = size.height / 2.0, + titleHeight = _titleField ? _titleSize.height : 0.0, + titleRect = CGRectMake(0.0, centerY - titleHeight / 2.0, size.width, titleHeight); + + if (_imagePosition != CPNoImage && _image) + { + var imageSize = [_image size], + imageWidth = imageSize.width, + imageHeight = imageSize.height; + + if (_imageScaling == CPScaleToFit) + { + imageWidth = size.width; + imageHeight = size.height; + } + else if (_imageScaling == CPScaleProportionally) + { + var scale = MIN(MIN(size.width, imageWidth) / imageWidth, MIN(size.height, imageHeight) / imageHeight); + + imageWidth *= scale; + imageHeight *= scale; + } + +#if PLATFORM(DOM) + _DOMImageElement.width = imageWidth; + _DOMImageElement.height = imageHeight; + _DOMImageElement.style.width = imageWidth + "px"; + _DOMImageElement.style.height = imageHeight + "px"; +#endif + + if (_imagePosition == CPImageBelow) + { +#if PLATFORM(DOM) + _DOMImageElement.style.left = FLOOR(centerX - imageWidth / 2.0) + "px"; + _DOMImageElement.style.top = FLOOR(size.height - imageHeight) + "px"; +#endif + + titleRect.origin.y = (size.height - imageHeight - titleHeight) / 2.0; + } + else if (_imagePosition == CPImageAbove) + { +#if PLATFORM(DOM) + CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, FLOOR(centerX - imageWidth / 2.0), 0); +#endif + + titleRect.origin.y = imageHeight + (size.height - imageHeight - titleHeight) / 2.0; + } + else if (_imagePosition == CPImageLeft) + { +#if PLATFORM(DOM) + _DOMImageElement.style.top = FLOOR(centerY - imageHeight / 2.0) + "px"; + _DOMImageElement.style.left = "0px"; +#endif + + titleRect.origin.x += imageWidth; + titleRect.size.width -= imageWidth; + } + else if (_imagePosition == CPImageRight) + { +#if PLATFORM(DOM) + _DOMImageElement.style.top = FLOOR(centerY - imageHeight / 2.0) + "px"; + _DOMImageElement.style.left = FLOOR(size.width - imageWidth) + "px"; +#endif + + titleRect.size.width -= imageWidth; + } + else if(_imagePosition == CPImageOnly) + { +#if PLATFORM(DOM) + _DOMImageElement.style.top = FLOOR(centerY - imageHeight / 2.0) + "px"; + _DOMImageElement.style.left = FLOOR(centerX - imageWidth / 2.0) + "px"; +#endif + } + } + + [_titleField setFrame:titleRect]; +} + +- (void)sizeToFit +{ + var size = CGSizeMakeZero(); + + if (_imagePosition != CPNoImage && _image) + { + var imageSize = [_image size]; + + size.width += imageSize.width; + size.height += imageSize.height; + } + + if (_imagePosition != CPImageOnly && [_title length]) + { + if (_imagePosition == CPImageLeft || _imagePosition == CPImageRight) + { + size.width += _titleSize.width; + size.height = MAX(size.height, _titleSize.height); + } + else if (_imagePosition == CPImageAbove || _imagePosition == CPImageBelow) + { + size.width = MAX(size.width, _titleSize.width); + size.height += _titleSize.height; + } + else // if (_imagePosition == CPImageOverlaps) + { + size.width = MAX(size.width, _titleSize.width); + size.height = MAX(size.height, _titleSize.height); + } + } + + [self setFrameSize:size]; +} + +- (void)resizeSubviewsWithOldSize:(CGSize)aSize +{ + [self tile]; +} + +@end diff --git a/AppKit/_CPWindowView.j b/AppKit/_CPWindowView.j new file mode 100644 index 0000000000..b0f8c48831 --- /dev/null +++ b/AppKit/_CPWindowView.j @@ -0,0 +1,270 @@ +/* + * _CPWindowView.j + * AppKit + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPView.j" +import "CPImageView.j" + + +var _CPWindowViewResizeIndicatorImage = nil; + +@implementation _CPWindowView : CPView +{ + unsigned _styleMask; + + CPImageView _resizeIndicator; + + CPString _title; +} + ++ (void)initialize +{ + if (self != [_CPWindowView class]) + return; + + _CPWindowViewResizeIndicatorImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:self] pathForResource:@"_CPWindowView/_CPWindowViewResizeIndicator.png"] size:CGSizeMake(12.0, 12.0)]; +} + +- (id)initWithFrame:(CPRect)aFrame forStyleMask:(unsigned)aStyleMask +{ + if (aStyleMask & CPHUDBackgroundWindowMask) + self = [_CPHUDWindowView alloc]; + + self._styleMask = aStyleMask; + + return [self initWithFrame:aFrame]; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + _resizeIndicator = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; + + [_resizeIndicator setImage:_CPWindowViewResizeIndicatorImage]; + [_resizeIndicator setFrameSize:CGSizeMakeCopy([_CPWindowViewResizeIndicatorImage size])]; + + [self addSubview:_resizeIndicator]; + + [self setShowsResizeIndicator:_styleMask & CPResizableWindowMask]; + } + + return self; +} + +- (void)setTitle:(CPString)aTitle +{ +} + +- (CPString)title +{ + return nil; +} + +- (BOOL)acceptsFirstMouse:(CPEvent)anEvent +{ + return YES; +} + +- (void)mouseDown:(CPEvent)anEvent +{ + var theWindow = [self window]; + + if (![_resizeIndicator isHidden]) + { + // FIXME: This should be better + var frame = CGRectMakeCopy([self frame]); + + frame.origin.x = CGRectGetWidth(frame) - 20.0; + frame.origin.y = CGRectGetHeight(frame) - 24.0; + frame.size.width = 20.0 - 5.0, + frame.size.height = 24.0 - 8.0; + + if (CGRectContainsPoint(frame, [self convertPoint:[anEvent locationInWindow] fromView:nil])) + return [theWindow trackResizeWithEvent:anEvent]; + } + + if ([theWindow isMovableByWindowBackground]) + [theWindow trackMoveWithEvent:anEvent]; + + else + [super mouseDown:anEvent]; +} + +- (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator +{ + [_resizeIndicator setHidden:!shouldShowResizeIndicator]; +} + +- (CPImage)showsResizeIndicator +{ + return ![_resizeIndicator isHidden]; +} + +- (void)setTitle:(CPString)title +{ + _title = title; +} + +- (CPString)title +{ + return _title; +} + +- (void)windowDidChangeDocumentEdited +{ +} + +- (void)windowDidChangeDocumentSaving +{ +} + +@end + +var _CPHUDWindowViewTopImage = nil, + _CPHUDWindowViewTopLeftImage = nil, + _CPHUDWindowViewTopRightImage = nil, + + _CPHUDWindowViewLeftImage = nil, + _CPHUDWindowViewRightImage = nil, + _CPHUDWindowViewCenterImage = nil, + + _CPHUDWindowViewBottomImage = nil, + _CPHUDWindowViewBottomLeftImage = nil, + _CPHUDWindowViewBottomRightImage = nil, + + _CPHUDWindowViewBackgroundColor = nil, + + CPHUDCloseButtonImage = nil; + +@implementation _CPHUDWindowView : _CPWindowView +{ + CPTextField _titleField; + CPButton _closeButton; +} + ++ (void)initialize +{ + if (self != [_CPHUDWindowView class]) + return; + + var bundle = [CPBundle bundleForClass:self]; + + _CPHUDWindowViewBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices: + [ + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowTopLeft.png"] size:CPSizeMake(15.0, 86.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowTopCenter.png"] size:CPSizeMake(1.0, 86.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowTopRight.png"] size:CPSizeMake(15.0, 86.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCenterLeft.png"] size:CPSizeMake(15.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCenter.png"] size:CPSizeMake(1.0, 1.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCenterRight.png"] size:CPSizeMake(15.0, 1.0)], + + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowBottomLeft.png"] size:CPSizeMake(15.0, 39.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowBottomCenter.png"] size:CPSizeMake(1.0, 39.0)], + [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowBottomRight.png"] size:CPSizeMake(15.0, 39.0)] + ]]]; + + _CPHUDWindowViewCloseImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowClose.png"] size:CPSizeMake(20.0, 20.0)]; + _CPHUDWindowViewCloseActiveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCloseActive.png"] size:CPSizeMake(20.0, 20.0)]; +} + +- (CPView)hitTest:(CPPoint)aPoint +{ + var view = [super hitTest:aPoint]; + + if (view == _titleField) + return self; + + return view; +} + +- (id)initWithFrame:(CPRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + var bounds = [self bounds]; + + [self setBackgroundColor:_CPHUDWindowViewBackgroundColor]; + + _titleField = [[CPTextField alloc] initWithFrame:CPRectMakeZero()]; + + [_titleField setFont:[CPFont systemFontOfSize:11.0]]; + [_titleField setTextColor:[CPColor whiteColor]]; + [_titleField setTextShadow:[CPShadow shadowWithOffset:CPSizeMake(0.0, 1.0) blurRadius:2.0 color:[CPColor blackColor]]]; + [_titleField setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin]; + + [self addSubview:_titleField]; + + if (_styleMask & CPClosableWindowMask) + { + var closeSize = [_CPHUDWindowViewCloseImage size]; + + _closeButton = [[CPButton alloc] initWithFrame:CPRectMake(10.0, 7.0, closeSize.width, closeSize.height)]; + + [_closeButton setBordered:NO]; + + [_closeButton setImage:_CPHUDWindowViewCloseImage]; + [_closeButton setAlternateImage:_CPHUDWindowViewCloseActiveImage]; + + [_closeButton setTarget:self]; + [_closeButton setAction:@selector(close:)]; + + [self addSubview:_closeButton]; + } + + [_resizeIndicator setFrameOrigin:CGPointMake(CGRectGetWidth(bounds) - 20.0, CGRectGetHeight(bounds) - 24.0)]; + [_resizeIndicator setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin]; + } + + return self; +} + +- (void)close:(id)aSender +{ + [[self window] performClose:self]; +} + +- (void)setTitle:(CPString)aTitle +{ + [_titleField setStringValue:aTitle]; + [_titleField sizeToFit]; + + var size = [_titleField frame].size; + + [_titleField setFrameOrigin:CPPointMake((CPRectGetWidth([self frame]) - size.width) / 2.0, (26.0 - size.height) / 2.0)]; +} + +- (CPString)title +{ + return [_titleField stringValue]; +} + +- (void)setFrameSize:(CPSize)aSize +{ + [super setFrameSize:aSize]; +} + +@end diff --git a/AppKit/build.xml b/AppKit/build.xml new file mode 100644 index 0000000000..86262c550c --- /dev/null +++ b/AppKit/build.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Foundation/CPArray.j b/Foundation/CPArray.j new file mode 100755 index 0000000000..e787d52ceb --- /dev/null +++ b/Foundation/CPArray.j @@ -0,0 +1,762 @@ +/* + * CPArray.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" +import "CPRange.j" +import "CPEnumerator.j" +import "CPSortDescriptor.j" + +@implementation _CPArrayEnumerator : CPEnumerator +{ + CPArray _array; + int _index; +} + +- (id)initWithArray:(CPArray)anArray +{ + self = [super init]; + + if (self) + { + _array = anArray; + _index = -1; + } + + return self; +} + +- (id)nextObject +{ + if (++_index >= [_array count]) + return nil; + + return _array[_index]; +} + +@end + +@implementation _CPReverseArrayEnumerator : CPEnumerator +{ + CPArray _array; + int _index; +} + +- (id)initWithArray:(CPArray)anArray +{ + self = [super init]; + + if (self) + { + _array = anArray; + _index = [_array count]; + } + + return self; +} + +- (id)nextObject +{ + if (--_index < 0) + return nil; + + return _array[_index]; +} + +@end + +@implementation CPArray : CPObject + ++ (id)alloc +{ + return []; +} + ++ (id)array +{ + return [[self alloc] init]; +} + ++ (id)arrayWithArray:(CPArray)anArray +{ + return [[self alloc] initWithArray:anArray]; +} + ++ (id)arrayWithObject:(id)anObject +{ + return [[self alloc] initWithObjects:anObject]; +} + ++ (id)arrayWithObjects:(id)anObject, ... +{ + var i = 2, + array = [[self alloc] init], + argument; + + for(; i < arguments.length && (argument = arguments[i]) != nil; ++i) + array.push(argument); + + return array; +} + ++ (id)arrayWithObjects:(id)objects count:(unsigned)aCount +{ + return [[self alloc] initWithObjects:objects count:aCount]; +} + +- (id)init +{ + return self; +} + +// Creating an Array + +- (id)initWithArray:(CPArray)anArray +{ + self = [super init]; + + if (self) + [self setArray:anArray]; + + return self; +} + +- (id)initWithArray:(CPArray)anArray copyItems:(BOOL)copyItems +{ + if (!copyItems) + return [self initWithArray:anArray]; + + self = [super init]; + + if (self) + { + var index = 0, + count = [anArray count]; + + for(; index < count; ++i) + { + if (anArray[i].isa) + self[i] = [anArray copy]; + // Do a deep/shallow copy? + else + self[i] = anArray; + } + } + + return self; +} + +- (id)initWithObjects:(Array)anObject, ... +{ + // The arguments array contains self and _cmd, so the first object is at position 2. + var i = 2, + argument; + + for(; i < arguments.length && (argument = arguments[i]) != nil; ++i) + push(argument); + + return self; +} + +- (id)initWithObjects:(id)objects count:(unsigned)aCount +{ + self = [super init]; + + if (self) + { + var index = 0; + + for(; index < aCount; ++index) + push(objects[index]); + } + + return self; +} + +- (unsigned)hash +{ + if (self.__address == nil) + self.__address = _objj_generateObjectHash(); + + return self.__address; +} + +// Querying an array + +- (BOOL)containsObject:(id)anObject +{ + return [self indexOfObject:anObject] != CPNotFound; +} + +- (int)count +{ + return length; +} + +- (int)indexOfObject:(id)anObject +{ + if (anObject === nil) + return CPNotFound; + + var i = 0, + count = length; + + // Only use isEqual: if our object is a CPObject. + if (anObject.isa) + { + for(; i < count; ++i) + if([self[i] isEqual:anObject]) + return i; + } + // If indexOf exists, use it since it's probably + // faster than anything we can implement. + else if (self.indexOf) + return indexOf(anObject); + // Last resort, do a straight forward linear O(N) search. + else + for(; i < count; ++i) + if(self[i] == anObject) + return i; + + return CPNotFound; +} + +- (int)indexOfObject:(id)anObject inRange:(CPRange)aRange +{ + if (anObject === nil) + return CPNotFound; + + var i = aRange.location, + count = MIN(CPMaxRange(aRange), length); + + // Only use isEqual: if our object is a CPObject. + if (anObject.isa) + { + for(; i < count; ++i) + if([self[i] isEqual:anObject]) + return i; + } + // Last resort, do a straight forward linear O(N) search. + else + for(; i < count; ++i) + if(self[i] == anObject) + return i; + + return CPNotFound; +} + +- (int)indexOfObjectIdenticalTo:(id)anObject +{ + if (anObject === nil) + return CPNotFound; + + // If indexOf exists, use it since it's probably + // faster than anything we can implement. + if (self.indexOf) + return indexOf(anObject); + + // Last resort, do a straight forward linear O(N) search. + else + { + var index = 0, + count = length; + + for(; index < count; ++index) + if(self[index] == anObject) + return index; + } + + return CPNotFound; +} + +- (int)indexOfObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange +{ + if (anObject === nil) + return CPNotFound; + + // If indexOf exists, use it since it's probably + // faster than anything we can implement. + if (self.indexOf) + { + var index = indexOf(anObject, aRange.location); + + if (CPLocationInRange(index, aRange)) + return index; + } + + // Last resort, do a straight forward linear O(N) search. + else + { + var index = aRange.location, + count = MIN(CPMaxRange(aRange), length); + + for(; index < count; ++index) + if(self[index] == anObject) + return index; + } + + return CPNotFound; +} + +- (id)lastObject +{ + var count = [self count]; + + if (!count) return nil; + + return self[count - 1]; +} + +- (id)objectAtIndex:(int)anIndex +{ + return self[anIndex]; +} + +- (CPArray)objectsAtIndexes:(CPIndexSet)indexes +{ + var index = [indexes firstIndex], + objects = []; + + while(index != CPNotFound) + { + [objects addObject:self[index]]; + index = [indexes indexGreaterThanIndex:index]; + } + + return objects; +} + +- (CPEnumerator)objectEnumerator +{ + return [[_CPArrayEnumerator alloc] initWithArray:self]; +} + +- (CPEnumerator)reverseObjectEnumerator +{ + return [[_CPReverseArrayEnumerator alloc] initWithArray:self]; +} + +// Sending messages to elements + +- (void)makeObjectsPerformSelector:(SEL)aSelector +{ + var index = 0, + count = length; + + for(; index < count; ++index) + objj_msgSend(self[index], aSelector); +} + +- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject +{ + var index = 0, + count = length; + + for(; index < count; ++index) + objj_msgSend(self[index], aSelector, anObject); +} + +// Comparing arrays + +- (id)firstObjectCommonWithArray:(CPArray)anArray +{ + if (![anArray count] || ![self count]) + return nil; + + var i = 0, + count = [self count]; + + for(; i < count; ++i) + if([anArray containsObject:self[i]]) + return self[i]; + + return nil; +} + +- (BOOL)isEqualToArray:(id)anArray +{ + if(length != anArray.length) + return NO; + + var index = 0, + count = [self count]; + + for(; index < count; ++index) + if(self[index] != anObject && (!self[index].isa || !anObject.isa || ![self[index] isEqual:anObject])) + return NO; + + return YES; +} + +// Deriving new arrays + +- (CPArray)arrayByAddingObject:(id)anObject +{ + var array = [self copy]; + + array.push(anObject); + + return array; +} + +- (CPArray)arrayByAddingObjectsFromArray:(CPArray)anArray +{ + return slice(0).concat(anArray); +} + +/* +- (CPArray)filteredArrayUsingPredicate:(CPPredicate)aPredicate +{ + var i= 0, + count = [self count], + array = [CPArray array]; + + for(; i= upper) + { + _cachedRangeIndex = i; + return YES; + } + + // The value isn't here, but values greater than anIndex should + // start from here regardless. + _cachedRangeIndex = i; + + return NO; +} + +- (BOOL)containsIndexes:(CPIndexSet)anIndexSet +{ + // Return YES if anIndexSet has no indexes + if(![anIndexSet count]) + return YES; + + // Return NO if we have no indexes. + if(!_count) + return NO; + + var i = 0, + count = _ranges.length; + + // This is fast thanks to the _cachedIndexRange. + for(; i < count; ++i) + if (![anIndexSet containsIndexesInRange:_ranges[i]]) + return NO; + + return YES; +} + +- (BOOL)intersectsIndexesInRange:(CPRange)aRange +{ + // This is fast thanks to the _cachedIndexRange. + if(!_count) + return NO; + + var i = SOERangeIndex(self, aRange.location), + count = _ranges.length, + upper = CPMaxRange(aRange); + + // Stop if the location is ever bigger than or equal to our + // non-inclusive upper bound + for (; i < count && _ranges[i].location < upper; ++i) + if(CPIntersectionRange(aRange, _ranges[i]).length) + return YES; + + return NO; +} + +- (int)count +{ + return _count; +} + +// Accessing Indexes + +- (int)firstIndex +{ + return _count ? _ranges[0].location : CPNotFound; +} + +- (int)lastIndex +{ + return _count ? CPMaxRange(_ranges[_ranges.length - 1]) - 1 : CPNotFound; +} + +- (unsigned)indexGreaterThanIndex:(unsigned)anIndex +{ + if(!_count) + return CPNotFound; + + var i = SOERangeIndex(self, anIndex++), + count = _ranges.length; + + for(; i < count && anIndex >= CPMaxRange(_ranges[i]); ++i) ; + + if (i == count) + return CPNotFound; + + _cachedRangeIndex = i; + + if (anIndex < _ranges[i].location) + return _ranges[i].location; + + return anIndex; +} + +- (unsigned)indexLessThanIndex:(unsigned)anIndex +{ + if (!_count) + return CPNotFound; + + var i = GOERangeIndex(self, anIndex--); + + for (; i >= 0 && anIndex < _ranges[i].location; --i) ; + + if(i < 0) + return CPNotFound; + + _cachedRangeIndex = i; + + if (CPLocationInRange(anIndex, _ranges[i])) + return anIndex; + + if (CPMaxRange(_ranges[i]) - 1 < anIndex) + return CPMaxRange(_ranges[i]) - 1; + + return CPNotFound; +} + +- (unsigned int)indexGreaterThanOrEqualToIndex:(unsigned)anIndex +{ + return [self indexGreaterThanIndex:anIndex - 1]; +} + +- (unsigned int)indexLessThanOrEqualToIndex:(unsigned)anIndex +{ + return [self indexLessThanIndex:anIndex + 1]; +} + +- (unsigned)getIndexes:(CPArray)anArray maxCount:(unsigned)aMaxCount inIndexRange:(CPRangePointer)aRangePointer +{ + if (!_count || aMacCount <= 0 || aRangePointer && !aRangePointer.length) + return 0; + + var i = SOERangeIndex(self, aRangePointer.location), + total = 0, + count = _ranges.length; + + for (; i < count; ++i) + { + // If aRangePointer is nil, all indexes are acceptable. + var intersection = aRangePointer ? CPIntersectionRange(_ranges[i], aRangePointer) : _ranges[i], + index = intersection.location, + maximum = CPMaxRange(intersection); + + for (; index < maximum; ++index) + { + anArray[total++] = index; + + if (total == aMaxCount) + { + // Update aRangePointer if it exists... + if (aRangePointer) + { + var upper = CPMaxRange(aRangePointer); + + // Don't use CPMakeRange since the values need to persist. + aRangePointer.location = index + 1; + aRangePointer.length = upper - index - 1; + } + + return aMaxCount; + } + } + } + + // Update aRangePointer if it exists... + if (aRangePointer) + { + aRangePointer.location = CPNotFound; + aRangePointer.length = 0; + } + + return total; +} + +- (CPString)description +{ + var desc = [super description] + " "; + + if (_count) + { + desc += "[number of indexes: " + _count + " (in " + _ranges.length + " ranges), indexes: ("; + for (i = 0; i < _ranges.length; i++) + { + desc += _ranges[i].location; + if (_ranges[i].length > 1) desc += "-" + (CPMaxRange(_ranges[i])-1) + ":"+_ranges[i].length+":"; + if (i+1 < _ranges.length) desc += " "; + } + desc += ")]"; + } + else + desc += "(no indexes)"; + return desc; +} + +@end + +@implementation CPIndexSet(CPMutableIndexSet) + +// Adding indexes. + +- (void)addIndex:(unsigned)anIndex +{ + [self addIndexesInRange:CPMakeRange(anIndex, 1)]; +} + +- (void)addIndexes:(CPIndexSet)anIndexSet +{ + var i = 0, + ranges = anIndexSet._ranges, + count = ranges.length; + + // Simply add each range within anIndexSet. + for(; i < count; ++i) + [self addIndexesInRange:ranges[i]]; +} + +- (void)addIndexesInRange:(CPRange)aRange +{ + if (_ranges.length == 0) + { + _count = aRange.length; + + return [_ranges addObject:CPCopyRange(aRange)]; + } + + // FIXME: Should we really use SOERangeIndex here? There is no real + // reason the cached index would be a better guess than 0, and it + // would avoid a function call. + var i = SOERangeIndex(self, aRange.location), + count = _ranges.length, + padded = CPMakeRange(aRange.location - 1, aRange.length + 2), + maximum = CPMaxRange(aRange); + + // If our range won't intersect with the last range, just append it to the end. + if (count && CPMaxRange(_ranges[count - 1]) < aRange.location) + [_ranges addObject:CPCopyRange(aRange)]; + else + for (; i < count; ++i) + { + // This range is completely independent of existing ranges, + // simply add it to the array. + if (maximum < _ranges[i].location) + { + _count += aRange.length; + + // Keep _cachedRangeIndex relevant. + if (i < _cachedRangeIndex) ++_cachedRangeIndex; + + return [_ranges insertObject:CPCopyRange(aRange) atIndex:i]; + } + + if (CPIntersectionRange(_ranges[i], padded).length) + { + var union = CPUnionRange(_ranges[i], aRange); + + // We already contain all the indexes in this range. + if (union.length == _ranges[i].length) + return; + + // Pad the length to collapse with later ranges. + ++union.length; + + // We only need to check if we now intersect with any following + // ranges since if we now intersected with the previous range, + // it would have already been handled. We start at i and not i + 1 + // to make sure we subtract i's length. + var j = i; + + for(; j < count; ++j) + // Bail as soon as we don't find an intersection. + if(CPIntersectionRange(union, _ranges[j]).length) + _count -= _ranges[j].length; + else + break; + + // Remove the padding now that we are done. + // NOTE: We could have set _ranges[i] = CPCopyRange(union), + // and then not had to bother decerementing here, but this avoids + // a lookup above during unioning, and a function call (CPCopyRange). + --union.length; + _ranges[i] = union; + + // Now remove indexes [i + 1, j - 1] + if (j - i - 1 > 0) + { + var remove = CPMakeRange(i + 1, j - i - 1); + + _ranges[i] = CPUnionRange(_ranges[i], _ranges[j - 1]); + [_ranges removeObjectsInRange:remove]; + + // Keep _cachedRangeIndex relevant. + if (_cachedRangeIndex >= CPMaxRange(remove)) _cachedRangedIndex -= remove.length; + else if (CPLocationInRange(_cachedRangeIndex, remove)) _cachedRangeIndex = i; + } + + // Update count. + _count += _ranges[i].length; + + return; + } + } + + _count += aRange.length; +} + +// Removing Indexes + +- (void)removeIndex:(unsigned int)anIndex +{ + [self removeIndexesInRange:CPMakeRange(anIndex, 1)]; +} + +- (void)removeIndexes:(CPIndexSet)anIndexSet +{ + var i = 0, + ranges = anIndexSet._ranges, + count = ranges.length; + + // Simply remove each index from anIndexSet + for(; i < count; ++i) + [self removeIndexesInRange:ranges[i]]; +} + +- (void)removeAllIndexes +{ + _ranges = []; + _count = 0; + _cachedRangeIndex = 0; +} + +- (void)removeIndexesInRange:(CPRange)aRange +{ + // FIXME: Should we really use SOERangeIndex here? There is no real + // reason the cached index would be a better guess than 0, and it + // would avoid a function call. + var i = SOERangeIndex(self, aRange.location), + count = _ranges.length, + maximum = CPMaxRange(aRange), + removal = CPMakeRange(CPNotFound, 0); + + for (; i < count; ++i) + { + var range = _ranges[i]; + + // Our range will not intersect with any coming ranges. + if (maximum < range.location) + break; + + var intersection = CPIntersectionRange(range, aRange); + + // If we don't have an intersection, then just continue iterating. + if (!intersection.length) + continue; + + // If the intersection consists of the entirety of this range, + // then remove it completely. + else if (intersection.length == range.length) + { + if (removal.location == CPNotFound) + removal = CPMakeRange(i, 1); + else + ++removal.length; + } + // If the intersection is contained entirely within this range, + // then split it into two and return. + else if (intersection.location > range.location && CPMaxRange(intersection) < CPMaxRange(range)) + { + var insert = CPMakeRange(CPMaxRange(intersection), CPMaxRange(range) - CPMaxRange(intersection)); + + range.length = intersection.location - range.location; + + _count -= intersection.length; + + return [_ranges insertObject:insert atIndex:i + 1]; + } + // Else if we at least have an intersection, then trim the existing range. + else + { + range.length -= intersection.length; + + if (intersection.location <= range.location) + range.location += intersection.length; + } + + _count -= intersection.length; + } + + if (removal.length) + [_ranges removeObjectsInRange:removal]; +} + +// Shifting Index Groups + +- (void)shiftIndexesStartingAtIndex:(unsigned)anIndex by:(int)aDelta +{ + if (!_count || aDelta == 0) + return; + + // Later indexes have a higher probability of being shifted + // than lower ones, so start at the end and work backwards. + var i = _ranges.length - 1, + shifted = CPMakeRange(CPNotFound, 0); + + for(; i >= 0; --i) + { + var range = _ranges[i], + maximum = CPMaxRange(range); + + if (anIndex > maximum) + break; + + // If our index is within our range, but not the first index, + // then this range will be split. + if (anIndex > range.location && anIndex < maximum) + { + // Split the range into shift and unshifted. + shifted = CPMakeRange(anIndex + aDelta, maximum - anIndex); + range.length = anIndex - range.location; + + // If our delta is positive, then we can simply add the range + // to the array. + if (aDelta > 0) + [_ranges insertObject:shifted atIndex:i + 1]; + // If it's negative, it needs to be added properly later. + else if (shifted.location < 0) + { + shifted.length = CPMaxRange(shifted); + shifted.location = 0; + } + + // We don't need to continue. + break; + } + + // Shift the range, and normalize it if the result is negative. + if ((range.location += aDelta) < 0) + { + range.length = CPMaxRange(range); + range.location = 0; + } + } + + // We need to add the shifted ranges if the delta is negative. + if (aDelta < 0) + { + var j = i + 1, + count = _ranges.length, + shifts = []; + + for (; j < count; ++j) + [shifts addObject:_ranges[j]]; + + if ((j = i + 1) < count) + { + [_ranges removeObjectsInRange:CPMakeRange(j, count - j)]; + + for (j = 0, count = shifts.length; j < count; ++j) + [self addIndexesInRange:shifts[j]]; + } + + if (shifted.location != CPNotFound) + [self addIndexesInRange:shifted]; + } +} + +@end + +var CPIndexSetCountKey = @"CPIndexSetCountKey", + CPIndexSetCachedRangeIndexKey = @"CPIndexSetCachedRangeIndexKey", + CPIndexSetRangeStringsKey = @"CPIndexSetRangeStringsKey"; + +@implementation CPIndexSet (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + { + _count = [aCoder decodeIntForKey:CPIndexSetCountKey]; + _cachedRangeIndex = [aCoder decodeIntForKey:CPIndexSetCachedRangeIndexKey]; + _ranges = []; + + var rangeStrings = [aCoder decodeObjectForKey:CPIndexSetRangeStringsKey], + index = 0, + count = rangeStrings.length; + + for (; index < count; ++index) + _ranges.push(CPRangeFromString(rangeStrings[index])); + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeInt:_count forKey:CPIndexSetCountKey]; + [aCoder encodeInt:_cachedRangeIndex forKey:CPIndexSetCachedRangeIndexKey]; + + var index = 0, + count = _ranges.length, + rangeStrings = []; + + for (; index < count; ++index) + rangeStrings[index] = CPStringFromRange(_ranges[index]); + + [aCoder encodeObject:rangeStrings forKey:CPIndexSetRangeStringsKey]; +} + +@end + +@implementation CPIndexSet (CPCopying) + +- (id)copy +{ + return [[[self class] alloc] initWithIndexSet:self]; +} + +- (id)mutableCopy +{ + return [[[self class] alloc] initWithIndexSet:self]; +} + +@end + +@implementation CPMutableIndexSet : CPIndexSet + +@end + +var SOERangeIndex = function(anIndexSet, anIndex) +{ + var ranges = anIndexSet._ranges, + cachedRangeIndex = 0;//anIndexSet._cachedRangeIndex; + + if(cachedRangeIndex < ranges.length && anIndex >= ranges[cachedRangeIndex].location) + return cachedRangeIndex; + + return 0; +} + +var GOERangeIndex = function(anIndexSet, anIndex) +{ + var ranges = anIndexSet._ranges, + cachedRangeIndex = anIndexSet._ranges.length;//anIndexSet._cachedRangeIndex; + + if(cachedRangeIndex < ranges.length && anIndex <= ranges[cachedRangeIndex].location) + return cachedRangeIndex; + + return ranges.length - 1; +} + +/* +new old method +X + (id)indexSet; +X + (id)indexSetWithIndex:(unsigned int)value; +X + (id)indexSetWithIndexesInRange:(NSRange)range; +X X - (id)init; +X X - (id)initWithIndex:(unsigned int)value; +X X - (id)initWithIndexesInRange:(NSRange)range; // designated initializer +X X - (id)initWithIndexSet:(NSIndexSet *)indexSet; // designated initializer +X - (BOOL)isEqualToIndexSet:(NSIndexSet *)indexSet; +X X - (unsigned int)count; +X X - (unsigned int)firstIndex; +X X - (unsigned int)lastIndex; +X X - (unsigned int)indexGreaterThanIndex:(unsigned int)value; +X X - (unsigned int)indexLessThanIndex:(unsigned int)value; +X X - (unsigned int)indexGreaterThanOrEqualToIndex:(unsigned int)value; +X X - (unsigned int)indexLessThanOrEqualToIndex:(unsigned int)value; +X - (unsigned int)getIndexes:(unsigned int *)indexBuffer maxCount:(unsigned int)bufferSize inIndexRange:(NSRangePointer)range; +X X - (BOOL)containsIndex:(unsigned int)value; +X X - (BOOL)containsIndexesInRange:(NSRange)range; +X X - (BOOL)containsIndexes:(NSIndexSet *)indexSet; +X X - (BOOL)intersectsIndexesInRange:(NSRange)range; +X X - (void)addIndexes:(NSIndexSet *)indexSet; +X - (void)removeIndexes:(NSIndexSet *)indexSet; +X X - (void)removeAllIndexes; +X - (void)addIndex:(unsigned int)value; +X - (void)removeIndex:(unsigned int)value; +X - (void)addIndexesInRange:(NSRange)range; +X - (void)removeIndexesInRange:(NSRange)range; + - (void)shiftIndexesStartingAtIndex:(unsigned int)index by:(int)delta; +*/ diff --git a/Foundation/CPInvocation.j b/Foundation/CPInvocation.j new file mode 100644 index 0000000000..9566889076 --- /dev/null +++ b/Foundation/CPInvocation.j @@ -0,0 +1,133 @@ +/* + * CPInvocation.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" + +@implementation CPInvocation : CPObject +{ + id _returnValue; + CPMutableArray _arguments; + CPMethodSignature _methodSignature; +} + +// Creating CPInvocation Objects + ++ (id)invocationWithMethodSignature:(CPMethodSignature)aMethodSignature +{ + return [[self alloc] initWithMethodSignature:aMethodSignature]; +} + +- (id)initWithMethodSignature:(CPMethodSignature)aMethodSignature +{ + self = [super init]; + + if (self) + { + _arguments = []; + _methodSignature = aMethodSignature; + } + + return self; +} + +// Configuring an Invocation Object + +- (void)setSelector:(SEL)aSelector +{ + _arguments[1] = aSelector; +} + +- (SEL)selector +{ + return _arguments[1]; +} + +- (void)setTarget:(id)aTarget +{ + _arguments[0] = aTarget; +} + +- (id)target +{ + return _arguments[0]; +} + +- (void)setArgument:(id)anArgument atIndex:(unsigned)anIndex +{ + _arguments[anIndex] = anArgument; +} + +- (id)argumentAtIndex:(unsigned)anIndex +{ + return _arguments[anIndex]; +} + +- (void)setReturnValue:(id)aReturnValue +{ + _returnValue = aReturnValue; +} + +- (id)returnValue +{ + return _returnValue; +} + +// Dispatching an Invocation + +- (void)invoke +{ + _returnValue = objj_msgSend.apply(objj_msgSend, _arguments); +} + +- (void)invokeWithTarget:(id)aTarget +{ + _arguments[0] = aTarget; + _returnValue = objj_msgSend.apply(objj_msgSend, _arguments); +} + +@end + +var CPInvocationArguments = @"CPInvocationArguments", + CPInvocationReturnValue = @"CPInvocationReturnValue"; + +@implementation CPInvocation (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + { + _returnValue = [aCoder decodeObjectForKey:CPInvocationReturnValue]; + _arguments = [aCoder decodeObjectForKey:CPInvocationArguments]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_returnValue forKey:CPInvocationReturnValue]; + [aCoder encodeObject:_arguments forKey:CPInvocationArguments]; +} + +@end diff --git a/Foundation/CPJSONPConnection.j b/Foundation/CPJSONPConnection.j new file mode 100644 index 0000000000..521b6661bb --- /dev/null +++ b/Foundation/CPJSONPConnection.j @@ -0,0 +1,107 @@ +/* + * CPJSONPConnection.j + * Foundation + * + * Created by Ross Boucher. + * Copyright 2008, 280 North, Inc. + * + * 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 + +CPJSONPConnectionCallbacks = {}; + +@implementation CPJSONPConnection : CPObject +{ + CPURLRequest _request; + id _delegate; + + CPString _callbackParameter; + DOMElement _scriptTag; +} + ++ (CPData)sendRequest:(CPURLRequest)aRequest callback:(CPString)callbackParameter delegate:(id)aDelegate +{ + return [[[self class] alloc] initWithRequest:aRequest callback:callbackParameter delegate:aDelegate startImmediately:YES];; +} + +- (id)initWithRequest:(CPURLRequest)aRequest callback:(CPString)aString delegate:(id)aDelegate +{ + return [self initWithRequest:aRequest callback:aString delegate:aDelegate startImmediately: NO]; +} + +- (id)initWithRequest:(CPURLRequest)aRequest callback:(CPString)aString delegate:(id)aDelegate startImmediately:(BOOL)shouldStartImmediately +{ + self = [super init]; + + _request = aRequest; + _delegate = aDelegate; + + _callbackParameter = aString; + + CPJSONPConnectionCallbacks["callback"+[self hash]] = function(data) + { + [_delegate connection:self didReceiveData:data]; + [self removeScriptTag]; + }; + + if(shouldStartImmediately) + [self start]; + + return self; +} + +- (void)start +{ + try + { + var head = document.getElementsByTagName("head").item(0); + + var source = [_request URL]; + source += (source.indexOf('?') < 0) ? "?" : "&"; + source += _callbackParameter+"=CPJSONPConnectionCallbacks.callback"+[self hash]; + + _scriptTag = document.createElement("script"); + _scriptTag.setAttribute("type", "text/javascript"); + _scriptTag.setAttribute("charset", "utf-8"); + _scriptTag.setAttribute("src", source); + + head.appendChild(_scriptTag); + } + catch (exception) + { + [_delegate connection: self didFailWithError: exception]; + [self removeScriptTag]; + } +} + +- (void)removeScriptTag +{ + var head = document.getElementsByTagName("head").item(0); + + if(_scriptTag.parentNode == head) + head.removeChild(_scriptTag); + + CPJSONPConnectionCallbacks["callback"+[self hash]] = nil; + delete CPJSONPConnectionCallbacks["callback"+[self hash]]; +} + +- (void)cancel +{ + [self removeScriptTag]; +} + +@end \ No newline at end of file diff --git a/Foundation/CPKeyValueCoding.j b/Foundation/CPKeyValueCoding.j new file mode 100644 index 0000000000..260179071b --- /dev/null +++ b/Foundation/CPKeyValueCoding.j @@ -0,0 +1,135 @@ +/* + * CPKeyValueCoding.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" + +@implementation CPObject(CPKeyValueCoding) + ++ (BOOL)accessInstanceVariablesDirectly +{ + return YES; +} + ++ (SEL)_accessorForKey:(CPString)aKey +{ + var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substr(1), + selector = CPSelectorFromString("get" + capitalizedKey); + + if ([self instancesRespondToSelector:selector] || + [self instancesRespondToSelector:selector = CPSelectorFromString(aKey)] || + [self instancesRespondToSelector:selector = CPSelectorFromString("is" + capitalizedKey)]) + return selector; + + return nil; +} + ++ (SEL)_modifierForKey:(CPString)aKey +{ + var selector = CPSelectorFromString("set" + aKey.charAt(0).toUpperCase() + aKey.substr(1) + ':'); + + if ([self instancesRespondToSelector:selector]) + return selector; + + return nil; +} + +- (CPString)_ivarForKey:(CPString)aKey +{ + var ivar, + isKey = "is" + aKey.charAt(0).toUpperCase() + aKey.substr(1); + + if (self[ivar = "_" + aKey] != undefined || self[ivar = "_" + isKey] != undefined || + self[ivar = aKey] != undefined || self[ivar = isKey] != undefined) ; + return ivar; + + return nil; +} + +- (id)valueForKey:(CPString)aKey +{ + var ivar, + theClass = [self class], + selector = [theClass _accessorForKey:aKey] + + if (selector) + return [self performSelector:selector]; + else if([theClass accessInstanceVariablesDirectly] && (ivar = [self _ivarForKey:aKey])) + return self[ivar]; + else + return [self valueForUndefinedKey:aKey]; +} + +- (id)valueForKeyPath:(CPString)aKeyPath +{ + var i = 0, + keys = aKeyPath.split("."), + count = keys.length, + value = self; + + for(; i valueForKey()]: this class is not key value coding-compliant for the key "+aKey+".]"); +} + +- (void)setValue:(id)aValue forKeyPath:(CPString)aKeyPath +{ + if (!aKeyPath) aKeyPath = "self"; + + var i = 0, + keys = aKeyPath.split("."), + count = keys.length - 1, + owner = self; + + for(; i < count; ++i) + owner = [owner valueForKey:keys[i]]; + + [owner setValue:aValue forKey:keys[i]]; +} + +- (void)setValue:(id)aValue forKey:(CPString)aKey +{ + var ivar, + theClass = [self class], + selector = [theClass _modifierForKey:aKey]; + + if (selector) + [self performSelector:selector withObject:aValue]; + else if([theClass accessInstanceVariablesDirectly] && (ivar = [self _ivarForKey:aKey])) + self[ivar] = aValue; + else + [self setValue:aValue forUndefinedKey:aKey]; +} + +- (void)setValue:(id)aValue forUndefinedKey:(CPString)aKey +{ + alert("IMPLEMENT EXCEPTIONS, also, setValueForKey died."); +// CPException.raise(CPUndefinedKeyException, "[<"+this.className()+" "+this+"> setValueForKey()]: this class is not key value coding-compliant for the key "+aKey+".]"); +} + +@end \ No newline at end of file diff --git a/Foundation/CPKeyedArchiver.j b/Foundation/CPKeyedArchiver.j new file mode 100644 index 0000000000..02c71570c5 --- /dev/null +++ b/Foundation/CPKeyedArchiver.j @@ -0,0 +1,457 @@ +/* + * CPKeyedArchiver.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPData.j" +import "CPCoder.j" +import "CPArray.j" +import "CPString.j" +import "CPNumber.j" +import "CPDictionary.j" +import "CPValue.j" + +var CPArchiverReplacementClassNames = nil; + +var _CPKeyedArchiverDidEncodeObjectSelector = 1, + _CPKeyedArchiverWillEncodeObjectSelector = 2, + _CPKeyedArchiverWillReplaceObjectWithObjectSelector = 4, + _CPKeyedArchiverDidFinishSelector = 8, + _CPKeyedArchiverWillFinishSelector = 16; + +var _CPKeyedArchiverNullString = "$null", + _CPKeyedArchiverNullReference = nil, + + _CPKeyedArchiverUIDKey = "CP$UID", + + _CPKeyedArchiverTopKey = "$top", + _CPKeyedArchiverObjectsKey = "$objects", + _CPKeyedArchiverArchiverKey = "$archiver", + _CPKeyedArchiverVersionKey = "$version", + + _CPKeyedArchiverClassNameKey = "$classname", + _CPKeyedArchiverClassesKey = "$classes", + _CPKeyedArchiverClassKey = "$class"; + +var _CPKeyedArchiverStringClass = Nil, + _CPKeyedArchiverNumberClass = Nil; + +@implementation _CPKeyedArchiverValue : CPValue +{ +} +@end + +@implementation CPKeyedArchiver : CPCoder +{ + id _delegate; + unsigned _delegateSelectors; + + CPData _data; + + CPArray _objects; + + CPDictionary _UIDs; + CPDictionary _conditionalUIDs; + + CPDictionary _replacementObjects; + CPDictionary _replacementClassNames; + + id _plistObject; + CPMutableArray _plistObjects; + + CPPropertyListFormat _outputFormat; + +} + ++ (void)initialize +{ + if (self != [CPKeyedArchiver class]) + return; + + _CPKeyedArchiverStringClass = [CPString class]; + _CPKeyedArchiverNumberClass = [CPNumber class]; + + _CPKeyedArchiverNullReference = [CPDictionary dictionaryWithObject:0 forKey:_CPKeyedArchiverUIDKey]; +} + ++ (BOOL)allowsKeyedCoding +{ + return YES; +} + ++ (CPData)archivedDataWithRootObject:(id)anObject +{ + var data = [CPData dataWithPlistObject:nil], + archiver = [[self alloc] initForWritingWithMutableData:data]; + + [archiver encodeObject:anObject forKey:@"root"]; + [archiver finishEncoding]; + + return data; +} + +// Initializing an NSKeyedArchiver object + +- (id)initForWritingWithMutableData:(CPMutableData)data +{ + self = [super init]; + + if (self) + { + _data = data; + + _objects = []; + + _UIDs = [CPDictionary dictionary]; + _conditionalUIDs = [CPDictionary dictionary]; + + _replacementObjects = [CPDictionary dictionary]; + + _data = data; + + _plistObject = [CPDictionary dictionary]; + _plistObjects = [CPArray arrayWithObject:_CPKeyedArchiverNullString]; + } + + return self; +} + +// Archiving Data +- (void)finishEncoding +{ + if (_delegate && _delegateSelectors & _CPKeyedArchiverWillFinishSelector) + [_delegate archiverWillFinish:self]; + + var i = 0, + topObject = _plistObject, + classes = []; + + for (; i < _objects.length; ++i) + { + var object = _objects[i], + theClass = [object classForKeyedArchiver]; + + // Do whatever with the class, yo. + // We call willEncodeObject previously. + + _plistObject = _plistObjects[[_UIDs objectForKey:[object hash]]]; + [object encodeWithCoder:self]; + + if (_delegate && _delegateSelectors & _CPKeyedArchiverDidEncodeObjectSelector) + [_delegate archiver:self didEncodeObject:object]; + } + + _plistObject = [CPDictionary dictionary]; + + [_plistObject setObject:topObject forKey:_CPKeyedArchiverTopKey]; + [_plistObject setObject:_plistObjects forKey:_CPKeyedArchiverObjectsKey]; + [_plistObject setObject:[self className] forKey:_CPKeyedArchiverArchiverKey]; + [_plistObject setObject:@"100000" forKey:_CPKeyedArchiverVersionKey]; + + [_data setPlistObject:_plistObject]; + + if (_delegate && _delegateSelectors & _CPKeyedArchiverDidFinishSelector) + [_delegate archiverDidFinish:self]; +} + +- (void)outputFormat +{ + return _outputFormat; +} + +- (void)setOutputFormat:(CPPropertyListFormat)aPropertyListFormat +{ + _outputFormat = aPropertyListFormat; +} + +- (void)encodeBool:(BOOL)aBOOL forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aBOOL, NO) forKey:aKey]; +} + +- (void)encodeDouble:(double)aDouble forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aDouble, NO) forKey:aKey]; +} + +- (void)encodeFloat:(float)aFloat forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aFloat, NO) forKey:aKey]; +} + +- (void)encodeInt:(float)anInt forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anInt, NO) forKey:aKey]; +} + +// Managing Delegates + +- (void)setDelegate:(id)aDelegate +{ + _delegate = aDelegate; + + if ([_delegate respondsToSelector:@selector(archiver:didEncodeObject:)]) + _delegateSelectors |= _CPKeyedArchiverDidEncodeObjectSelector; + + if ([_delegate respondsToSelector:@selector(archiver:willEncodeObject:)]) + _delegateSelectors |= _CPKeyedArchiverWillEncodeObjectSelector; + + if ([_delegate respondsToSelector:@selector(archiver:willReplaceObject:withObject:)]) + _delegateSelectors |= _CPKeyedArchiverWillReplaceObjectWithObjectSelector; + + if ([_delegate respondsToSelector:@selector(archiver:didFinishEncoding:)]) + _delegateSelectors |= _CPKeyedArchiverDidFinishEncodingSelector; + + if ([_delegate respondsToSelector:@selector(archiver:willFinishEncoding:)]) + _delegateSelectors |= _CPKeyedArchiverWillFinishEncodingSelector; + +} + +- (id)delegate +{ + return _delegate; +} + +- (void)encodePoint:(CPPoint)aPoint forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CPStringFromPoint(aPoint), NO) forKey:aKey]; +} + +- (void)encodeRect:(CPRect)aRect forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CPStringFromRect(aRect), NO) forKey:aKey]; +} + +- (void)encodeSize:(CPSize)aSize forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CPStringFromSize(aSize), NO) forKey:aKey]; +} + +- (void)encodeConditionalObject:(id)anObject forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anObject, YES) forKey:aKey]; +} + +- (void)encodeNumber:(CPNumber)aNumber forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aNuumber, NO) forKey:aKey]; +} + +- (void)encodeObject:(id)anObject forKey:(CPString)aKey +{ + [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anObject, NO) forKey:aKey]; +} + +- (void)_encodeArrayOfObjects:(CPArray)objects forKey:(CPString)aKey +{ + var i = 0, + count = objects.length, + references = [CPArray arrayWithCapacity:count]; + + for (; i < count; ++i) + [references addObject:_CPKeyedArchiverEncodeObject(self, objects[i], NO)]; + + [_plistObject setObject:references forKey:aKey]; +} + +- (void)_encodeDictionaryOfObjects:(CPDictionary)aDictionary forKey:(CPString)aKey +{ + var key, + keys = [aDictionary keyEnumerator], + references = [CPDictionary dictionary]; + + while (key = [keys nextObject]) + [references setObject:_CPKeyedArchiverEncodeObject(self, [aDictionary objectForKey:key], NO) forKey:key]; + + [_plistObject setObject:references forKey:aKey]; +} + +// Managing classes and class names + ++ (void)setClassName:(CPString)aClassName forClass:(Class)aClass +{ + if (!CPArchiverReplacementClassNames) + CPArchiverReplacementClassNames = [CPDictionary dictionary]; + + [CPArchiverReplacementClassNames setObject:aClassName forKey:CPStringFromClass(aClass)]; +} + ++ (CPString)classNameForClass:(Class)aClass +{ + if (!CPArchiverReplacementClassNames) + return aClass.name; + + var className = [CPArchiverReplacementClassNames objectForKey:CPStringFromClass(aClassName)]; + + return className ? className : aClass.name; +} + +- (void)setClassName:(CPString)aClassName forClass:(Class)aClass +{ + if (!_replacementClassNames) + _replacementClassNames = [CPDictionary dictionary]; + + [_replacementClassNames setObject:aClassName forKey:CPStringFromClass(aClass)]; +} + +- (CPString)classNameForClass:(Class)aClass +{ + if (!_replacementClassNames) + return aClass.name; + + var className = [_replacementClassNames objectForKey:CPStringFromClass(aClassName)]; + + return className ? className : aClass.name; +} + +@end + +var _CPKeyedArchiverEncodeObject = function(self, anObject, isConditional) +{ + // We wrap primitive JavaScript objects in a unique subclass of CPValue. + // This way, when we unarchive, we know to unwrap it, since + // _CPKeyedArchiverValue should not be used anywhere else. + if (anObject != nil && !anObject.isa) + anObject = [_CPKeyedArchiverValue valueWithJSObject:anObject]; + + // Get the proper replacement object + var hash = [anObject hash], + object = [self._replacementObjects objectForKey:hash]; + + // If a replacement object doesn't exist, then actually ask for one. + // Explicitly compare to nil because object could be === 0. + if (object == nil) + { + object = [anObject replacementObjectForKeyedArchiver:self]; + + // Notify our delegate of this replacement. + if (self._delegate) + { + if (object != anObject && self._delegateSelectors & _CPKeyedArchiverWillReplaceObjectWithObjectSelector) + [self._delegate archiver:self willReplaceObject:anObject withObject:object]; + + if (self._delegateSelectors & _CPKeyedArchiverWillEncodeObjectSelector) + { + anObject = [self._delegate archiver:self willEncodeObject:object]; + + if (anObject != object && self._delegateSelectors & _CPKeyedArchiverWillReplaceObjectWithObjectSelector) + [self._delegate archiver:self willReplaceObject:object withObject:anObject]; + + object = anObject; + } + } + + [self._replacementObjects setObject:object forKey:hash]; + } + + // If we still don't have an object by this point, then return a + // reference to the null object. + // Explicitly compare to nil because object could be === 0. + if (object == nil) + return _CPKeyedArchiverNullReference; + + // If not, then grab the object's UID + var UID = [self._UIDs objectForKey:hash = [object hash]]; + + // If this object doesn't have a unique index in the object table yet, + // then it also hasn't been properly encoded. We explicitly compare + // index to nil since it could be 0, which would also evaluate to false. + if (UID == nil) + { + // If it is being conditionally encoded, then + if (isConditional) + { + // If we haven't already noted this conditional object... + if ((UID = [self._conditionalUIDs objectForKey:hash]) == nil) + { + // Use the null object as a placeholder. + [self._conditionalUIDs setObject:UID = [self._plistObjects count] forKey:hash]; + [self._plistObjects addObject:_CPKeyedArchiverNullString]; + } + } + else + { + var theClass = [anObject classForKeyedArchiver], + plistObject = nil, + shouldEncodeObject = NO; + + if (theClass == _CPKeyedArchiverStringClass || theClass == _CPKeyedArchiverNumberClass)// || theClass == _CPKeyedArchiverBooleanClass) + plistObject = object; + else + { + shouldEncodeObject = YES; + plistObject = [CPDictionary dictionary]; + } + + // If this object was previously encoded conditionally... + if ((UID = [self._conditionalUIDs objectForKey:hash]) == nil) + { + [self._UIDs setObject:UID = [self._plistObjects count] forKey:hash]; + [self._plistObjects addObject:plistObject]; + + // Only encode the object if it is not the same as the plist object. + if (shouldEncodeObject) + { + [self._objects addObject:object]; + + var className = [self classNameForClass:theClass]; + + if (!className) + className = [[self class] classNameForClass:theClass]; + + if (!className) + className = theClass.name; + else + theClass = window[className]; + + var classUID = [self._UIDs objectForKey:className]; + + if (!classUID) + { + var plistClass = [CPDictionary dictionary], + hierarchy = []; + + [plistClass setObject:className forKey:_CPKeyedArchiverClassNameKey]; + + do + { + [hierarchy addObject:CPStringFromClass(theClass)]; + } while (theClass = [theClass superclass]); + + [plistClass setObject:hierarchy forKey:_CPKeyedArchiverClassesKey]; + + classUID = [self._plistObjects count]; + [self._plistObjects addObject:plistClass]; + [self._UIDs setObject:classUID forKey:className]; + } + + [plistObject setObject:[CPDictionary dictionaryWithObject:classUID forKey:_CPKeyedArchiverUIDKey] forKey:_CPKeyedArchiverClassKey]; + } + } + else + { + [self._UIDs setObject:object forKey:UID]; + [self._plistObjects replaceObjectAtIndex:UID withObject:plistObject]; + } + } + } + + return [CPDictionary dictionaryWithObject:UID forKey:_CPKeyedArchiverUIDKey]; +} diff --git a/Foundation/CPKeyedUnarchiver.j b/Foundation/CPKeyedUnarchiver.j new file mode 100644 index 0000000000..fb34849f6a --- /dev/null +++ b/Foundation/CPKeyedUnarchiver.j @@ -0,0 +1,353 @@ +/* + * CPKeyedUnarchiver.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPNull.j" +import "CPCoder.j" + + +var _CPKeyedUnarchiverCannotDecodeObjectOfClassNameOriginalClassSelector = 1, + _CPKeyedUnarchiverDidDecodeObjectSelector = 1 << 1, + _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector = 1 << 2, + _CPKeyedUnarchiverWillFinishSelector = 1 << 3, + _CPKeyedUnarchiverDidFinishSelector = 1 << 4; + +var _CPKeyedArchiverNullString = "$null" + + _CPKeyedArchiverUIDKey = "CP$UID", + + _CPKeyedArchiverTopKey = "$top", + _CPKeyedArchiverObjectsKey = "$objects", + _CPKeyedArchiverArchiverKey = "$archiver", + _CPKeyedArchiverVersionKey = "$version", + + _CPKeyedArchiverClassNameKey = "$classname", + _CPKeyedArchiverClassesKey = "$classes", + _CPKeyedArchiverClassKey = "$class"; + +var _CPKeyedUnarchiverArrayClass = Nil, + _CPKeyedUnarchiverStringClass = Nil, + _CPKeyedUnarchiverDictionaryClass = Nil, + _CPKeyedUnarchiverArchiverValueClass = Nil; + +@implementation CPKeyedUnarchiver : CPCoder +{ + id _delegate; + unsigned _delegateSelectors; + + CPData _data; + + // FIXME: We need to support this! + CPDictionary _replacementClassNames; + + CPDictionary _objects; + CPDictionary _archive; + + CPDictionary _plistObject; + CPArray _plistObjects; +} + ++ (void)initialize +{ + if (self != [CPKeyedUnarchiver class]) + return; + + _CPKeyedUnarchiverArrayClass = [CPArray class]; + _CPKeyedUnarchiverStringClass = [CPString class]; + _CPKeyedUnarchiverDictionaryClass = [CPDictionary class]; + _CPKeyedUnarchiverArchiverValueClass = [_CPKeyedArchiverValue class]; +} + +- (id)initForReadingWithData:(CPData)data +{ + self = [super init]; + + if (self) + { + _archive = [data plistObject]; + _objects = [CPArray arrayWithObject:[CPNull null]]; + + _plistObject = [_archive objectForKey:_CPKeyedArchiverTopKey]; + _plistObjects = [_archive objectForKey:_CPKeyedArchiverObjectsKey]; + } + + return self; +} + ++ (id)unarchiveObjectWithData:(CPData)data +{ + var unarchiver = [[self alloc] initForReadingWithData:data], + object = [unarchiver decodeObjectForKey:@"root"]; + + [unarchiver finishDecoding]; + + return object; +} + ++ (id)unarchiveObjectWithFile:(CPString)aFilePath +{ +} + ++ (id)unarchiveObjectWithFile:(CPString)aFilePath asynchronously:(BOOL)aFlag +{ +} + +- (BOOL)containsValueForKey:(CPString)aKey +{ + return [_plistObject objectForKey:aKey] != nil; +} + +- (id)_decodeArrayOfObjectsForKey:(CPString)aKey +{ + var object = [_plistObject objectForKey:aKey]; + + if ([object isKindOfClass:_CPKeyedUnarchiverArrayClass]) + { + var index = 0, + count = object.length, + array = []; + + for (; index < count; ++index) + array[index] = _CPKeyedUnarchiverDecodeObjectAtIndex(self, [object[index] objectForKey:_CPKeyedArchiverUIDKey]); + + return array; + } + + return nil; +} + +- (void)_decodeDictionaryOfObjectsForKey:(CPString)aKey +{ + var object = [_plistObject objectForKey:aKey]; + + if ([object isKindOfClass:_CPKeyedUnarchiverDictionaryClass]) + { + var key, + keys = [object keyEnumerator], + dictionary = [CPDictionary dictionary]; + + while (key = [keys nextObject]) + [dictionary setObject:_CPKeyedUnarchiverDecodeObjectAtIndex(self, [[object objectForKey:key] objectForKey:_CPKeyedArchiverUIDKey]) forKey:key]; + + return dictionary; + } + + return nil; +} + +- (BOOL)decodeBoolForKey:(CPString)aKey +{ + return [self decodeObjectForKey:aKey]; +} + +- (float)decodeFloatForKey:(CPString)aKey +{ + return [self decodeObjectForKey:aKey]; +} + +- (double)decodeDoubleForKey:(CPString)aKey +{ + return [self decodeObjectForKey:aKey]; +} + +- (int)decodeIntForKey:(CPString)aKey +{ + return [self decodeObjectForKey:aKey]; +} + +- (CPPoint)decodePointForKey:(CPString)aKey +{ + var object = [self decodeObjectForKey:aKey]; + + if(object) + return CPPointFromString(object); + else + return CPPointMake(0.0, 0.0); +} + +- (CPRect)decodeRectForKey:(CPString)aKey +{ + var object = [self decodeObjectForKey:aKey]; + + if(object) + return CPRectFromString(object); + else + return CPRectMakeZero(); +} + +- (CPSize)decodeSizeForKey:(CPString)aKey +{ + var object = [self decodeObjectForKey:aKey]; + + if(object) + return CPSizeFromString(object); + else + return CPSizeMake(0.0, 0.0); +} + +- (id)decodeObjectForKey:(CPString)aKey +{ + var object = [_plistObject objectForKey:aKey]; + + if ([object isKindOfClass:_CPKeyedUnarchiverDictionaryClass]) + return _CPKeyedUnarchiverDecodeObjectAtIndex(self, [object objectForKey:_CPKeyedArchiverUIDKey]); + else if ([object isKindOfClass:[CPNumber class]]) + return object; +/* else + alert([object className] + " " + object + " " + aKey + " " + [_plistObject description]);*/ + + return nil; +} + +- (void)finishDecoding +{ + if (_delegateSelectors & _CPKeyedUnarchiverWillFinishSelector) + [_delegate unarchiverWillFinish:self]; + + if (_delegateSelectors & _CPKeyedUnarchiverDidFinishSelector) + [_delegate unarchiverDidFinish:self]; +} + +- (id)delegate +{ + return _delegate; +} + +- (void)setDelegate:(id)aDelegate +{ + _delegate = aDelegate; + + if ([_delegate respondsToSelector:@selector(unarchiver:CannotDecodeObjectOfClassName:originalClass:)]) + _delegateSelectors |= _CPKeyedUnarchiverCannotDecodeObjectOfClassNameOriginalClassSelector; + + if ([_delegate respondsToSelector:@selector(unarchiver:didDecodeObject:)]) + _delegateSelectors |= _CPKeyedUnarchiverDidDecodeObjectSelector; + + if ([_delegate respondsToSelector:@selector(unarchiver:willReplaceObject:withObject:)]) + _delegateSelectors |= _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector; + + if ([_delegate respondsToSelector:@selector(unarchiverWillFinish:)]) + _delegateSelectors |= _CPKeyedUnarchiverWilFinishSelector; + + if ([_delegate respondsToSelector:@selector(unarchiverDidFinish:)]) + _delegateSelectors |= _CPKeyedUnarchiverDidFinishSelector; +} + +- (BOOL)allowsKeyedCoding +{ + return YES; +} + +@end + +var _CPKeyedUnarchiverDecodeObjectAtIndex = function(self, anIndex) +{ + var object = self._objects[anIndex]; + + if (object) + if (object == self._objects[0]) + return nil; + else + return object; + + var object, + plistObject = self._plistObjects[anIndex]; + + if ([plistObject isKindOfClass:_CPKeyedUnarchiverDictionaryClass]) + { + var plistClass = self._plistObjects[[[plistObject objectForKey:_CPKeyedArchiverClassKey] objectForKey:_CPKeyedArchiverUIDKey]], + className = [plistClass objectForKey:_CPKeyedArchiverClassNameKey], + classes = [plistClass objectForKey:_CPKeyedArchiverClassesKey], + theClass = CPClassFromString(className); + + object = [theClass alloc]; + + // It is important to do this before calling initWithCoder so that decoding can be self referential (something = self). + self._objects[anIndex] = object; + + var savedPlistObject = self._plistObject; + + self._plistObject = plistObject; + + var processedObject = [object initWithCoder:self]; + + self._plistObject = savedPlistObject; + + if (processedObject != object) + { + if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector) + [self._delegate unarchiver:self willReplaceObject:object withObject:processedObject]; + + object = processedObject; + self._objects[anIndex] = processedObject; + } + + processedObject = [object awakeAfterUsingCoder:self]; + + if (processedObject == object) + { + if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector) + [self._delegate unarchiver:self willReplaceObject:object withObject:processedObject]; + + object = processedObject; + self._objects[anIndex] = processedObject; + } + + if (self._delegate) + { + if (self._delegateSelectors & _CPKeyedUnarchiverDidDecodeObjectSelector) + processedObject = [self._delegate unarchiver:self didDecodeObject:object]; + + if (processedObject != object) + { + if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector) + [self._delegate unarchiver:self willReplaceObject:object withObject:processedObject]; + + object = processedObject; + self._objects[anIndex] = processedObject; + } + } + } + else + { + self._objects[anIndex] = object = plistObject; + + if ([object class] == _CPKeyedUnarchiverStringClass) + { + if (object == _CPKeyedArchiverNullString) + { + self._objects[anIndex] = self._objects[0]; + + return nil; + } + else + self._objects[anIndex] = object = plistObject; + } + } + + // If this object is a member of _CPKeyedArchiverValue, then we know + // that it is a wrapper for a primitive JavaScript object. + if ([object isMemberOfClass:_CPKeyedUnarchiverArchiverValueClass]) + object = [object JSObject]; + + return object; +} + diff --git a/Foundation/CPLog.j b/Foundation/CPLog.j new file mode 100644 index 0000000000..82d67a1d15 --- /dev/null +++ b/Foundation/CPLog.j @@ -0,0 +1,418 @@ +/* + * CPLog.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +window.CPLogDisable = false; + +var CPLogDefaultTitle = "Cappuccino"; + +var CPLogLevels = ["fatal", "error", "warn", "info", "debug", "trace"]; +var CPLogDefaultLevel = CPLogLevels[0]; + +var CPLogLevelsInverted = {}; +for (var i = 0; i < CPLogLevels.length; i++) + CPLogLevelsInverted[CPLogLevels[i]] = i; + +var _CPLogRegistrations = {}; + +// helpers: +var _randomInteger = function(max) { return Math.round(Math.random() * (typeof(max) != "undefined" ? max : (1 << 30))); } +var _randomArrayElement = function(element) { return element[_randomInteger(element.length)]; } + +var _CPFormatLogMessage = function(aString, aLevel, aTitle) +{ + var now = new Date(), + zero = function(number, zeros) + { + var digits = number.toString(); + return zeros.substring(0, zeros.length - digits.length) + digits; + } + + var level = aLevel ? " ["+aLevel+"]" : ""; + var message = + now.getFullYear() + "-" + zero(now.getMonth()+1, "00") + "-" + zero(now.getDate(), "00") + " " + + zero(now.getHours(), "00") + ":" + zero(now.getMinutes(), "00") + ":" + zero(now.getSeconds(), "00") + "." + + zero(now.getMilliseconds(), "000")+" " + aTitle + level + ": " + aString; + + return message; +} + +// Register Functions: + +// Register a logger for all levels, or up to an optional max level +function CPLogRegister(aProvider, aMaxLevel) +{ + CPLogRegisterRange(aProvider, CPLogLevels[0], aMaxLevel || CPLogLevels[CPLogLevels.length-1]); +} +// Register a logger for a range of levels +function CPLogRegisterRange(aProvider, aMinLevel, aMaxLevel) +{ + var min = CPLogLevelsInverted[aMinLevel]; + var max = CPLogLevelsInverted[aMaxLevel]; + + if (min != undefined && max != undefined) + for (var i = 0; i <= max; i++) + CPLogRegisterSingle(aProvider, CPLogLevels[i]); +} +// Regsiter a logger for +function CPLogRegisterSingle(aProvider, aLevel) +{ + if (_CPLogRegistrations[aLevel] == undefined) + _CPLogRegistrations[aLevel] = [aProvider]; + else + _CPLogRegistrations[aLevel].push(aProvider); +} + +// Main CPLog, which dispatches to individual loggers +function CPLog(aString, aLevel, aTitle) +{ + if (aTitle == undefined) + aTitle = CPLogDefaultTitle; + if (aLevel == undefined) + aLevel = CPLogDefaultLevel; + + if (_CPLogRegistrations[aLevel]) + for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++) + _CPLogRegistrations[aLevel][i](aString, aLevel, aTitle); +} + +// Shortcuts for common log levels (CPLog.fatal(), CPLog.error(), etc) +for (var i = 0; i < CPLogLevels.length; i++) + CPLog[CPLogLevels[i]] = (function(level) { return function(message, title) { CPLog(message, level, title); }; })(CPLogLevels[i]); + +// Loggers: + +ANSI_ESC = String.fromCharCode(0x1B); +ANSI_CSI = ANSI_ESC + '['; +ANSI_TEXT_PROP = 'm'; +ANSI_RESET = '0'; +ANSI_BOLD = '1'; +ANSI_FAINT = '2'; // unsupported? +ANSI_NORMAL = '22'; +ANSI_ITALIC = '3'; // unsupported? +ANSI_UNDER = '4'; +ANSI_UNDER_DBL = '21'; // unsupported? +ANSI_UNDER_OFF = '24'; +ANSI_BLINK = '5'; +ANSI_BLINK_FAST = '6'; // unsupported? +ANSI_BLINK_OFF = '25'; +ANSI_REVERSE = '7'; +ANSI_POSITIVE = '27'; +ANSI_CONCEAL = '8'; +ANSI_REVEAL = '28'; +ANSI_FG = '3'; +ANSI_BG = '4'; +ANSI_FG_INTENSE = '9'; +ANSI_BG_INTENSE = '10'; +ANSI_BLACK = '0'; +ANSI_RED = '1'; +ANSI_GREEN = '2'; +ANSI_YELLOW = '3'; +ANSI_BLUE = '4'; +ANSI_MAGENTA = '5'; +ANSI_CYAN = '6'; +ANSI_WHITE = '7'; + +var colorCodeMap = { + "black" : ANSI_BLACK, + "red" : ANSI_RED, + "green" : ANSI_GREEN, + "yellow" : ANSI_YELLOW, + "blue" : ANSI_BLUE, + "magenta" : ANSI_MAGENTA, + "cyan" : ANSI_CYAN, + "white" : ANSI_WHITE +} + +ANSIControlCode = function(code, parameters) +{ + if (parameters == undefined) + parameters = ""; + else if (typeof(parameters) == 'object' && (parameters instanceof Array)) + parameters = parameters.join(';'); + return ANSI_CSI + String(parameters) + String(code); +} + +// simple text helpers: + +ANSITextApplyProperties = function(string, properties) +{ + return ANSIControlCode(ANSI_TEXT_PROP, properties) + String(string) + ANSIControlCode(ANSI_TEXT_PROP); +} + +ANSITextColorize = function(string, color) +{ + if (colorCodeMap[color] == undefined) + return string; + return ANSITextApplyProperties(string, ANSI_FG + colorCodeMap[color]); +} + +// CPLogPrint uses the print() functions present in many non-browser command line JavaScript interpreters +var levelColorMap = { + "fatal": "red", + "error": "red", + "warn" : "yellow", + "info" : "green", + "debug": "cyan", + "trace": "blue" +} + +function CPLogPrint(aString, aLevel, aTitle) +{ + if (typeof print != "undefined") + { + if (aLevel == "fatal" || aLevel == "error" || aLevel == "warn") + var message = ANSITextColorize(_CPFormatLogMessage(aString, aLevel, aTitle), levelColorMap[aLevel]); + else + var message = _CPFormatLogMessage(aString, ANSITextColorize(aLevel, levelColorMap[aLevel]), aTitle); + print(message); + } +} + +// CPLogAlert uses basic browser alert() functions +function CPLogAlert(aString, aLevel, aTitle) +{ + if (typeof alert != "undefined" && !window.CPLogDisable) + { + var message = _CPFormatLogMessage(aString, aLevel, aTitle); + window.CPLogDisable = !confirm(message + "\n\n(Click cancel to stop log alerts)"); + } +} + +// CPLogConsole uses the built in "console" object +function CPLogConsole(aString, aLevel, aTitle) +{ + if (typeof console != "undefined") + { + var message = _CPFormatLogMessage(aString, aLevel, aTitle); + + var logger = { + "fatal": "error", + "error": "error", + "warn": "warn", + "info": "info", + "debug": "debug", + "trace": "debug" + }[aLevel]; + + if (logger && console[logger]) + console[logger](message); + else if (console.log) + console.log(message); + } +} + +// CPLogPopup uses a slick popup window in the browser: +var CPLogWindow = null; +CPLogPopup = function(aString, aLevel, aTitle) +{ + try { + if (window.CPLogDisable || window.open == undefined) + return; + + if (!CPLogWindow || !CPLogWindow.document) + { + CPLogWindow = window.open("", "_blank", "width=600,height=400,status=no,resizable=yes,scrollbars=yes"); + + if (!CPLogWindow) { + window.CPLogDisable = !confirm(aString + "\n\n(Disable pop-up blocking for CPLog window; Click cancel to stop log alerts)"); + return; + } + + _CPLogInitPopup(CPLogWindow); + } + + var logDiv = CPLogWindow.document.createElement("div"); + logDiv.setAttribute("class", aLevel || "fatal"); + + var message = _CPFormatLogMessage(aString, null, aTitle); + + logDiv.appendChild(CPLogWindow.document.createTextNode(message)); + CPLogWindow.log.appendChild(logDiv); + + if (CPLogWindow.focusEnabled.checked) + CPLogWindow.focus(); + if (CPLogWindow.blockEnabled.checked) + CPLogWindow.blockEnabled.checked = CPLogWindow.confirm(message+"\nContinue blocking?"); + if (CPLogWindow.scrollEnabled.checked) + CPLogWindow.scrollToBottom(); + } catch(e) { + // TODO: some error handling/reporting + } +} + +var _CPLogInitPopup = function(logWindow) +{ + var doc = logWindow.document; + + // HACK so that head is available below: + doc.writeln(""); + + doc.title = CPLogDefaultTitle + " Run Log"; + + var head = doc.getElementsByTagName("head")[0]; + var body = doc.getElementsByTagName("body")[0]; + + var base = window.location.protocol + "//" + window.location.host + window.location.pathname; + base = base.substring(0,base.lastIndexOf("/")+1); + + var link = doc.createElement("link"); + link.setAttribute("type", "text/css"); + link.setAttribute("rel", "stylesheet"); + link.setAttribute("href", base+"Frameworks/Foundation/Resources/log.css"); + link.setAttribute("media", "screen"); + head.appendChild(link); + + var div = doc.createElement("div"); + div.setAttribute("id", "header"); + body.appendChild(div); + + // Enablers + var ul = doc.createElement("ul"); + ul.setAttribute("id", "enablers"); + div.appendChild(ul); + + for (var i = 0; i < CPLogLevels.length; i++) { + var li = doc.createElement("li"); + li.setAttribute("id", "en"+CPLogLevels[i]); + li.setAttribute("class", CPLogLevels[i]); + li.setAttribute("onclick", "toggle(this);"); + li.setAttribute("enabled", "yes"); + li.appendChild(doc.createTextNode(CPLogLevels[i])); + ul.appendChild(li); + } + + // Options + var ul = doc.createElement("ul"); + ul.setAttribute("id", "options"); + div.appendChild(ul); + + var options = {"focus":["Focus",false], "block":["Block",false], "wrap":["Wrap",false], "scroll":["Scroll",true], "close":["Close",true]}; + for (o in options) { + var li = doc.createElement("li"); + ul.appendChild(li); + + logWindow[o+"Enabled"] = doc.createElement("input"); + logWindow[o+"Enabled"].setAttribute("id", o); + logWindow[o+"Enabled"].setAttribute("type", "checkbox"); + if (options[o][1]) + logWindow[o+"Enabled"].setAttribute("checked", "checked"); + li.appendChild(logWindow[o+"Enabled"]); + + var label = doc.createElement("label"); + label.setAttribute("for", o); + label.appendChild(doc.createTextNode(options[o][0])); + li.appendChild(label); + } + + // Log + logWindow.log = doc.createElement("div"); + logWindow.log.setAttribute("class", "enerror endebug enwarn eninfo enfatal entrace"); + body.appendChild(logWindow.log); + + logWindow.toggle = function(elem) { + var enabled = (elem.getAttribute("enabled") == "yes") ? "no" : "yes"; + elem.setAttribute("enabled", enabled); + + if (enabled == "yes") + logWindow.log.className += " " + elem.id + else + logWindow.log.className = logWindow.log.className.replace(new RegExp("[\\s]*"+elem.id, "g"), ""); + } + + // Scroll + logWindow.scrollToBottom = function() { + logWindow.scrollTo(0, body.offsetHeight); + } + + // Wrap + logWindow.wrapEnabled.addEventListener("click", function() { + logWindow.log.setAttribute("wrap", logWindow.wrapEnabled.checked ? "yes" : "no"); + }, false); + + // Clear + logWindow.addEventListener("keydown", function(e) { + var e = e || logWindow.event; + if (e.keyCode == 75 && (e.ctrlKey || e.metaKey)) { + while (logWindow.log.firstChild) { + logWindow.log.removeChild(logWindow.log.firstChild); + } + e.preventDefault(); + } + }, "false"); + + // Parent closing + window.addEventListener("unload", function() { + if (logWindow && logWindow.closeEnabled && logWindow.closeEnabled.checked) { + window.CPLogDisable = true; + logWindow.close(); + } + }, false); + + // Log popup closing + logWindow.addEventListener("unload", function() { + if (!window.CPLogDisable) { + window.CPLogDisable = !confirm("Click cancel to stop logging"); + } + }, false); +} + +// CPLogServer sends log messages to the server +//var _CPLogServerBatch = true; +//var _CPLogServerSessionID = _randomInteger(); +//function CPLogServer(aString, aLevel, aTitle) +//{ +// var entry = { title: aTitle, message: aString, level: aLevel, timestamp: new Date().getTime() } +// +// if (_CPLogServerBatch) +// { +// } +// else +// { +// +// } +//} +// +//function _CPLogServerSubmit() +//{ +// +//} + +// Testing purposes: + +var lorem = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas fermentum, elit non lobortis cursus, orci velit suscipit est, id mollis turpis mi eget orci. Ut aliquam sollicitudin metus. Mauris at sapien sed sapien congue iaculis. Nulla lorem urna, bibendum id, laoreet iaculis, nonummy eget, massa. Phasellus ullamcorper commodo velit."; + +function CPLogTest(n) { + for(var i=0;i O(N^2)), so to avoid it, + // we keep track of whether observers are added or removed, and only do our + // rigorous testing in those cases. + var object = [aNotification object]; + + if (object != nil && (_postingObservers = [_objectObservers objectForKey:[object hash]])) + { + var observers = _postingObservers, + count = observers.length; + + _observerRemoval = NO; + while (count--) + { + var observer = _postingObservers[count]; + + // if there wasn't removal of an observer during this posting, or there + // was but we are still in the observer list... + if (!_observerRemoval || [observers indexOfObjectIdenticalTo:observer] != CPNotFound) + [observer postNotification:aNotification]; + + } + } + + // Now do the same for the nil object observers... + _postingObservers = [_objectObservers objectForKey:[[CPNull null] hash]]; + + if (!_postingObservers) + return; + + var observers = _postingObservers, + count = observers.length; + + _observerRemoval = NO; + while (count--) + { + var observer = _postingObservers[count]; + + // if there wasn't removal of an observer during this posting, or there + // was but we are still in the observer list... + if (!_observerRemoval || [observers indexOfObjectIdenticalTo:observer] != CPNotFound) + [observer postNotification:aNotification]; + } + + _postingObservers = nil; +} + +- (unsigned)count +{ + return [_objectObservers count]; +} + +@end + +@implementation _CPNotificationObserver : CPObject +{ + id _observer; + SEL _selector; +} + +- (id)initWithObserver:(id)anObserver selector:(SEL)aSelector +{ + if (self) + { + _observer = anObserver; + _selector = aSelector; + } + + return self; +} + +- (id)observer +{ + return _observer; +} + +-(void)postNotification:(CPNotification)aNotification +{ + [_observer performSelector:_selector withObject:aNotification]; +} + +@end diff --git a/Foundation/CPNull.j b/Foundation/CPNull.j new file mode 100644 index 0000000000..7d267a1f96 --- /dev/null +++ b/Foundation/CPNull.j @@ -0,0 +1,47 @@ +/* + * CPNull.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" + +var CPNullSharedNull = nil; + +@implementation CPNull : CPObject +{ +} + +/*+ (id)alloc +{ + if (CPNullSharedNull) + return CPNullSharedNull; + + return [super alloc]; +}*/ + ++ (CPNull)null +{ + if (!CPNullSharedNull) + CPNullSharedNull = [[CPNull alloc] init]; + + return CPNullSharedNull; +} + +@end \ No newline at end of file diff --git a/Foundation/CPNumber.j b/Foundation/CPNumber.j new file mode 100644 index 0000000000..99ead4aaab --- /dev/null +++ b/Foundation/CPNumber.j @@ -0,0 +1,318 @@ +/* + * CPNumber.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" +import "CPObjJRuntime.j" + +var __placeholder = new Number(), + _CPNumberHashes = { }; + +@implementation CPNumber : CPObject + ++ (id)alloc +{ + return __placeholder; +} + ++ (id)numberWithBool:(BOOL)aBoolean +{ + return aBoolean; +} + ++ (id)numberWithChar:(char)aChar +{ + if (aChar.charCodeAt) + return aChar.charCodeAt(0); + + return aChar; +} + ++ (id)numberWithDouble:(double)aDouble +{ + return aDouble; +} + ++ (id)numberWithFloat:(float)aFloat +{ + return aFloat; +} + ++ (id)numberWithInt:(int)anInt +{ + return anInt; +} + ++ (id)numberWithLong:(long)aLong +{ + return aLong; +} + ++ (id)numberWithLongLong:(long long)aLongLong +{ + return aLongLong; +} + ++ (id)numberWithShort:(short)aShort +{ + return aShort; +} + ++ (id)numberWithUnsignedChar:(unsigned char)aChar +{ + if (aChar.charCodeAt) + return aChar.charCodeAt(0); + + return aChar; +} + ++ (id)numberWithUnsignedInt:(unsigned)anUnsignedInt +{ + return anUnsignedInt; +} + ++ (id)numberWithUnsignedLong:(unsigned long)anUnsignedLong +{ + return anUnsignedLong; +} + ++ (id)numberWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong +{ + return anUnsignedLongLong; +} + ++ (id)numberWithUnsignedShort:(unsigned short)anUnsignedShort +{ + return anUnsignedShort; +} + +- (id)initWithBool:(BOOL)aBoolean +{ + return aBoolean; +} + +- (id)initWithChar:(char)aChar +{ + if (aChar.charCodeAt) + return aChar.charCodeAt(0); + + return aChar; +} + +- (id)initWithDouble:(double)aDouble +{ + return aDouble; +} + +- (id)initWithFloat:(float)aFloat +{ + return aFloat; +} + +- (id)initWithInt:(int)anInt +{ + return anInt; +} + +- (id)initWithLong:(long)aLong +{ + return aLong; +} + +- (id)initWithLongLong:(long long)aLongLong +{ + return aLongLong; +} + +- (id)initWithShort:(short)aShort +{ + return aShort; +} + +- (id)initWithUnsignedChar:(unsigned char)aChar +{ + if (aChar.charCodeAt) + return aChar.charCodeAt(0); + + return aChar; +} + +- (id)initWithUnsignedInt:(unsigned)anUnsignedInt +{ + return anUnsignedInt; +} + +- (id)initWithUnsignedLong:(unsigned long)anUnsignedLong +{ + return anUnsignedLong; +} + +- (id)initWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong +{ + return anUnsignedLongLong; +} + +- (id)initWithUnsignedShort:(unsigned short)anUnsignedShort +{ + return anUnsignedShort; +} + +- (CPString)hash +{ + if (!_CPNumberHashes[self]) + _CPNumberHashes[self] = _objj_generateObjectHash(); + + return _CPNumberHashes[self]; +} + +- (BOOL)boolValue +{ + // Ensure we return actual booleans. + return self ? true : false; +} + +- (char)charValue +{ + return String.fromCharCode(self); +} + +/* +FIXME: Do we need this? +*/ +- (CPDecimal)decimalValue +{ + objj_throw_exception("decimalValue: NOT YET IMPLEMENTED"); +} + +- (CPString)descriptionWithLocale:(CPDictionary)aDictionary +{ + if (!aDictionary) return toString(); + + objj_throw_exception("descriptionWithLocale: NOT YET IMPLEMENTED"); +} + +- (CPString)description +{ + return [self descriptionWithLocale:nil]; +} + +- (double)doubleValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (float)floatValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (int)intValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (long long)longLongValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (long)longValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (short)shortValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (CPString)stringValue +{ + return toString(); +} + +- (unsigned char)unsignedCharValue +{ + return String.fromCharCode(self); +} + +- (unsigned int)unsignedIntValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (unsigned long long)unsignedLongLongValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (unsigned long)unsignedLongValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (unsigned short)unsignedShortValue +{ + if (typeof self == "boolean") return self ? 1 : 0; + return self; +} + +- (CPComparisonResult)compare:(CPNumber)aNumber +{ + if (self > aNumber) return CPOrderedDescending; + else if (self < aNumber) return CPOrderedAscending; + + return CPOrderedSame; +} + +- (BOOL)isEqualToNumber:(CPNumber)aNumber +{ + return self == aNumber; +} + +@end + +@implementation CPNumber (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [aCoder decodeNumber]; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeNumber:self forKey:@"self"]; +} + +@end + +Number.prototype.isa = CPNumber; +Boolean.prototype.isa = CPNumber; +[CPNumber initialize]; diff --git a/Foundation/CPObjJRuntime.j b/Foundation/CPObjJRuntime.j new file mode 100644 index 0000000000..fd789a6f9a --- /dev/null +++ b/Foundation/CPObjJRuntime.j @@ -0,0 +1,68 @@ +/* + * CPObjJRuntime.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPLog.j" + +function CPStringFromSelector(aSelector) +{ + return sel_getName(aSelector); +} + +function CPSelectorFromString(aSelectorName) +{ + return sel_registerName(aSelectorName); +} + +function CPClassFromString(aClassName) +{ + return objj_getClass(aClassName); +} + +function CPStringFromClass(aClass) +{ + return class_getName(aClass); +} + +CPOrderedAscending = -1; +CPOrderedSame = 0; +CPOrderedDescending = 1; + +CPNotFound = -1; + +MIN = Math.min; +MAX = Math.max; +ABS = Math.abs; + +/*function MIN(lhs, rhs) +{ + return Math.min(lhs, rhs); +} + +function MAX(lhs, rhs) +{ + return Math.max(lhs, rhs); +} + +function ABS(argument) +{ + return Math.abs(argument); +}*/ diff --git a/Foundation/CPObject.j b/Foundation/CPObject.j new file mode 100644 index 0000000000..35587acec0 --- /dev/null +++ b/Foundation/CPObject.j @@ -0,0 +1,291 @@ +/* + * CPObject.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +@implementation CPObject +{ + Class isa; +} + ++ (void)load +{ +} + ++ (void)initialize +{ +// CPLog("calling initialize "+self.name); +} + ++ (id)new +{ + return [[self alloc] init]; +} + ++ (id)alloc +{ +// CPLog("calling alloc on " + self.name + "."); + return class_createInstance(self); +} + +- (id)init +{ + return self; +} + +- (id)copy +{ + return self; +} + +- (id)mutableCopy +{ + return [self copy]; +} + +- (void)dealloc +{ +} + +// Identifying classes + ++ (Class)class +{ + return self; +} + +- (Class)class +{ + return isa; +} + ++ (Class)superclass +{ + return super_class; +} + ++ (BOOL)isSubclassOfClass:(Class)aClass +{ + var theClass = self; + + for(; theClass; theClass = theClass.super_class) + if(theClass == aClass) return YES; + + return NO; +} + +- (BOOL)isKindOfClass:(Class)aClass +{ + return [isa isSubclassOfClass:aClass]; +} + +- (BOOL)isMemberOfClass:(Class)aClass +{ + return self.isa == aClass; +} + +- (BOOL)isProxy +{ + return NO; +} + +// Testing class functionality + ++ (BOOL)instancesRespondToSelector:(SEL)aSelector +{ + return class_getInstanceMethod(self, aSelector); +} + +- (BOOL)respondsToSelector:(SEL)aSelector +{ + return class_getInstanceMethod(isa, aSelector) != NULL; +} + +// Obtaining method information + +- (IMP)methodForSelector:(SEL)aSelector +{ + return class_getInstanceMethod(isa, aSelector); +} + ++ (IMP)instanceMethodForSelector:(SEL)aSelector +{ + return class_getInstanceMethod(isa, aSelector); +} + +- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector +{ + // FIXME: We need to implement method signatures. + return nil; +} + +// Describing objects + +- (CPString)description +{ + return "<" + isa.name + " 0x" + [CPString stringWithHash:[self hash]] + ">"; +} + +// Sending Messages + +- (id)performSelector:(SEL)aSelector +{ + return objj_msgSend(self, aSelector); +} + +- (id)performSelector:(SEL)aSelector withObject:(id)anObject +{ + return objj_msgSend(self, aSelector, anObject); +} + +- (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject +{ + return objj_msgSend(self, aSelector, anObject, anotherObject); +} + +// Forwarding Messages + +- (void)forwardInvocation:(CPInvocation)anInvocation +{ + [self doesNotRecognizeSelector:[anInvocation selector]]; +} + +- (void)forward:(SEL)aSelector :(marg_list)args +{ + var signature = [self methodSignatureForSelector:_cmd]; + + if (signature) + { + invocation = [CPInvocation invocationWithMethodSignature:signature]; + + [invocation setTarget:self]; + [invocation setSelector:aSelector]; + + var index = 2, + count = args.length; + + for (; index < count; ++index) + [invocation setArgument:args[index] atIndex:index]; + + [self forwardInvocation:invocation]; + + return [invocation returnValue]; + } + + [self doesNotRecognizeSelector:aSelector]; +} + +// Error Handling + +- (void)doesNotRecognizeSelector:(SEL)aSelector +{ + [CPException raise:CPInvalidArgumentException reason: + (class_isMetaClass(isa) ? "+" : "-") + " [" + [self className] + " " + aSelector + "] unrecognized selector sent to " + + (class_isMetaClass(isa) ? "class" : "instance") + " 0x" + [CPString stringWithHash:[self hash]]]; +} + +// Archiving + +- (void)awakeAfterUsingCoder:(CPCoder)aCoder +{ + return self; +} + +- (Class)classForKeyedArchiver +{ + return [self classForCoder]; +} + +- (Class)classForCoder +{ + return [self class]; +} + +- (id)replacementObjectForArchiver:(CPArchiver)anArchiver +{ + return [self replacementObjectForCoder:anArchiver]; +} + +- (id)replacementObjectForKeyedArchiver:(CPKeyedArchiver)anArchiver +{ + return [self replacementObjectForCoder:anArchiver]; +} + +- (id)replacementObjectForCoder:(CPCoder)aCoder +{ + return self; +} + ++ (id)setVersion:(int)aVersion +{ + version = aVersion; + + return self; +} + ++ (int)version +{ + return version; +} + +// Scripting (?) + +- (CPString)className +{ + return isa.name; +} + +// Extras + +- (id)autorelease +{ + return self; +} + +- (unsigned)hash +{ + return __address; +} + +- (BOOL)isEqual:(id)anObject +{ + return self === anObject; +} + +- (void)retain +{ + return self; +} + +- (void)release +{ +} + +- (id)self +{ + return self; +} + +- (Class)superclass +{ + return isa.super_class; +} + +@end diff --git a/Foundation/CPPropertyListSerialization.j b/Foundation/CPPropertyListSerialization.j new file mode 100644 index 0000000000..0999ea4c3f --- /dev/null +++ b/Foundation/CPPropertyListSerialization.j @@ -0,0 +1,42 @@ +/* + * CPPropertyListSerialization.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +CPPropertyListOpenStepFormat = kCFPropertyListOpenStepFormat; +CPPropertyListXMLFormat_v1_0 = kCFPropertyListXMLFormat_v1_0; +CPPropertyListBinaryFormat_v1_0 = kCFPropertyListBinaryFormat_v1_0; +CPPropertyList280NorthFormat_v1_0 = kCFPropertyList280NorthFormat_v1_0; + +@implementation CPPropertyListSerialization : CPObject +{ +} + ++ (CPData)dataFromPropertyList:(id)aPlist format:(CPPropertyListFormat)aFormat errorDescription:({CPString})anErrorString +{ + return CPPropertyListCreateData(aPlist, aFormat); +} + ++ (id)propertyListFromData:(CPData)data format:(CSPropertyListFormat)aFormat errorDescription:({CPString})errorString +{ + return CPPropertyListCreateFromData(data, aFormat); +} + +@end diff --git a/Foundation/CPRange.j b/Foundation/CPRange.j new file mode 100755 index 0000000000..30c36c86d6 --- /dev/null +++ b/Foundation/CPRange.j @@ -0,0 +1,83 @@ +/* + * CPRange.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +function CPMakeRange(location, length) +{ + return { location: location, length: length }; +} + +function CPCopyRange(aRange) +{ + return { location: aRange.location, length: aRange.length }; +} + +function CPMakeRangeCopy(aRange) +{ + return { location:aRange.location, length:aRange.length }; +} + +function CPEmptyRange(aRange) +{ + return aRange.length == 0; +} + +function CPMaxRange(aRange) +{ + return aRange.location + aRange.length; +} + +function CPEqualRanges(lhsRange, rhsRange) +{ + return ((lhsRange.location == rhsRange.location) && (lhsRange.length == rhsRange.length)); +} + +function CPLocationInRange(aLocation, aRange) +{ + return (aLocation >= aRange.location) && (aLocation < CPMaxRange(aRange)); +} + +function CPUnionRange(lhsRange, rhsRange) +{ + var location = Math.min(lhsRange.location, rhsRange.location); + return CPMakeRange(location, Math.max(CPMaxRange(lhsRange), CPMaxRange(rhsRange)) - location); +} + +function CPIntersectionRange(lhsRange, rhsRange) +{ + if(CPMaxRange(lhsRange) < rhsRange.location || CPMaxRange(rhsRange) < lhsRange.location) + return CPMakeRange(0, 0); + + var location = Math.max(lhsRange.location, rhsRange.location); + return CPMakeRange(location, Math.min(CPMaxRange(lhsRange), CPMaxRange(rhsRange)) - location); +} + +function CPStringFromRange(aRange) +{ + return "{" + aRange.location + ", " + aRange.length + "}"; +} + +function CPRangeFromString(aString) +{ + var comma = aString.indexOf(','); + + return { location:parseInt(aString.substr(1, comma - 1)), length:parseInt(aString.substring(comma + 1, aString.length)) }; +} diff --git a/Foundation/CPRunLoop.j b/Foundation/CPRunLoop.j new file mode 100644 index 0000000000..57c9f690d3 --- /dev/null +++ b/Foundation/CPRunLoop.j @@ -0,0 +1,224 @@ +/* + * CPRunLoop.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" +import "CPArray.j" +import "CPString.j" + +CPDefaultRunLoopMode = @"CPDefaultRunLoopMode"; + +function _CPRunLoopPerformCompare(lhs, rhs) +{ + return [rhs order] - [lhs order]; +} + +var _CPRunLoopPerformPool = [], + _CPRunLoopPerformPoolCapacity = 5; + +@implementation _CPRunLoopPerform : CPObject +{ + id _target; + SEL _selector; + id _argument; + unsigned _order; + CPArray _runLoopModes; +} + ++ (void)_poolPerform:(_CPRunLoopPerform)aPerform +{ + if (!aPerform || _CPRunLoopPerformPool.length >= _CPRunLoopPerformPoolCapacity) + return; + + _CPRunLoopPerformPool.push(aPerform); +} + ++ (_CPRunLoopPerform)performWithSelector:(SEL)aSelector target:(id)aTarget argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes +{ + if (_CPRunLoopPerformPool.length) + { + var perform = _CPRunLoopPerformPool.pop(); + + perform._target = aTarget; + perform._selector = aSelector; + perform._arguments = anArgument; + perform._order = anOrder; + perform._runLoopModes = modes; + + return perform; + } + + return [[self alloc] initWithSelector:aSelector target:aTarget argument:anArgument order:anOrder modes:modes]; +} + +- (id)initWithSelector:(SEL)aSelector target:(SEL)aTarget argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes +{ + self = [super init]; + + if (self) + { + _selector = aSelector; + _target = aTarget; + _argument = anArgument; + _order = anOrder; + _runLoopModes = modes; + } + + return self; +} + +- (SEL)selector +{ + return _selector; +} + +- (id)target +{ + return _target; +} + +- (id)argument +{ + return _argument; +} + +- (unsigned)order +{ + return _order; +} + +- (BOOL)fireInMode:(CPString)aRunLoopMode +{ + if ([_runLoopModes containsObject:aRunLoopMode]) + { + [_target performSelector:_selector withObject:_argument]; + + return YES; + } + + return NO; +} + +@end + +@implementation CPRunLoop : CPObject +{ + CPArray _queuedPerforms; + CPArray _orderedPerforms; + BOOL _isPerformingSelectors; +} + ++ (void)initialize +{ + if (self != [CPRunLoop class]) + return; + + CPMainRunLoop = [[CPRunLoop alloc] init]; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + _queuedPerforms = []; + _orderedPerforms = []; + } + + return self; +} + ++ (CPRunLoop)currentRunLoop +{ + return CPMainRunLoop; +} + ++ (CPRunLoop)mainRunLoop +{ + return CPMainRunLoop; +} + +- (void)performSelector:(SEL)aSelector target:(id)aTarget argument:(id)anArgument order:(int)anOrder modes:(CPArray)modes +{ + var perform = [_CPRunLoopPerform performWithSelector:aSelector target:aTarget argument:anArgument order:anOrder modes:modes]; + + if (_isPerformingSelectors) + _queuedPerforms.push(perform); + else + { + var count = _orderedPerforms.length; + + // We sort ourselves in reverse because we iterate this list backwards. + while (count--) + if (anOrder < [_orderedPerforms[count] order]) + break; + + _orderedPerforms.splice(count + 1, 0, perform); + } +} + +- (void)cancelPerformSelector:(SEL)aSelector target:(id)aTarget argument:(id)anArgument +{ + var count = _orderedPerforms.length; + + while (count--) + { + var perform = _orderedPerforms[count]; + + if ([perform selector] == aSelector && [perform target] == aTarget && [perform argument] == anArgument) + [_orderedPerforms removeObjectAtIndex:count]; + } +} + +- (void)performSelectors +{ + if (_isPerformingSelectors) + return; + + _isPerformingSelectors = YES; + + var index = _orderedPerforms.length; + + while (index--) + { + var perform = _orderedPerforms[index]; + + if ([perform fireInMode:CPDefaultRunLoopMode]) + { + [_CPRunLoopPerform _poolPerform:perform]; + + _orderedPerforms.splice(index, 1); + } + } + + _isPerformingSelectors = NO; + + if (_queuedPerforms.length) + { + _orderedPerforms = _orderedPerforms.concat(_queuedPerforms); + _orderedPerforms.sort(_CPRunLoopPerformCompare); + } + + _queuedPerforms = []; +} + +@end \ No newline at end of file diff --git a/Foundation/CPSortDescriptor.j b/Foundation/CPSortDescriptor.j new file mode 100755 index 0000000000..197cc7372a --- /dev/null +++ b/Foundation/CPSortDescriptor.j @@ -0,0 +1,83 @@ +/* + * CPSortDescriptor.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" +import "CPObjJRuntime.j" + +@implementation CPSortDescriptor : CPObject +{ + CPString _key; + SEL _selector; + BOOL _ascending; +} + +// Initializing a sort descriptor + +- (id)initWithKey:(CPString)aKey ascending:(BOOL)isAscending +{ + return [self initWithKey:aKey ascending:isAscending selector:@selector(compare:)]; +} + +- (id)initWithKey:(CPString)aKey ascending:(BOOL)isAscending selector:(SEL)aSelector +{ + self = [super init]; + + if (self) + { + _key = aKey; + _ascending = isAscending; + _selector = aSelector; + } + + return self; +} + +// Getting information about a sort descriptor + +- (BOOL)ascending +{ + return _ascending; +} + +- (CPString)key +{ + return _key; +} + +- (SEL)selector +{ + return _selector; +} + +// Using sort descriptors + +- (CPComparisonResult)compareObject:(id)lhsObject withObject:(id)rhsObject +{ + return (_ascending ? 1 : -1) * [[lhsObject valueForKey:_key] performSelector:_selector withObject:[rhsObject valueForKey:_key]]; +} + +- (id)reversedSortDescriptor +{ + return [[[self class] alloc] initWithKey:_key ascending:!_ascending selector:_selector]; +} + +@end \ No newline at end of file diff --git a/Foundation/CPString.j b/Foundation/CPString.j new file mode 100644 index 0000000000..d52d3df7fd --- /dev/null +++ b/Foundation/CPString.j @@ -0,0 +1,285 @@ +/* + * CPString.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" + + +CPCaseInsensitiveSearch = 1; +CPLiteralSearch = 2; +CPBackwardsSearch = 4; +CPAnchoredSearch = 8; +CPNumericSearch = 64; + +var CPStringHashes = new objj_dictionary(); + +@implementation CPString : CPObject + ++ (id)alloc +{ + return new String; +} + ++ (id)string +{ + return [[self alloc] init]; +} + ++ (id)stringWithHash:(unsigned)aHash +{ + var zeros = "000000", + digits = aHash.toString(16); + + return zeros.substring(0, zeros.length - digits.length) + digits; +} + ++ (id)stringWithString:(CPString)aString +{ + return [[self alloc] initWithString:aString]; +} + +- (id)initWithString:(CPString)aString +{ + return aString + ""; +} + +- (CPString)description +{ + return "<" + self.isa.name + " 0x" + [CPString stringWithHash:[self hash]] + " \"" + self + "\">"; +} + +- (int)length +{ + return length; +} + +- (char)characterAtIndex:(unsigned)anIndex +{ + return charAt(anIndex); +} + +// Combining strings + +- (CPString)stringByAppendingString:(CPString)aString +{ + return self + aString; +} + +- (CPString)stringByPaddingToLength:(unsigned)aLength withString:(CPString)aString startingAtIndex:(unsigned)anIndex +{ + if (length == aLength) + return self; + + if (aLength < length) + return substr(0, aLength); + + var string = self, + substring = aString.substr(anIndex), + difference = aLength - length; + + while ((difference -= substring.length) > 0) + string += substring; + + if (difference) string += substring.substr(difference + substring.length); +} + +- (CPArray)componentsSeparatedByString:(CPString)aString +{ + return split(aString); +} + +- (CPString)substringFromIndex:(unsigned)anIndex +{ + return substr(anIndex); +} + +- (CPString)substringWithRange:(CPRange)aRange +{ + return substr(aRange.location, aRange.length); +} + +- (CPString)substringToIndex:(unsigned)anIndex +{ + return substring(0, anIndex); +} + +// Finding characters and substrings + +- (CPRange)rangeOfString:(CPString)aString +{ + var location = indexOf(aString); + + return CPMakeRange(location, location == CPNotFound ? 0 : aString.length); +} + +- (CPRange)rangeOfString:(CPString)aString options:(int)aMask +{ + var string = self, + location = CPNotFound; + + if (aMask & CPCaseInsensitiveSearch) + { + string = string.toLowerCase(); + aString = aString.toLowerCase(); + } + + if (CPBackwardsSearch) location = lastIndexOf(aString, aMask & CPAnchoredSearch ? length - aString.length : 0); + else if (aMask & CPAnchoredSearch) location = substr(0, aString.length).indexOf(aString) != CPNotFound ? 0 : CPNotFound; + else location = indexOf(aString); + + return CPMakeRange(location, location == CPNotFound ? 0 : aString.length); +} + +// Identifying and comparing strings + +- (CPComparisonResult)caseInsensitiveCompare:(CPString)aString +{ + return [self compare:aString options:CPCaseInsensitiveSearch] +} + +- (CPComparisonResult)compare:(CPString)aString options:(int)aMask +{ + var lhs = self, + rhs = aString; + + if (aMask & CPCaseInsensitiveSearch) + { + lhs = lhs.toLowerCase(); + rhs = rhs.toLowerCase(); + } + + if (lhs < rhs) + return CPOrderedAscending; + else if (lhs > rhs) + return CPOrderedDescending; + + return CPOrderedSame; +} + +- (BOOL)hasPrefix:(CPString)aString +{ + return aString && aString != "" && indexOf(aString) == 0; +} + +- (BOOL)hasSuffix:(CPString)aString +{ + return aString && aString != "" && lastIndexOf(aString) == (length - aString.length); +} + +- (BOOL)isEqualToString:(CPString)aString +{ + return self == aString; +} + +- (unsigned)hash +{ + var hash = dictionary_getValue(CPStringHashes, self); + + if (!hash) + { + hash = _objj_generateObjectHash(); + dictionary_setValue(CPStringHashes, self, hash); + } + + return hash; +} + +- (CPString)capitalizedString +{ + var i = 0, + last = true, + capitalized = self; + + for(; i < length; ++i) + { + var character = charAt(i); + if (character == ' ' || character == '\t' || character == '\n') last = true; + else + { + if (last) capitalized = capitalized.substr(0, i - 1) + character.toUpperCase() + capitalized.substr(i); + last = false; + } + } + + return capitalized; +} + +- (CPString)lowercaseString +{ + return toLowerCase(); +} + +- (CPString)uppercaseString +{ + return toUpperCase(); +} + +- (double)doubleValue +{ + return eval(self); +} + +- (float)floatValue +{ + return eval(self); +} + +- (int)intValue +{ + return parseInt(self); +} + +- (CPArray)pathComponents +{ + return split('/'); +} + +- (CPString)pathExtension +{ + return substr(lastIndexOf('.')+1); +} + +- (CPString)lastPathComponent +{ + var components = [self pathComponents]; + return components[components.length -1]; +} + +- (CPString)stringByDeletingLastPathComponent +{ + // FIMXE: this is wrong: a/a/ returns a/a/. + return substr(0, lastIndexOf('/') + 1); +} + +- (CPString)stringByStandardizingPath +{ + return objj_standardize_path(self); +} + +- (CPString)copy +{ + return new String(self); +} + +@end + + +String.prototype.isa = CPString; diff --git a/Foundation/CPURLConnection.j b/Foundation/CPURLConnection.j new file mode 100644 index 0000000000..a98acb9b19 --- /dev/null +++ b/Foundation/CPURLConnection.j @@ -0,0 +1,181 @@ +/* + * CPURLConnection.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" +import "CPRunLoop.j" +import "CPURLRequest.j" +import "CPURLResponse.j" + + +var XMLHTTPRequestUninitialized = 0, + XMLHTTPRequestLoading = 1, + XMLHTTPRequestLoaded = 2, + XMLHTTPRequestInteractive = 3, + XMLHTTPRequestComplete = 4; + +var CPURLConnectionDelegate = nil; + +@implementation CPURLConnection : CPObject +{ + CPURLRequest _request; + id _delegate; + BOOL _isCanceled; + + XMLHTTPRequest _XMLHTTPRequest; +} + ++ (void)setClassDelegate:(id)delegate +{ + CPURLConnectionDelegate = delegate; +} + ++ (CPData)sendSynchronousRequest:(CPURLRequest)aRequest returningResponse:(CPURLResponse **)aURLResponse error:(CPError **)anError +{ + try + { + var request = objj_request_xmlhttp(); + + request.open([aRequest HTTPMethod], [aRequest URL], NO); + + var fields = [aRequest allHTTPHeaderFields], + key = nil, + keys = [fields keyEnumerator]; + + while (key = [keys nextObject]) + request.setRequestHeader(key, [fields objectForKey:key]); + + request.send([aRequest HTTPBody]); + + return [CPData dataWithString:request.responseText]; + } + catch (anException) + { + } + + return nil; +} + ++ (CPURLConnection)connectionWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate +{ + return [[self alloc] initWithRequest:aRequest delegate:aDelegate]; +} + +- (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate startImmediately:(BOOL)shouldStartImmediately +{ + self = [super init]; + + if (self) + { + _request = aRequest; + _delegate = aDelegate; + _isCanceled = NO; + + _XMLHTTPRequest = objj_request_xmlhttp(); + + if (shouldStartImmediately) + [self start]; + } + + return self; +} + +- (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate +{ + return [self initWithRequest:aRequest delegate:aDelegate startImmediately:YES]; +} + +- (id)delegate +{ + return _delegate; +} + +- (void)start +{ + _isCanceled = NO; + + try + { + _XMLHTTPRequest.open([_request HTTPMethod], [_request URL], YES); + + _XMLHTTPRequest.onreadystatechange = function() { [self _readyStateDidChange]; } + + var fields = [_request allHTTPHeaderFields], + key = nil, + keys = [fields keyEnumerator]; + + while (key = [keys nextObject]) + _XMLHTTPRequest.setRequestHeader(key, [fields objectForKey:key]); + + _XMLHTTPRequest.send([_request HTTPBody]); + } + catch (anException) + { + [_delegate connection:self didFailWithError:anException]; + } +} + +- (void)cancel +{ + _isCanceled = YES; + + try + { + _XMLHTTPRequest.abort(); + } + // We expect an exception in some browsers like FireFox. + catch (anException) + { + } +} + +- (void)_readyStateDidChange +{ + if (_XMLHTTPRequest.readyState == XMLHTTPRequestComplete) + { + var statusCode = _XMLHTTPRequest.status; + + if ([_delegate respondsToSelector:@selector(connection:didReceiveResponse:)]) + [_delegate connection:self didReceiveResponse:[[CPHTTPURLResponse alloc] _initWithStatusCode:statusCode]]; + + if (!_isCanceled) + { + if (statusCode == 200) + { + [_delegate connection:self didReceiveData:_XMLHTTPRequest.responseText]; + [_delegate connectionDidFinishLoading:self]; + } + else if (statusCode == 401 && [CPURLConnectionDelegate respondsToSelector:@selector(connectionDidReceiveAuthenticationChallenge:)]) + [CPURLConnectionDelegate connectionDidReceiveAuthenticationChallenge:self]; + else + [_delegate connection:self didFailWithError:_XMLHTTPRequest.status] + } + } + + [[CPRunLoop currentRunLoop] performSelectors]; +} + +- (void)_XMLHTTPRequest +{ + return _XMLHTTPRequest; +} + +@end diff --git a/Foundation/CPURLRequest.j b/Foundation/CPURLRequest.j new file mode 100644 index 0000000000..7a8e61a2a4 --- /dev/null +++ b/Foundation/CPURLRequest.j @@ -0,0 +1,103 @@ +/* + * CPURLRequest.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" + +@implementation CPURLRequest : CPObject +{ + CPURL _URL; + + // FIXME: this should be CPData + CPString _HTTPBody; + CPString _HTTPMethod; + CPDictionary _HTTPHeaderFields; +} + ++ (id)requestWithURL:(CPURL)aURL +{ + return [[CPURLRequest alloc] initWithURL:aURL]; +} + +- (id)initWithURL:(CPURL)aURL +{ + self = [super init]; + + if (self) + { + _URL = aURL; + _HTTPBody = @""; + _HTTPMethod = @"GET"; + _HTTPHeaderFields = [CPDictionary dictionary]; + + [self setValue:"Thu, 1 Jan 1970 00:00:00 GMT" forHTTPHeaderField:"If-Modified-Since"]; + [self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"]; + } + + return self; +} + +- (CPURL)URL +{ + return _URL; +} + +- (void)setURL:(CPURL)aURL +{ + _URL = aURL; +} + +- (void)setHTTPBody:(CPString)anHTTPBody +{ + _HTTPBody = anHTTPBody; +} + +- (CPString)HTTPBody +{ + return _HTTPBody; +} + +- (void)setHTTPMethod:(CPString)anHTTPMethod +{ + _HTTPMethod = anHTTPMethod; +} + +- (CPString)HTTPMethod +{ + return _HTTPMethod; +} + +- (CPDictionary)allHTTPHeaderFields +{ + return _HTTPHeaderFields; +} + +- (CPString)valueForHTTPHeaderField:(CPString)aField +{ + return [_HTTPHeaderFields objectForKey:aField]; +} + +- (void)setValue:(CPString)aValue forHTTPHeaderField:(CPString)aField +{ + [_HTTPHeaderFields setObject:aValue forKey:aField]; +} + +@end diff --git a/Foundation/CPURLResponse.j b/Foundation/CPURLResponse.j new file mode 100644 index 0000000000..4362c2d083 --- /dev/null +++ b/Foundation/CPURLResponse.j @@ -0,0 +1,67 @@ +/* + * CPURLResponse.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" + + +/* + CPURL _URL; + CPString _MIMEType; + unsigned _expectedContentLength; + CPString _textEncodingName; +*/ +@implementation CPURLResponse : CPObject +{ +} +/* +Creating a Response +Ð initWithURL:MIMEType:expectedContentLength:textEncodingName: +Getting the Response Properties +Ð expectedContentLength +Ð suggestedFilename +Ð MIMEType +Ð textEncodingName +Ð URL +*/ +@end + +@implementation CPHTTPURLResponse : CPURLResponse +{ + int _statusCode; +} + +- (id)_initWithStatusCode:(int)aStatusCode +{ + self = [super init]; + + if (self) + _statusCode = aStatusCode; + + return self; +} + +- (int)statusCode +{ + return _statusCode; +} + +@end diff --git a/Foundation/CPUndoManager.j b/Foundation/CPUndoManager.j new file mode 100644 index 0000000000..e25703b720 --- /dev/null +++ b/Foundation/CPUndoManager.j @@ -0,0 +1,599 @@ +/* + * CPUndoManager.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" +import "CPInvocation.j" + +var CPUndoManagerNormal = 0, + CPUndoManagerUndoing = 1, + CPUndoManagerRedoing = 2; + +CPUndoManagerCheckpointNotification = @"CPUndoManagerCheckpointNotification"; +CPUndoManagerDidOpenUndoGroupNotification = @"CPUndoManagerDidOpenUndoGroupNotification"; +CPUndoManagerDidRedoChangeNotification = @"CPUndoManagerDidRedoChangeNotification"; +CPUndoManagerDidUndoChangeNotification = @"CPUndoManagerDidUndoChangeNotification"; +CPUndoManagerWillCloseUndoGroupNotification = @"CPUndoManagerWillCloseUndoGroupNotification"; +CPUndoManagerWillRedoChangeNotification = @"CPUndoManagerWillRedoChangeNotification"; +CPUndoManagerWillUndoChangeNotification = @"CPUndoManagerWillUndoChangeNotification"; + +CPUndoCloseGroupingRunLoopOrdering = 350000; + +var _CPUndoGroupingPool = [], + _CPUndoGroupingPoolCapacity = 5; + +@implementation _CPUndoGrouping : CPObject +{ + _CPUndoGrouping _parent; + CPMutableArray _invocations; +} + ++ (void)_poolUndoGrouping:(_CPUndoGrouping)anUndoGrouping +{ + if (!anUndoGrouping || _CPUndoGroupingPool.length >= _CPUndoGroupingPoolCapacity) + return; + + _CPUndoGroupingPool.push(anUndoGrouping); +} + ++ (id)undoGroupingWithParent:(_CPUndoGrouping)anUndoGrouping +{ + if (_CPUndoGroupingPool.length) + { + var grouping = _CPUndoGroupingPool.pop(); + + grouping._parent = anUndoGrouping; + + if (grouping._invocations.length) + grouping._invocations = []; + + return grouping; + } + + return [[self alloc] initWithParent:anUndoGrouping]; +} + +- (id)initWithParent:(_CPUndoGrouping)anUndoGrouping +{ + self = [super init]; + + if (self) + { + _parent = anUndoGrouping; + _invocations = []; + } + + return self; +} + +- (_CPUndoGrouping)parent +{ + return _parent; +} + +- (void)addInvocation:(CPInvocation)anInvocation +{ + _invocations.push(anInvocation); +} + +- (void)addInvocationsFromArray:(CPArray)invocations +{ + [_invocations addObjectsFromArray:invocations]; +} + +- (BOOL)removeInvocationsWithTarget:(id)aTarget +{ + var index = _invocations.length; + + while (index--) + if ([_invocations[index] target] == aTarget) + _invocations.splice(index, 1); +} + +- (CPArray)invocations +{ + return _invocations; +} + +- (void)invoke +{ + var index = _invocations.length; + + while (index--) + [_invocations[index] invoke]; +} + +@end + +var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey", + _CPUndoGroupingInvocationsKey = @"_CPUndoGroupingInvocationsKey"; + +@implementation _CPUndoGrouping (CPCoder) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + { + _parent = [aCoder decodeObjectForKey:_CPUndoGroupingParentKey]; + _invocations = [aCoder decodeObjectForKey:_CPUndoGroupingInvocationsKey]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_parent forKey:_CPUndoGroupingParentKey]; + [aCoder encodeObject:_invocations forKey:_CPUndoGroupingInvocationsKey]; +} + +@end + +@implementation CPUndoManager : CPObject +{ + CPMutableArray _redoStack; + CPMutableArray _undoStack; + + BOOL _groupsByEvent; + int _disableCount; + int _levelsOfUndo; + id _currentGrouping; + int _state; + CPString _actionName; + id _preparedTarget; + + CPArray _runLoopModes; + BOOL _registeredWithRunLoop; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + _redoStack = []; + _undoStack = []; + + _state = CPUndoManagerNormal; + + [self setRunLoopModes:[CPDefaultRunLoopMode]]; + [self setGroupsByEvent:YES]; + _performRegistered = NO; + } + + return self; +} + +// Registering Undo Operations + +- (void)registerUndoWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anObject +{ + if (_disableCount > 0) + return; + +/* if (_currentGroup == nil) + [NSException raise:NSInternalInconsistencyException + format:@"forwardInvocation called without first opening an undo group"]; +*/ + //signature = [target methodSignatureForSelector:selector]; + // FIXME: we need method signatures. + var invocation = [CPInvocation invocationWithMethodSignature:nil]; + + [invocation setTarget:aTarget]; + [invocation setSelector:aSelector]; + [invocation setArgument:anObject atIndex:2]; + + [_currentGrouping addInvocation:invocation]; + + if (_state == CPUndoManagerNormal) + [_redoStack removeAllObjects]; +} + +- (id)prepareWithInvocationTarget:(id)aTarget +{ + _preparedTarget = aTarget; + + return self; +} + +-(CPMethodSignature)methodSignatureForSelector:(SEL)aSelector +{ + if ([_preparedTarget respondsToSelector:aSelector]) + return 1; + + return nil;//[_preparedTarget methodSignatureForSelector:selector]; +} + +- (void)forwardInvocation:(CPInvocation)anInvocation +{ + if (_disableCount > 0) + return; + +/* if (_preparedTarget == nil) + [NSException raise:NSInternalInconsistencyException + format:@"forwardInvocation called without first preparing a target"]; + if (_currentGroup == nil) + [NSException raise:NSInternalInconsistencyException + format:@"forwardInvocation called without first opening an undo group"]; +*/ + [anInvocation setTarget:_preparedTarget]; + [_currentGrouping addInvocation:anInvocation]; + + if (_state == CPUndoManagerNormal) + [_redoStack removeAllObjects]; + + _preparedTarget = nil; +} + +// Checking Undo Ability + +- (BOOL)canRedo +{ + return _redoStack.length > 0; +} + +- (BOOL)canUndo +{ + if (_undoStack.length > 0) + return YES; + + return [_currentGrouping actions].length > 0; +} + +// Preform Undo and Redo + +- (void)undo +{ + if ([self groupingLevel] == 1) + [self endUndoGrouping]; + + [self undoNestedGroup]; +} + +- (void)undoNestedGroup +{ + if (_undoStack.length == 0) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; +/* [[NSNotificationCenter defaultCenter] postNotificationName:NSUndoManagerCheckpointNotification + object:self]; +*/ + [defaultCenter postNotificationName:CPUndoManagerWillUndoChangeNotification object:self]; + + var undoGrouping = _undoStack.pop(); + + _state = CPUndoManagerUndoing; + + [self beginUndoGrouping]; + [undoGrouping invoke]; + [self endUndoGrouping]; + + [_CPUndoGrouping _poolUndoGrouping:undoGrouping]; + + _state = CPUndoManagerNormal; + + [defaultCenter postNotificationName:CPUndoManagerDidUndoChangeNotification object:self]; +} + +- (void)redo +{ + // Don't do anything if we have no redos. + if (_redoStack.length == 0) + return; + +/* if (_state == NSUndoManagerUndoing) + [NSException raise:NSInternalInconsistencyException + format:@"redo called while undoing"]; + + [[NSNotificationCenter defaultCenter] postNotificationName:NSUndoManagerCheckpointNotification + object:self]; +*/ + var defaultCenter = [CPNotificationCenter defaultCenter]; + + [defaultCenter postNotificationName:CPUndoManagerWillRedoChangeNotification object:self]; + + var oldUndoGrouping = _currentGrouping, + undoGrouping = _redoStack.pop(); + + _currentGrouping = nil; + _state = CPUndoManagerRedoing; + + [self beginUndoGrouping]; + [undoGrouping invoke]; + [self endUndoGrouping]; + + [_CPUndoGrouping _poolUndoGrouping:undoGrouping]; + + _currentGrouping = oldUndoGrouping; + _state = CPUndoManagerNormal; + + [defaultCenter postNotificationName:CPUndoManagerDidRedoChangeNotification object:self]; +} + +// Creating Undo Groups + +- (void)beginUndoGrouping +{ + _currentGrouping = [_CPUndoGrouping undoGroupingWithParent:_currentGrouping]; +} + +- (void)endUndoGrouping +{ + if (!_currentGrouping) + alert("FIXME: this should be an exception endUndoGrouping - currentUndoGrouping = nil."); + + var parent = [_currentGrouping parent]; + + if (!parent && [_currentGrouping invocations].length > 0) + { + [[CPNotificationCenter defaultCenter] + postNotificationName:CPUndoManagerWillCloseUndoGroupNotification + object:self]; + + // Put this group on the redo stack if we are currently undoing, otherwise + // put it on the undo stack. That way, "undos" become "redos". + var stack = _state == CPUndoManagerUndoing ? _redoStack : _undoStack; + + stack.push(_currentGrouping); + + if (_levelsOfUndo > 0 && stack.length > _levelsOfUndo) + stack.splice(0, 1); + } + + // Nested Undo Grouping + else + { + [parent addInvocationsFromArray:[_currentGrouping invocations]]; + + [_CPUndoGrouping _poolUndoGrouping:_currentGrouping]; + } + + _currentGrouping = parent; +} + +- (void)enableUndoRegistration +{ + if (_disableCount <= 0) + return; + + _disableCount--; +} + +- (BOOL)groupsByEvent +{ + return _groupsByEvent; +} + +- (void)setGroupsByEvent:(BOOL)aFlag +{ + if (_groupsByEvent == aFlag) + return; + + _groupsByEvent = aFlag; + + if (_groupsByEvent) + { + [self _registerWithRunLoop]; + + // There is a chance that the event loop selector won't fire before our first register, + // so kick it off here. + if (!_currentGrouping) + [self beginUndoGrouping]; + } + else + [self _unregisterWithRunLoop]; +} + +- (unsigned)groupingLevel +{ + var grouping = _currentGrouping, + level = _currentGrouping != nil; + + while (grouping = [grouping parent]) + ++level; + + return level; +} + +// Disabling Undo + +- (void)disableUndoRegistration +{ + ++_disableCount; +} + +- (BOOL)isUndoRegistrationEnabled +{ + return _disableCount == 0; +} + +// Checking Whether Undo or Redo Is Being Performed + +- (BOOL)isUndoing +{ + return _state == CPUndoManagerUndoing; +} + +- (BOOL)isRedoing +{ + return _state == CPUndoManagerRedoing; +} + +// Clearing Undo Operations + +- (void)removeAllActions +{ + _redoStack = []; + _undoStack = []; + _disableCount = 0; +} + +- (void)removeAllActionsWithTarget:(id)aTarget +{ + [_currentGrouping removeInvocationsWithTarget:aTarget]; + + var index = _redoStack.length; + + while (index--) + { + var grouping = _redoStack[index]; + + [grouping removeInvocationsWithTarget:aTarget]; + + if (![grouping invocations].length) + _redoStack.splice(index, 1); + } + + index = _undoStack.length; + + while (index--) + { + var grouping = _undoStack[index]; + + [grouping removeInvocationsWithTarget:aTarget]; + + if (![grouping invocations].length) + _undoStack.splice(index, 1); + } +} + +// Managing the Action Name + +- (void)setActionName:(CPString)anActionName +{ + _actionName = anActionName; +} + +- (CPString)redoActionName +{ + return [self canRedo] ? _actionName : nil; +} + +- (CPString)undoActionName +{ + return [self canUndo] ? _actionName : nil; +} + +// Working With Run Loops + +- (CPArray)runLoopModes +{ + return _runLoopModes; +} + +- (void)setRunLoopModes:(CPArray)modes +{ + _runLoopModes = modes; + + [self _unregisterWithRunLoop]; + + if (_groupsByEvent) + [self _registerWithRunLoop]; +} + +- (void)beginUndoGroupingForEvent +{ + if (!_groupsByEvent) + return; + + if (_currentGrouping != nil) + [self endUndoGrouping]; + + [self beginUndoGrouping]; + + [[CPRunLoop currentRunLoop] performSelector:@selector(beginUndoGroupingForEvent) + target:self argument:nil order:CPUndoCloseGroupingRunLoopOrdering modes:_runLoopModes]; +} + +- (void)_registerWithRunLoop +{ + if (_registeredWithRunLoop) + return; + + _registeredWithRunLoop = YES; + [[CPRunLoop currentRunLoop] performSelector:@selector(beginUndoGroupingForEvent) + target:self argument:nil order:CPUndoCloseGroupingRunLoopOrdering modes:_runLoopModes]; +} + +- (void)_unregisterWithRunLoop +{ + if (!_registeredWithRunLoop) + return; + + _registeredWithRunLoop = NO; + [[CPRunLoop currentRunLoop] cancelPerformSelector:@selector(beginUndoGroupingForEvent) target:self argument:nil]; +} + +@end + +var CPUndoManagerRedoStackKey = @"CPUndoManagerRedoStackKey", + CPUndoManagerUndoStackKey = @"CPUndoManagerUndoStackKey"; + + CPUndoManagerLevelsOfUndoKey = @"CPUndoManagerLevelsOfUndoKey"; + CPUndoManagerActionNameKey = @"CPUndoManagerActionNameKey"; + CPUndoManagerCurrentGroupingKey = @"CPUndoManagerCurrentGroupingKey"; + + CPUndoManagerRunLoopModesKey = @"CPUndoManagerRunLoopModesKey"; + CPUndoManagerGroupsByEventKey = @"CPUndoManagerGroupsByEventKey"; + +@implementation CPUndoManager (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + { + _redoStack = [aCoder decodeObjectForKey:CPUndoManagerRedoStackKey]; + _undoStack = [aCoder decodeObjectForKey:CPUndoManagerUndoStackKey]; + + _levelsOfUndo = [aCoder decodeObjectForKey:CPUndoManagerLevelsOfUndoKey]; + _actionName = [aCoder decodeObjectForKey:CPUndoManagerActionNameKey]; + _currentGrouping = [aCoder decodeObjectForKey:CPUndoManagerCurrentGroupingKey]; + + _state = CPUndoManagerNormal; + + [self setRunLoopModes:[aCoder decodeObjectForKey:CPUndoManagerRunLoopModesKey]]; + [self setGroupsByEvent:[aCoder decodeBoolForKey:CPUndoManagerGroupsByEventKey]]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_redoStack forKey:CPUndoManagerRedoStackKey]; + [aCoder encodeObject:_undoStack forKey:CPUndoManagerUndoStackKey]; + + [aCoder encodeInt:_levelsOfUndo forKey:CPUndoManagerLevelsOfUndoKey]; + [aCoder encodeObject:_actionName forKey:CPUndoManagerActionNameKey]; + + [aCoder encodeObject:_currentGrouping forKey:CPUndoManagerCurrentGroupingKey]; + + [aCoder encodeObject:_runLoopModes forKey:CPUndoManagerRunLoopModesKey]; + [aCoder encodeBool:_groupsByEvent forKey:CPUndoManagerGroupsByEventKey]; +} + +@end diff --git a/Foundation/CPUserSessionManager.j b/Foundation/CPUserSessionManager.j new file mode 100644 index 0000000000..97fd323627 --- /dev/null +++ b/Foundation/CPUserSessionManager.j @@ -0,0 +1,98 @@ +/* + * CPUserSessionManager.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 +import + + +CPUserSessionUndeterminedStatus = 0; +CPUserSessionLoggedInStatus = 1; +CPUserSessionLoggedOutStatus = 2; + +CPUserSessionManagerStatusDidChangeNotification = @"CPUserSessionManagerStatusDidChangeNotification"; +CPUserSessionManagerUserIdentifierDidChangeNotification = @"CPUserSessionManagerUserIdentifierDidChangeNotification"; + +var CPDefaultUserSessionManager = nil; + +@implementation CPUserSessionManager : CPObject +{ + CPUserSessionStatus _status; + + CPString _userIdentifier; +} + ++ (id)defaultManager +{ + if (!CPDefaultUserSessionManager) + CPDefaultUserSessionManager = [[CPUserSessionManager alloc] init]; + + return CPDefaultUserSessionManager; +} + +- (id)init +{ + self = [super init]; + + if (self) + _status = CPUserSessionUndeterminedStatus; + + return self; +} + +- (CPUserSessionStatus)status +{ + return _status; +} + +- (void)setStatus:(CPUserSessionStatus)aStatus +{ + if (_status == aStatus) + return; + + _status = aStatus; + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPUserSessionManagerStatusDidChangeNotification + object:self]; + + if (_status != CPUserSessionLoggedInStatus) + [self setUserIdentifier:nil]; +} + +- (CPString)userIdentifier +{ + return _userIdentifier; +} + +- (void)setUserIdentifier:(CPString)anIdentifier +{ + if (_userIdentifier == anIdentifier) + return; + + _userIdentifier = anIdentifier; + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPUserSessionManagerUserIdentifierDidChangeNotification + object:self]; +} + +@end diff --git a/Foundation/CPValue.j b/Foundation/CPValue.j new file mode 100644 index 0000000000..7bfd6c5a7a --- /dev/null +++ b/Foundation/CPValue.j @@ -0,0 +1,196 @@ +/* + * CPValue.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPObject.j" +import "CPCoder.j" + +@implementation CPValue : CPObject +{ + JSObject _JSObject; +} + ++ (id)valueWithJSObject:(JSObject)aJSObject +{ + return [[self alloc] initWithJSObject:aJSObject]; +} + +- (id)initWithJSObject:(JSObject)aJSObject +{ + self = [super init]; + + if (self) + _JSObject = aJSObject; + + return self; +} + +- (JSObject)JSObject +{ + return _JSObject; +} + +@end + +var CPValueValueKey = @"CPValueValueKey"; + +@implementation CPValue (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + _JSObject = CPJSObjectCreateWithJSON([aCoder decodeObjectForKey:CPValueValueKey]); + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:CPJSObjectCreateJSON(_JSObject) forKey:CPValueValueKey]; +} + +@end + +var _JSONCharacterEncodings = {}; + +_JSONCharacterEncodings['\b'] = "\\b"; +_JSONCharacterEncodings['\t'] = "\\t"; +_JSONCharacterEncodings['\n'] = "\\n"; +_JSONCharacterEncodings['\f'] = "\\f"; +_JSONCharacterEncodings['\r'] = "\\r"; +_JSONCharacterEncodings['"'] = "\\\""; +_JSONCharacterEncodings['\\'] = "\\\\"; + +// FIXME: Workaround for https://trac.280north.com/ticket/16 +var _JSONEncodedCharacters = new RegExp("[\\\"\\\\\\x00-\\x1f\\x7f-\\x9f]", 'g'); + +function CPJSObjectCreateJSON(aJSObject) +{ + // typeof new Number() and new String() gives you "object", + // so valueof in those cases. + var type = typeof aJSObject, + valueOf = aJSObject.valueOf(), + typeValueOf = typeof valueOf; + + if (type != typeValueOf) + { + type = typeValueOf; + aJSObject = valueOf; + } + + switch (type) + { + case "string": // If the string contains no control characters, no quote characters, and no + // backslash characters, then we can safely slap some quotes around it. + // Otherwise we must also replace the offending characters with safe sequences. + + if (!_JSONEncodedCharacters.test(aJSObject)) + return '"' + aJSObject + '"'; + + return '"' + aJSObject.replace(_JSONEncodedCharacters, _CPJSObjectEncodeCharacter) + '"'; + + + case "number": // JSON numbers must be finite. Encode non-finite numbers as null. + return isFinite(aJSObject) ? String(aJSObject) : "null"; + + case "boolean": + case "null": return String(aJSObject); + + case "object": // Due to a specification blunder in ECMAScript, + // typeof null is 'object', so watch out for that case. + + if (!aJSObject) + return "null"; + + // If the object has a toJSON method, call it, and stringify the result. + + if (typeof aJSObject.toJSON === "function") + return CPJSObjectCreateJSON(aJSObject.toJSON()); + + var array = []; + + // If the object is an array. Stringify every element. Use null as a placeholder + // for non-JSON values. + if (aJSObject.slice) + { + var index = 0, + count = aJSObject.length; + + for (; index < count; ++index) + array.push(CPJSObjectCreateJSON(aJSObject[index]) || "null"); + + // Join all of the elements together and wrap them in brackets. + return '[' + array.join(',') + ']'; + } + + + // Otherwise, iterate through all of the keys in the object. + var key = NULL; + + for (key in aJSObject) + { + if (!(typeof key === "string")) + continue; + + var value = CPJSObjectCreateJSON(aJSObject[key]); + + if (value) + array.push(CPJSObjectCreateJSON(key) + ':' + value); + } + + // Join all of the member texts together and wrap them in braces. + return '{' + array.join(',') + '}'; + } +} + +var _CPJSObjectEncodeCharacter = function(aCharacter) +{ + var encoding = _JSONCharacterEncodings[aCharacter]; + + if (encoding) + return encoding; + + encoding = aCharacter.charCodeAt(0); + + return '\\u00' + FLOOR(encoding / 16).toString(16) + (encoding % 16).toString(16); +} + +var _JSONBackslashCharacters = new RegExp("\\\\.", 'g'), + _JSONSimpleValueTokens = new RegExp("\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?", 'g'), + _JSONValidOpenBrackets = new RegExp("(?:^|:|,)(?:\\s*\\[)+", 'g'), + _JSONValidExpression = new RegExp("^[\\],:{}\\s]*$"); + +function CPJSObjectCreateWithJSON(aString) +{ + if (_JSONValidExpression.test(aString.replace(_JSONBackslashCharacters, '@').replace(_JSONSimpleValueTokens, ']').replace(_JSONValidOpenBrackets, ''))) + return eval('(' + aString + ')'); + + return nil; +} + +/* +var _JSONBackslashCharacters = /\\./g, + _JSONSimpleValueTokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + _JSONValidOpenBrackets = /(?:^|:|,)(?:\s*\[)+/g, + _JSONValidExpression = /^[\],:{}\s]*$/; +*/ diff --git a/Foundation/Foundation.j b/Foundation/Foundation.j new file mode 100755 index 0000000000..d1e8d97c78 --- /dev/null +++ b/Foundation/Foundation.j @@ -0,0 +1,53 @@ +/* + * Foundation.j + * Foundation + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 "CPArray.j" +import "CPBundle.j" +import "CPCoder.j" +import "CPData.j" +import "CPDictionary.j" +import "CPEnumerator.j" +import "CPException.j" +import "CPIndexSet.j" +import "CPInvocation.j" +import "CPJSONPConnection.j" +import "CPKeyedArchiver.j" +import "CPKeyedUnarchiver.j" +import "CPKeyValueCoding.j" +import "CPLog.j" +import "CPNotification.j" +import "CPNotificationCenter.j" +import "CPNull.j" +import "CPNumber.j" +import "CPObject.j" +import "CPObjJRuntime.j" +import "CPPropertyListSerialization.j" +import "CPRange.j" +import "CPRunLoop.j" +import "CPSortDescriptor.j" +import "CPString.j" +import "CPUndoManager.j" +import "CPURLConnection.j" +import "CPURLRequest.j" +import "CPURLResponse.j" +import "CPUserSessionManager.j" +import "CPValue.j" diff --git a/Foundation/Foundation.steam b/Foundation/Foundation.steam new file mode 100644 index 0000000000..05f157698e --- /dev/null +++ b/Foundation/Foundation.steam @@ -0,0 +1,40 @@ + + + + + name + Foundation + Targets + + + name + Foundation + + + Configurations + + + name + Debug + Settings + + PREPROCESS + + FLAGS + -DDEBUG + + + + name + Release + Settings + + PREPROCESS + + PREINTERPRET + + + + + + diff --git a/Foundation/Info.plist b/Foundation/Info.plist new file mode 100644 index 0000000000..c16e019edc --- /dev/null +++ b/Foundation/Info.plist @@ -0,0 +1,14 @@ + + + + + CPBundleIdentifier + com.280n.Foundation + CPBundleInfoDictionaryVersion + 6.0 + CPBundleName + Foundation + CPBundlePackageType + FMWK + + diff --git a/Foundation/Resources/log.css b/Foundation/Resources/log.css new file mode 100644 index 0000000000..8a4ed37bc8 --- /dev/null +++ b/Foundation/Resources/log.css @@ -0,0 +1,72 @@ +body { + font: 10px Monaco, Courier, "Courier New", monospace, mono; + padding-top: 15px; +} + +div > .fatal, div > .error, div > .warn, div > .info, div > .debug, div > .trace { + display: none; + overflow: hidden; + white-space: pre; + padding: 0px 5px 0px 5px; + margin-top: 2px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +div[wrap="yes"] > div { + white-space: normal; +} + +.fatal { background-color: #ffb2b3; } +.error { background-color: #ffe2b2; } +.warn { background-color: #fdffb2; } +.info { background-color: #e4ffb2; } +.debug { background-color: #a0e5a0; } +.trace { background-color: #99b9ff; } + +.enfatal .fatal, .enerror .error, .enwarn .warn, .eninfo .info, .endebug .debug, .entrace .trace { display: block; } + +div#header { + background-color: rgba(240,240,240,0.82); + position: fixed; + top: 0px; + left: 0px; + width: 100%; + border-bottom: 1px solid rgba(0,0,0,0.33); + text-align: center; + /*opacity: 0.2;*/ +} +/* +div#header:hover { + opacity: 1.0; +} +*/ +ul#enablers { + display: inline-block; + margin: 1px 15px 0 15px; + padding: 2px 0 2px 0; +} + +ul#enablers li { + display: inline; + padding: 0px 5px 0px 5px; + margin-left: 4px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +[enabled="no"] { + opacity: 0.25; +} + +ul#options { + display: inline-block; + margin: 0 15px 0px 15px; + padding: 0 0px; +} + +ul#options li { + margin: 0 0 0 0; + padding: 0 0 0 0; + display: inline; +} \ No newline at end of file diff --git a/Foundation/build.xml b/Foundation/build.xml new file mode 100644 index 0000000000..d72125d166 --- /dev/null +++ b/Foundation/build.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..9ef3d701d1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,503 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/Objective-J/Object.j b/Objective-J/Object.j new file mode 100644 index 0000000000..0e96b94b2b --- /dev/null +++ b/Objective-J/Object.j @@ -0,0 +1,41 @@ +/* + * Object.j + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +@implementation Object +{ + Class isa; +} + ++ (void)initialize +{ +} + +- (void)doesNotRecognizeSelector:(SEL)aSelector +{ +} + +- (id)forwardSelector:(SEL)selector arguments:(void *)args +{ + return nil; +} + +@end diff --git a/Objective-J/build.xml b/Objective-J/build.xml new file mode 100644 index 0000000000..b4ec114921 --- /dev/null +++ b/Objective-J/build.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Objective-J/constants.js b/Objective-J/constants.js new file mode 100644 index 0000000000..c0b97bd5f4 --- /dev/null +++ b/Objective-J/constants.js @@ -0,0 +1,101 @@ +/* + * constants.js + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + + // Objective-J Constants +var NO = false, + YES = true, + + nil = null, + Nil = null, + NULL = null, + + // In IE, it is much faster to call these global methods than their Math. equivalents. + // We var them in order to make them DontDelete globals, which are known to be much faster in Safari. + ABS = Math.abs, + + ASIN = Math.asin, + ACOS = Math.acos, + ATAN = Math.atan, + ATAN2 = Math.atan2, + + SIN = Math.sin, + COS = Math.cos, + TAN = Math.tan, + + EXP = Math.exp, + POW = Math.pow, + + CEIL = Math.ceil, + FLOOR = Math.floor, + ROUND = Math.round, + + MIN = Math.min, + MAX = Math.max, + + RAND = Math.random, + SQRT = Math.sqrt, + + E = Math.E, + + LN2 = Math.LN2, + LN10 = Math.LN10, + LOG2E = Math.LOG2E, + LOG10E = Math.LOG10E, + + PI = Math.PI, + PI2 = Math.PI * 2.0, + PI_2 = Math.PI / 2.0, + + SQRT1_2 = Math.SQRT1_2, + SQRT2 = Math.SQRT2; + +// Detecting Browser Features +#define ACTIVE_X window.ActiveXObject +#define OPERA window.opera +#define NATIVE_XMLHTTPREQUEST window.XMLHttpRequest + +#define IF(FEATURE) if (FEATURE) { +#define ELSE } else { +#define ELIF(FEATURE) } else if (FEATURE) { +#define ENDIF } + +// +// FIXME: Should we just override normal alerts? +var objj_continue_alerting = NO; + +function objj_alert(aString) +{ + if (!objj_continue_alerting) + return; + + objj_continue_alerting = confirm(aString + "\n\nClick cancel to prevent further alerts."); +} + +function objj_fprintf(stream, string) +{ + stream(string); +} + +function objj_printf(string) +{ + objj_fprintf(alert, string); +} diff --git a/Objective-J/dictionary.js b/Objective-J/dictionary.js new file mode 100644 index 0000000000..80fc82f2f0 --- /dev/null +++ b/Objective-J/dictionary.js @@ -0,0 +1,119 @@ +/* + * dictionary.js + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +function objj_dictionary() +{ + this.keys = []; + this.count = 0; + this.buckets = {}; + this.__address = _objj_generateObjectHash(); +} + +function dictionary_containsKey(aDictionary, aKey) +{ + return aDictionary.buckets[aKey] != NULL; +} + +#define dictionary_containsKey(aDictionary, aKey) ((aDictionary).buckets[aKey] != NULL) + +function dictionary_getCount(aDictionary) +{ + return aDictionary.count; +} + +#define dictionary_getCount(aDictionary) ((aDictionary).count) + +function dictionary_getValue(aDictionary, aKey) +{ + return aDictionary.buckets[aKey]; +} + +#define dictionary_getValue(aDictionary, aKey) ((aDictionary).buckets[aKey]) + +function dictionary_setValue(aDictionary, aKey, aValue) +{ + if (aDictionary.buckets[aKey] == NULL) + { + aDictionary.keys.push(aKey); + ++aDictionary.count; + } + + if ((aDictionary.buckets[aKey] = aValue) == NULL) + --aDictionary.count; +} + +#define dictionary_setValue(aDictionary, aKey, aValue)\ +{\ + if ((aDictionary).buckets[aKey] == NULL)\ + {\ + (aDictionary).keys.push(aKey);\ + ++(aDictionary).count;\ + }\ +\ + if (((aDictionary).buckets[aKey] = aValue) == NULL) \ + --(aDictionary).count;\ +} + + +function dictionary_removeValue(aDictionary, aKey) +{ + if (aDictionary.buckets[aKey] == NULL) + return; + + --aDictionary.count; + if (aDictionary.keys.indexOf) + aDictionary.keys.splice(aDictionary.keys.indexOf(aKey), 1); + else + { + var keys = aDictionary.keys, + index = 0, + count = keys.length; + + for (; index < count; ++index) + if (keys[index] == aKey) + { + keys.splice(index, 1); + break; + } + } + + delete aDictionary.buckets[aKey]; +} + +function dictionary_replaceValue(aDictionary, aKey, aValue) +{ + if (aDictionary[aKey] == NULL) + return; + +// FIXME: Implement +// aDictionary.keys.splice(aDictionary.keys.indexOf(aKey), 1); +} + +function dictionary_description(aDictionary) +{ + str = "{ "; + for ( x in aDictionary.buckets) + str += x + ":" + aDictionary.buckets[x] + ","; + str += " }"; + + return str; +} diff --git a/Objective-J/evaluate.js b/Objective-J/evaluate.js new file mode 100644 index 0000000000..c325dc9c12 --- /dev/null +++ b/Objective-J/evaluate.js @@ -0,0 +1,239 @@ +/* + * evaluate.js + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +var objj_included_files = { }; + +var FRAGMENT_CODE = 1, + + FRAGMENT_FILE = 1 << 2, + FRAGMENT_LOCAL = 1 << 3, + FRAGMENT_IMPORT = 1 << 4; + +function objj_fragment() +{ + this.info = NULL; + this.type = 0; + this.context = NULL; + this.bundle = NULL; + this.file = NULL; +} + +#define EVALUATION_PERIOD 3000 + +function objj_context() +{ + this.fragments = []; + this.scheduled = NO; + this.blocked = NO; +} + +#define SET_CONTEXT(aFragment, aContext) aFragment.context = aContext +#define GET_CONTEXT(aFragment) aFragment.context + +#define SET_TYPE(aFragment, aType) aFragment.type = (aType) +#define GET_TYPE(aFragment) aFragment.type + +#define GET_CODE(aFragment) aFragment.info +#define SET_CODE(aFragment, aCode) aFragment.info = (aCode) + +#define GET_PATH(aFragment) aFragment.info +#define SET_PATH(aFragment, aPath) aFragment.info = aPath + +#define GET_BUNDLE(aFragment) aFragment.bundle +#define SET_BUNDLE(aFragment, aBundle) aFragment.bundle = aBundle + +#define GET_FILE(aFragment) aFragment.file +#define SET_FILE(aFragment, aFile) aFragment.file = aFile + +#define IS_FILE(aFragment) (aFragment.type & FRAGMENT_FILE) +#define IS_LOCAL(aFragment) (aFragment.type & FRAGMENT_LOCAL) +#define IS_IMPORT(aFragment) (aFragment.type & FRAGMENT_IMPORT) + +function fragment_create_code(aCode, aBundle, aFile) +{ + var fragment = new objj_fragment(); + + SET_TYPE(fragment, FRAGMENT_CODE); + SET_CODE(fragment, aCode); + SET_BUNDLE(fragment, aBundle); + SET_FILE(fragment, aFile); + + return fragment; +} + +function fragment_create_file(aPath, aBundle, isLocal, isImport, aFile) +{ + var fragment = new objj_fragment(); + + SET_TYPE(fragment, FRAGMENT_FILE | (FRAGMENT_LOCAL * isLocal) | (FRAGMENT_IMPORT * isImport)); + SET_PATH(fragment, aPath); + SET_BUNDLE(fragment, aBundle); + SET_FILE(fragment, aFile); + + return fragment; +} + +objj_context.prototype.evaluate = function() +{ + this.scheduled = NO; + + // If we're blocked by IO, then just reschedule. + if (this.blocked) + return this.schedule(); + + // If not, begin the evaluation loop. + var sleep = NO, + start = new Date(), + fragments = this.fragments; + + while (!sleep && fragments.length) + { + var fragment = fragments.pop(); + + if (IS_FILE(fragment)) + sleep = fragment_evaluate_file(fragment); + else + sleep = fragment_evaluate_code(fragment); + + // Sleep evaluation if we've been at it too long to avoid + // unresponsive script errors. + sleep = sleep || ((new Date() - start) > EVALUATION_PERIOD); + } + + if (sleep) + this.schedule(); + + else if (this.didCompleteCallback) + this.didCompleteCallback(this); +} + +objj_context.prototype.schedule = function() +{ + if (this.scheduled) + return; + + this.scheduled = YES; + + var context = this; + + window.setTimeout(function () { context.evaluate(); }, 0); +} + +objj_context.prototype.pushFragment = function(aFragment) +{ + SET_CONTEXT(aFragment, this); + + this.fragments.push(aFragment); +} + +function fragment_evaluate_code(aFragment) +{ + var compiled; + + OBJJ_CURRENT_BUNDLE = GET_BUNDLE(aFragment); + + try + { + compiled = new Function(GET_CODE(aFragment)); + } + catch(anException) + { + objj_exception_report(anException, GET_FILE(aFragment)); + } + + try + { + compiled(); + } + catch(anException) + { + objj_exception_report(anException, GET_FILE(aFragment)); + } + + return NO; +} + +function fragment_evaluate_file(aFragment) +{ + var context = GET_CONTEXT(aFragment), + requiresSleep = YES; + + // Make us IO Blocked until we receive the file. + context.blocked = YES; + + objj_request_file(GET_PATH(aFragment), IS_LOCAL(aFragment), function(aFile) + { + // Use the captured value from the closure. If objj_request_file + // returns immediately, then this will cause us to avoid breaking, + // but if it doesn't, then this will have no effect. + requiresSleep = NO; + + // We've received the file, so no longer block execution. + context.blocked = NO; + + // FIXME: We need to actually support this, and handle it better + // as well. + if (aFile == OBJJ_NO_FILE) + objj_alert("uh oh!"); + + // If this file has already been included, skip it and bail. + if (objj_included_files[aFile.path]) + return; + + // If not, then mark it as included, for future imports. + objj_included_files[aFile.path] = YES; + + // Grab the file's fragments if it has them, or preprocess it if not. + var fragments = aFile.fragments ? aFile.fragments : objj_preprocess(aFile.contents, aFile.bundle, aFile), + count = fragments.length, + directory = aFile.path.substr(0, aFile.path.lastIndexOf('/') + 1); + + // Put the individual fragments at the head of the evaluation + // stack to be evaluated. + while (count--) + { + var fragment = fragments[count]; + + if (IS_FILE(fragment)) + { + if (IS_LOCAL(fragment)) + SET_PATH(fragment, directory + GET_PATH(fragment)); + + objj_request_file(GET_PATH(fragment), IS_LOCAL(fragment), NULL); + } + + context.pushFragment(fragment); + } + }); + + return requiresSleep; +} + +function objj_import(aPath, isLocal, didCompleteCallback) +{ + var context = new objj_context(); + + context.didCompleteCallback = didCompleteCallback; + context.pushFragment(fragment_create_file(aPath, isLocal, YES)); + + context.evaluate(); +} diff --git a/Objective-J/exception.js b/Objective-J/exception.js new file mode 100644 index 0000000000..df317d4951 --- /dev/null +++ b/Objective-J/exception.js @@ -0,0 +1,66 @@ +/* + * exception.js + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +var OBJJ_EXCEPTION_OUTPUT_STREAM = NULL; + +function objj_exception(aName, aReason, aUserInfo) +{ + this.name = aName; + this.reason = aReason; + this.userInfo = aUserInfo; + this.__address = _objj_generateObjectHash(); +} + +objj_exception.prototype.toString = function() +{ + return this.reason; +} + +function objj_exception_throw(anException) +{ + throw anException; +} + +function objj_exception_report(anException, aSourceFile) +{ + objj_fprintf(OBJJ_EXCEPTION_OUTPUT_STREAM, aSourceFile.path + "\n" + anException); + + throw anException; +} + +function objj_exception_setOutputStream(aStream) +{ + OBJJ_EXCEPTION_OUTPUT_STREAM = aStream; +} + +#if COMPILER +importPackage(java.lang); + +objj_exception_setOutputStream(function (aString) { System.out.println(aString) } ); +#elif DEBUG +if (window.console && window.console.error) + objj_exception_setOutputStream(function (aString) { console.error(aString) } ); +else + objj_exception_setOutputStream(alert); +#else +objj_exception_setOutputStream(function(aString) { }); +#endif diff --git a/Objective-J/file.js b/Objective-J/file.js new file mode 100644 index 0000000000..d9b268c03c --- /dev/null +++ b/Objective-J/file.js @@ -0,0 +1,533 @@ +/* + * file.js + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +// Notes: requests may return immediately (i.e. in IE when a file has been cached). Don't assume +// One line of code thus follows another, it may call the callback first! + +// FIXME: If we ever have bundles that replace only some files, we will definitely run into problems. +// Look inside bundleResponseCallback. + + +#define DIRECTORY(aPath) (aPath).substr(0, (aPath).lastIndexOf('/') + 1) + +OBJJFileNotFoundException = "OBJJFileNotFoundException"; +OBJJExecutableNotFoundException = "OBJJExecutableNotFoundException"; + +var objj_files = { }, + objj_bundles = { }, + objj_bundlesForClass = { }, + objj_searches = { }; + +var OBJJ_NO_FILE = {}, + OBJJ_INCLUDE_PATHS = ["Frameworks", "SomethingElse"]; + +var OBJJ_BASE_URI = ""; + +IF (OPERA) + +var DOMBaseElement = document.getElementsByTagName("base")[0]; + +if (DOMBaseElement) + OBJJ_BASE_URI = DIRECTORY(DOMBaseElement.getAttribute('href')); + +ENDIF + +function objj_file() +{ + this.path = NULL; + this.bundle = NULL; + + this.included = NO; + + this.contents = NULL; + this.fragments = NULL; +} + +function objj_bundle() +{ + this.path = NULL; + this.info = NULL; + + this.__address = _objj_generateObjectHash(); +} + +function objj_getBundleWithPath(aPath) +{ + return objj_bundles[aPath]; +} + +function objj_bundleForClass(aClass) +{ + return objj_bundlesForClass[aClass.name]; +} + +function objj_addClassForBundle(aClass, aBundle) +{ + objj_bundlesForClass[aClass.name] = aBundle; +} + +function objj_request_file(aFilePath, shouldSearchLocally, aCallback) +{ + new objj_search(aFilePath, shouldSearchLocally, aCallback).attemptNextSearchPath(); +} + +var objj_search = function(aFilePath, shouldSearchLocally, aCallback) +{ + this.filePath = aFilePath; + + this.bundle = NULL; + this.bundleObservers = []; + + this.searchPath = NULL; + this.searchedPaths = []; + this.includePathsIndex = shouldSearchLocally ? -1 : 0; + + this.searchRequest = NULL; + + this.didCompleteCallback = aCallback; +} + +objj_search.prototype.nextSearchPath = function() +{ + // FIXME: Path searching should be more complex (go up directories, etc). + var path = objj_standardize_path((this.includePathsIndex == -1 ? "" : OBJJ_INCLUDE_PATHS[this.includePathsIndex] + '/') + this.filePath); + + ++this.includePathsIndex; + + return path; +} + +objj_search.prototype.attemptNextSearchPath = function() +{ + var searchPath = this.nextSearchPath(), + file = objj_files[searchPath]; + + objj_alert("Will attempt to find " + this.filePath + " at " + searchPath); + + // If a file for this search path already exists, then it has already been downloaded. + // If there is a callback, we can just call it, if not, we can simply return. + if (file) + { + objj_alert("The file request at " + this.filePath + " has already been downloaded at " + searchPath); + if (this.didCompleteCallback) + this.didCompleteCallback(file); + + return; + } + + var existingSearch = objj_searches[searchPath]; + + // If there is already an ongoing search for this search path, then we can let it find + // the file for us. Make sure to assign it our callback, if we have one, since only + // one search can have a callback at a time. + if (existingSearch) + { + if (this.didCompleteCallback) + existingSearch.didCompleteCallback = this.didCompleteCallback; + + return; + } + + // At this point we have a legitimate search, so we should take note of this search path + // in our search paths. + this.searchedPaths.push(this.searchPath = searchPath); + + // Before we kick off our ajax requests, see whether this directory already has + // a bundle associated with it. + var infoPath = objj_standardize_path(DIRECTORY(searchPath) + "Info.plist") + bundle = objj_bundles[infoPath]; + + // If there is, then simply look for the file in question. + if (bundle) + { + this.bundle = bundle; + this.request(searchPath, this.didReceiveSearchResponse); + } + else + { + // If there isn't a bundle associated with this directory, then we also have to + // search for an Info.plist. + var existingBundleSearch = objj_searches[infoPath]; + + // Again, if a search already exists for this bundle, then add ourselves to the list + // of observers for when this bundle is either found or created. + if (existingBundleSearch) + { + --this.includePathsIndex; + this.searchedPaths.pop(); + if (this.searchedPaths.length) + this.searchPath = this.searchedPaths[this.searchedPaths.length - 1]; + else + this.searchPath = NULL; + + existingBundleSearch.bundleObservers.push(this); + return; + } + // If not, then look for it. + else + { + this.bundleObservers.push(this); + this.request(infoPath, this.didReceiveBundleResponse); + + // Requests may return immediately, so by this point, we may have already gotten the bundle and replaced the file. + if (!this.searchReplaced) + this.searchRequest = this.request(searchPath, this.didReceiveSearchResponse); + } + } +} + +IF (ACTIVE_X) + +objj_search.responseCallbackLock = NO; +objj_search.responseCallbackQueue = []; + +objj_search.removeResponseCallbackForFilePath = function(aFilePath) +{ + var queue = objj_search.responseCallbackQueue, + index = queue.length; + + while (index--) + if (queue[index][3] == aFilePath) + { + queue.splice(index, 1); + return; + } +} + +objj_search.serializeResponseCallback = function(aMethod, aSearch, aResponse, aFilePath) +{ + var queue = objj_search.responseCallbackQueue; + + queue.push([aMethod, aSearch, aResponse, aFilePath]); + + if (objj_search.responseCallbackLock) + return; + objj_search.responseCallbackLock = YES; + + while (queue.length) + { + var callback = queue[0]; + queue.splice(0, 1); + + callback[0].apply(callback[1], [callback[2]]); + } + + objj_search.responseCallbackLock = NO; +} + +ENDIF + +objj_search.prototype.request = function(aFilePath, aMethod) +{ + var search = this, + isPlist = aFilePath.substr(aFilePath.length - 6, 6) == ".plist", + request = objj_request_xmlhttp(), + response = objj_response_xmlhttp(); + + response.filePath = aFilePath; + + request.onreadystatechange = function() + { + if (request.readyState == 4) + { + if (response.success = (request.status != 404 && request.responseText && request.responseText.length) ? YES : NO) + { + if (window.files_total) + { + if (!window.files_loaded) + window.files_loaded = 0; + + window.files_loaded += request.responseText.length; + + if (window.update_progress) + window.update_progress(window.files_loaded / window.files_total); + } + + if (isPlist) + response.xml = objj_standardize_xml(request); + else + response.text = request.responseText; + } + + if (ACTIVE_X) + objj_search.serializeResponseCallback(aMethod, search, response, aFilePath); + else + aMethod.apply(search, [response]); + } + } + + objj_searches[aFilePath] = this; + + if (request.overrideMimeType && isPlist) + request.overrideMimeType('text/xml'); + + if (OPERA && aFilePath.charAt(0) != '/') + aFilePath = OBJJ_BASE_URI + aFilePath; + + try + { + request.open("GET", aFilePath, YES); + request.send(""); + } + catch (anException) + { + response.success = NO; + + if (ACTIVE_X) + objj_search.serializeResponseCallback(aMethod, search, response, aFilePath); + else + aMethod.apply(search, [response]); + } + + return request; +} + +objj_search.prototype.didReceiveSearchResponse = function(aResponse) +{ + // If we do not have a bundle yet, we cannot appropriately + // handle this response, so wait. + if (!this.bundle) + { + this.cachedSearchResponse = aResponse; + return; + } + + if (aResponse.success) + { + file = new objj_file(); + + file.path = aResponse.filePath; + file.bundle = this.bundle + file.contents = aResponse.text; + + this.complete(file); + } + else if (this.includePathsIndex < OBJJ_INCLUDE_PATHS.length) + { + // Clear out the bundle since it will no longer be valid. + // FIXME: FIXME: FIXME: WARNING: Wouldn't it be cleaner to just always do this in attemptNextSearchPath? + this.bundle = NULL; + this.attemptNextSearchPath(); + } + else + objj_exception_throw(new objj_exception(OBJJFileNotFoundException, "*** Could not locate file named \"" + this.filePath + "\" in search paths.")); +} + +objj_search.prototype.didReceiveBundleResponse = function(aResponse) +{ + var bundle = new objj_bundle(); + + bundle.path = aResponse.filePath; + + if (aResponse.success) + bundle.info = CPPropertyListCreateFromXMLData(aResponse.xml); + else + bundle.info = new objj_dictionary(); + + objj_bundles[aResponse.filePath] = bundle; + + var executablePath = dictionary_getValue(bundle.info, "CPBundleExecutable"); + + if (executablePath) + { + this.request(DIRECTORY(aResponse.filePath) + executablePath, this.didReceiveExecutableResponse); + + // FIXME: Is this the right approach? + // Request the compiled file regardless of whether our current inquiry + var directory = DIRECTORY(aResponse.filePath), + replacedFiles = dictionary_getValue(bundle.info, "CPBundleReplacedFiles"), + index = 0, + count = replacedFiles.length; + + for (; index < count; ++index) + { + // Halt any forward searches of these files from taking place. + objj_searches[directory + replacedFiles[index]] = this; + + if (directory + replacedFiles[index] == this.searchPath) + { + this.searchReplaced = YES; + + if (!this.cachedSearchResponse && this.searchRequest) + this.searchRequest.abort(); + + if (ACTIVE_X) + objj_search.removeResponseCallbackForFilePath(this.searchPath); + } + } + } + this.bundle = bundle; + var observers = this.bundleObservers, + index = 0, + count = observers.length; + + for(; index < count; ++index) + { + var observer = observers[index]; + + // Force the observer to just attempt the path all over again, + // since it may have been replaced by the executable. + // FIXME: we should be just reattempting the ones that collided. + if (observer != this) + observer.attemptNextSearchPath(); + + // If we have a cached response and the search has not been + // replaced by the executable, then let it proceed. + else if (this.cachedSearchResponse && !this.searchReplaced) + this.didReceiveSearchResponse(this.cachedSearchResponse); + } + + this.bundleObservers = []; +} + +objj_search.prototype.didReceiveExecutableResponse = function(aResponse) +{ + if (!aResponse.success) + objj_exception_throw(new objj_exception(OBJJExecutableNotFoundException, "*** The specified executable could not be located at \"" + this.filePath + "\".")); + + var files = objj_decompile(aResponse.text, this.bundle), + index = 0, + count = files.length, + length = this.filePath.length; + + for (; index < count; ++index) + { + var file = files[index], + path = file.path; + + if (this.filePath == path.substr(path.length - length)) + this.complete(file); + else + objj_files[path] = file; + } +} + +objj_search.prototype.complete = function(aFile) +{ + var index = 0, + count = this.searchedPaths.length; + + for (; index < count; ++index) + { + objj_files[this.searchedPaths[index]] = aFile; + //FIXME: uncomment this: + //delete objj_inquiries[anInquiry.searchedPaths[index]]; + } + + if (this.didCompleteCallback) + this.didCompleteCallback(aFile); +} + +// objj_standardize_path +// +// Standardizes the input path by removing extrenenous components and resolving +// references to parent directories. + +function objj_standardize_path(aPath) +{ + if (aPath.indexOf("/./") != -1 && aPath.indexOf("//") != -1 && aPath.indexOf("/../") != -1) + return aPath; + + var index = 0, + components = aPath.split('/'); + + for(;index < components.length; ++index) + if(components[index] == "..") + { + components.splice(index - 1, 2); + index -= 2; + } + else if(index != 0 && !components[index].length || components[index] == '.' || components[index] == "..") + components.splice(index--, 1); + + return components.join('/'); +} + +IF (ACTIVE_X) + +objj_standardize_xml = function(aRequest) +{ + var XMLData = new ActiveXObject("Microsoft.XMLDOM"); + XMLData.loadXML(aRequest.responseText.substr(aRequest.responseText.indexOf(".dtd\">") + 6)); + + return XMLData; +} + +ELSE + +objj_standardize_xml = function(aRequest) +{ + return aRequest.responseXML; +} + +ENDIF + +function objj_response_xmlhttp() +{ + return new Object; +} + +// objj_request_xmlhttp() +// +// To be used as a browser-independent implementation of XMLHttpRequest. +// Currently known to support Safari, Firefox, IE 6/7, and Opera. +// +// Throws a primitive exception if XMLHttpRequests are not supported on +// the given browser. + +IF (NATIVE_XMLHTTPREQUEST) + +objj_request_xmlhttp = function() +{ + return new XMLHttpRequest(); +} + +ELIF (ACTIVE_X) + +// DON'T try 4.0 and 5.0. Should we be trying anything other than 3.0 and 6.0? +// http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx +var MSXML_XMLHTTP_OBJECTS = [ "Microsoft.XMLHTTP", "Msxml2.XMLHTTP", "Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP.6.0" ],//"Msxml2.XMLHTTP.4.0", "Msxml2.XMLHTTP.5.0" ], + index = MSXML_XMLHTTP_OBJECTS.length; + +while (index--) +{ + try + { + new ActiveXObject(MSXML_XMLHTTP_OBJECTS[index]); + break; + } + catch (anException) + { + } +} + +var MSXML_XMLHTTP = MSXML_XMLHTTP_OBJECTS[index]; + +delete index; +delete MSXML_XMLHTTP_OBJECTS; + +objj_request_xmlhttp = function() +{ + return new ActiveXObject(MSXML_XMLHTTP); +} + +ENDIF diff --git a/Objective-J/license.txt b/Objective-J/license.txt new file mode 100644 index 0000000000..40190756cf --- /dev/null +++ b/Objective-J/license.txt @@ -0,0 +1,22 @@ +/* + * Objective-J.js + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + diff --git a/Objective-J/plist.js b/Objective-J/plist.js new file mode 100644 index 0000000000..d03ede5273 --- /dev/null +++ b/Objective-J/plist.js @@ -0,0 +1,661 @@ +/* + * plist.js + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +kCFPropertyListOpenStepFormat = 1; +kCFPropertyListXMLFormat_v1_0 = 100; +kCFPropertyListBinaryFormat_v1_0 = 200; +kCFPropertyList280NorthFormat_v1_0 = -1000; + +OBJJPlistParseException = "OBJJPlistParseException"; + +var kCFPropertyList280NorthMagicNumber = "280NPLIST"; + +/*var BASE64_TABLE = [ + "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", + "Q", "R", "S", "T", "U", "V", "W", "X", + "Y", "Z", "a", "b", "c", "d", "e", "f", + "g", "h", "i", "j", "k", "l", "m", "n", + "o", "p", "q", "r", "s", "t", "u", "v", + "w", "x", "y", "z", "0", "1", "2", "3", + "4", "5", "6", "7", "8", "9", "+", "/" +];*/ + +function objj_data() +{ + this.string = ""; + this._plistObject = NULL; + this.bytes = NULL; + this.base64 = NULL; +} + +/*function data_setBytes(data, bytes) +{ + this.bytes = bytes; + this.base64 = NULL; +} + +function data_getBytes(data) +{ + return this.bytes; +} + +function data_getBase64String() +{ + if (!this.base64) + { + this.base64 = ""; + + var offset = 0, + bytes = this.bytes, + length = bytes.length; + + uint24 = this.bytes[offset] << 16 | this.bytes[offset + 1] << 8 | this.bytes[offset + 2]; + this.base64 += BASE64_TABLE[ ( uint24 & 0xFC0000 ) >> 18 ]); + this.base64 += BASE64_TABLE[ ( uint24 & 0x03F000 ) >> 12 ]); + this.base64 += BASE64_TABLE[ ( uint24 & 0x0FC0 ) >> 6 ]); + this.base64 += BASE64_TABLE[ ( uint24 & 0x3F ) ]); + + for (;offset < length; ++offset) + { + this.base64 = BASE64_TABLE[bytes[offset] >> 2]; + this.base64 = BASE64_TABLE[((bytes[offset] & 3) << 4) | (bytes[++offset] >> 4)]; + this.base64 = offset < length ? "=" : BASE64_TABLE[((bytes[offset] & 15) << 2) | (bytes[++offset] >> 6)]; + this.base64 = offset < length ? "=" : BASE64_TABLE[bytes[offset] & 63]; + } + } + + return this.base64; +}*/ + +var objj_markedStream = function(aString) +{ + var index = aString.indexOf(';'); + + // Grab the magic number. + this._magicNumber = aString.substr(0, index); + + this._location = aString.indexOf(';', ++index); + + // Grab the version number. + this._version = aString.substring(index, this._location++); + this._string = aString; +} + +objj_markedStream.prototype.magicNumber = function() +{ + return this._magicNumber; +} + +objj_markedStream.prototype.version = function() +{ + return this._version; +} + +objj_markedStream.prototype.getMarker = function() +{ + var string = this._string, + location = this._location; + + if (location >= string.length) + return NULL; + + var next = string.indexOf(';', location); + + if (next < 0) + return NULL; + + var marker = string.substring(location, next); + + this._location = next + 1; + + return marker; +} + +objj_markedStream.prototype.getString = function() +{ + var string = this._string, + location = this._location; + + if (location >= string.length) + return NULL; + + var next = string.indexOf(';', location); + + if (next < 0) + return NULL; + + var size = parseInt(string.substring(location, next)), + text = string.substr(next + 1, size); + + this._location = next + 1 + size; + + return text; +} + +// + +function CPPropertyListCreateData(aPlistObject, aFormat) +{ + if (aFormat == kCFPropertyListXMLFormat_v1_0) + return CPPropertyListCreateXMLData(aPlistObject); + + if (aFormat == kCFPropertyList280NorthFormat_v1_0) + return CPPropertyListCreate280NorthData(aPlistObject); + + return NULL; +} + +function CPPropertyListCreateFromData(aData, aFormat) +{ + if (!aFormat) + { + // Attempt to guess the format. + if (aData instanceof objj_data) + { + var string = aData.string ? aData.string : objj_msgSend(aData, "string"); + + if (string.substr(0, kCFPropertyList280NorthMagicNumber.length) == kCFPropertyList280NorthMagicNumber) + aFormat = kCFPropertyList280NorthFormat_v1_0; + else + aFormat = kCFPropertyListXMLFormat_v1_0; + } + else + aFormat = kCFPropertyListXMLFormat_v1_0; + } + + if (aFormat == kCFPropertyListXMLFormat_v1_0) + return CPPropertyListCreateFromXMLData(aData); + + if (aFormat == kCFPropertyList280NorthFormat_v1_0) + return CPPropertyListCreateFrom280NorthData(aData); + + return NULL; +} + +var _CPPropertyListSerializeObject = function(aPlist, serializers) +{ + var type = typeof aPlist, + valueOf = aPlist.valueOf(), + typeValueOf = typeof valueOf; + + if (type != typeValueOf) + { + type = typeValueOf; + aPlist = valueOf; + } + + if (type == "string") + return serializers["string"](aPlist, serializers); + + else if (aPlist === true || aPlist === false) + return serializers["boolean"](aPlist, serializers); + + else if (type == "number") + { + var integer = FLOOR(aPlist); + + if (integer == aPlist) + return serializers["integer"](aPlist, serializers); + else + return serializers["real"](aPlist, serializers); + } + + else if (aPlist.slice) + return serializers["array"](aPlist, serializers); + + else + return serializers["dictionary"](aPlist, serializers); +} + +// XML Format 1.0 + +var XML_XML = "xml", + XML_DOCUMENT = "#document", + + PLIST_PLIST = "plist", + PLIST_KEY = "key", + PLIST_DICTIONARY = "dict", + PLIST_ARRAY = "array", + PLIST_STRING = "string", + PLIST_BOOLEAN_TRUE = "true", + PLIST_BOOLEAN_FALSE = "false", + PLIST_NUMBER_REAL = "real", + PLIST_NUMBER_INTEGER = "integer"; + +#if RHINO + +#define NODE_NAME(anXMLNode) (String(anXMLNode.getNodeName())) +#define NODE_TYPE(anXMLNode) (anXMLNode.getNodeType()) +#define NODE_VALUE(anXMLNode) (String(anXMLNode.getNodeValue())) +#define FIRST_CHILD(anXMLNode) (anXMLNode.getFirstChild()) +#define NEXT_SIBLING(anXMLNode) (anXMLNode.getNextSibling()) +#define PARENT_NODE(anXMLNode) (anXMLNode.getParentNode()) +#define DOCUMENT_ELEMENT(aDocument) (aDocument.getDocumentElement()) + +#else + +#define NODE_NAME(anXMLNode) (anXMLNode.nodeName) +#define NODE_TYPE(anXMLNode) (anXMLNode.nodeType) +#define NODE_VALUE(anXMLNode) (anXMLNode.nodeValue) +#define FIRST_CHILD(anXMLNode) (anXMLNode.firstChild) +#define NEXT_SIBLING(anXMLNode) (anXMLNode.nextSibling) +#define PARENT_NODE(anXMLNode) (anXMLNode.parentNode) +#define DOCUMENT_ELEMENT(aDocument) (aDocument.documentElement) + +#endif + +#define IS_OF_TYPE(anXMLNode, aType) (NODE_NAME(anXMLNode) == aType) +#define IS_PLIST(anXMLNode) IS_OF_TYPE(anXMLNode, PLIST_PLIST) + +#define IS_WHITESPACE(anXMLNode) (NODE_TYPE(anXMLNode) == 8 || NODE_TYPE(anXMLNode) == 3) +#define IS_DOCUMENTTYPE(anXMLNode) (NODE_TYPE(anXMLNode) == 10) + +#define PLIST_NEXT_SIBLING(anXMLNode) while ((anXMLNode = NEXT_SIBLING(anXMLNode)) && IS_WHITESPACE(anXMLNode)) ; +#define PLIST_FIRST_CHILD(anXMLNode) anXMLNode = FIRST_CHILD(anXMLNode); if (anXMLNode != NULL && IS_WHITESPACE(anXMLNode)) PLIST_NEXT_SIBLING(anXMLNode) + +// FIXME: no first child? +#define CHILD_VALUE(anXMLNode) (NODE_VALUE(FIRST_CHILD(anXMLNode))) + +var _plist_traverseNextNode = function(anXMLNode, stayWithin, stack) +{ + var node = anXMLNode; + + PLIST_FIRST_CHILD(node); + + // If this element has a child, traverse to it. + if (node) + return node; + + // If not, first check if it is a container class (as opposed to a designated leaf). + // If it is, then we have to pop this container off the stack, since it is empty. + if (NODE_NAME(anXMLNode) == PLIST_ARRAY || NODE_NAME(anXMLNode) == PLIST_DICTIONARY) + stack.pop(); + + // If not, next check whether it has a sibling. + else + { + if (node == stayWithin) + return NULL; + + node = anXMLNode; + + PLIST_NEXT_SIBLING(node); + + if (node) + return node; + } + + // If it doesn't, start working our way back up the node tree. + node = anXMLNode; + + // While we have a node and it doesn't have a sibling (and we're within our stayWithin), + // keep moving up. + while (node) + { + var next = node; + + PLIST_NEXT_SIBLING(next); + + // If we have a next sibling, just go to it. + if (next) + return next; + + var node = PARENT_NODE(node); + + // If we are being asked to move up, and our parent is the stay within, then just + if (stayWithin && node == stayWithin) + return NULL; + + // Pop the stack if we have officially "moved up" + stack.pop(); + } + + return NULL; +} + +function CPPropertyListCreateFromXMLData(XMLNodeOrData) +{ + var XMLNode = XMLNodeOrData; + + if (XMLNode.string) + { +#if RHINO + XMLNode = DOCUMENT_ELEMENT(Packages.javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder().parse( + new Packages.org.xml.sax.InputSource(new Packages.java.io.StringReader(XMLNode.string)))); +#else + if (window.ActiveXObject) + { + XMLNode = new ActiveXObject("Microsoft.XMLDOM"); + XMLNode.loadXML(XMLNodeOrData.string.substr(XMLNodeOrData.string.indexOf(".dtd\">") + 6)); + } + else + XMLNode = DOCUMENT_ELEMENT(new DOMParser().parseFromString(XMLNodeOrData.string, "text/xml")); +#endif + } + + // Skip over DOCTYPE and so forth. + while (IS_OF_TYPE(XMLNode, XML_DOCUMENT) || IS_OF_TYPE(XMLNode, XML_XML)) + PLIST_FIRST_CHILD(XMLNode); + + // Skip over the DOCTYPE... see a pattern? + if (IS_DOCUMENTTYPE(XMLNode)) + PLIST_NEXT_SIBLING(XMLNode); + + // If this is not a PLIST, bail. + if (!IS_PLIST(XMLNode)) + return NULL; + + var key = "", + object = NULL, + plistObject = NULL, + + plistNode = XMLNode, + + containers = [], + currentContainer = NULL; + + while (XMLNode = _plist_traverseNextNode(XMLNode, plistNode, containers)) + { + var count = containers.length; + + if (count) + currentContainer = containers[count - 1]; + + if (NODE_NAME(XMLNode) == PLIST_KEY) + { + key = CHILD_VALUE(XMLNode); + PLIST_NEXT_SIBLING(XMLNode); + } + + switch (NODE_NAME(XMLNode)) + { + case PLIST_ARRAY: object = [] + containers.push(object); + break; + case PLIST_DICTIONARY: object = new objj_dictionary(); + containers.push(object); + break; + + case PLIST_NUMBER_REAL: object = parseFloat(CHILD_VALUE(XMLNode)); + break; + case PLIST_NUMBER_INTEGER: object = parseInt(CHILD_VALUE(XMLNode)); + break; + + case PLIST_STRING: object = decodeURIComponent(FIRST_CHILD(XMLNode) ? CHILD_VALUE(XMLNode) : ""); + break; + + case PLIST_BOOLEAN_TRUE: object = true; + break; + case PLIST_BOOLEAN_FALSE: object = false; + break; + + default: objj_exception_throw(new objj_exception(OBJJPlistParseException, "*** " + NODE_NAME(XMLNode) + " tag not recognized in Plist.")); + } + + if (!plistObject) + plistObject = object; + + else if (currentContainer) + // If the container is an array... + if (currentContainer.slice) + currentContainer.push(object); + else + dictionary_setValue(currentContainer, key, object); + } + + return plistObject; +} + +function CPPropertyListCreateXMLData(aPlist) +{ + var data = new objj_data(); + + data.string = ""; + + data.string += ""; + data.string += ""; + data.string += ""; + + _CPPropertyListAppendXMLData(data, aPlist, ""); + + data.string += ""; + + return data; +} + +// CPPropertyListCreateXMLData Helper Functions + +var _CPArrayAppendXMLData = function(XMLData, anArray) +{ + var i = 0, + count = anArray.length; + + XMLData.string += ""; + + for (; i < count; ++i) + _CPPropertyListAppendXMLData(XMLData, anArray[i]); + + XMLData.string += ""; +} + +var _CPDictionaryAppendXMLData = function(XMLData, aDictionary) +{ + var keys = aDictionary.keys, + i = 0, + count = keys.length; + + XMLData.string += ""; + + for (; i < count; ++i) + { + XMLData.string += "" + keys[i] + ""; + _CPPropertyListAppendXMLData(XMLData, dictionary_getValue(aDictionary, keys[i])); + } + + XMLData.string += ""; +} + +var _CPPropertyListAppendXMLData = function(XMLData, aPlist) +{ + var type = typeof aPlist, + valueOf = aPlist.valueOf(), + typeValueOf = typeof valueOf; + + if (type != typeValueOf) + { + type = typeValueOf; + aPlist = valueOf; + } + + if (type == "string") + XMLData.string += "" + encodeURIComponent(aPlist) + ""; + + else if (aPlist === true) + XMLData.string += ""; + else if (aPlist === false) + XMLData.string += ""; + + else if (type == "number") + { + var integer = FLOOR(aPlist); + + if (integer == aPlist) + XMLData.string += "" + aPlist + ""; + else + XMLData.string += "" + aPlist + ""; + } + + else if (aPlist.slice) + _CPArrayAppendXMLData(XMLData, aPlist); + + else + _CPDictionaryAppendXMLData(XMLData, aPlist); +} + +// 280 North Plist Format + +var ARRAY_MARKER = "A", + DICTIONARY_MARKER = "D", + FLOAT_MARKER = "f", + INTEGER_MARKER = "d", + STRING_MARKER = "S", + TRUE_MARKER = "T", + FALSE_MARKER = "F", + KEY_MARKER = "K", + END_MARKER = "E"; + +function CPPropertyListCreateFrom280NorthData(aData) +{ + var stream = new objj_markedStream(aData.string), + + marker = NULL, + + key = "", + object = NULL, + plistObject = NULL, + + containers = [], + currentContainer = NULL; + + while (marker = stream.getMarker()) + { + if (marker == END_MARKER) + { + containers.pop(); + continue; + } + + var count = containers.length; + + if (count) + currentContainer = containers[count - 1]; + + if (marker == KEY_MARKER) + { + key = stream.getString(); + marker = stream.getMarker(); + } + + switch (marker) + { + case ARRAY_MARKER: object = [] + containers.push(object); + break; + case DICTIONARY_MARKER: object = new objj_dictionary(); + containers.push(object); + break; + + case FLOAT_MARKER: object = parseFloat(stream.getString()); + break; + case INTEGER_MARKER: object = parseInt(stream.getString()); + break; + + case STRING_MARKER: object = stream.getString(); + break; + + case TRUE_MARKER: object = true; + break; + case FALSE_MARKER: object = false; + break; + + default: objj_exception_throw(new objj_exception(OBJJPlistParseException, "*** " + marker + " marker not recognized in Plist.")); + } + + if (!plistObject) + plistObject = object; + + else if (currentContainer) + // If the container is an array... + if (currentContainer.slice) + currentContainer.push(object); + else + dictionary_setValue(currentContainer, key, object); + } + + return plistObject; +} + +function CPPropertyListCreate280NorthData(aPlist) +{ + var data = new objj_data(); + + data.string = kCFPropertyList280NorthMagicNumber + ";1.0;" + _CPPropertyListSerializeObject(aPlist, _CPPropertyList280NorthSerializers); + + return data; +} + +var _CPPropertyList280NorthSerializers = {}; + +_CPPropertyList280NorthSerializers["string"] = function(aString) +{ + return STRING_MARKER + ';' + aString.length + ';' + aString; +} + +_CPPropertyList280NorthSerializers["boolean"] = function(aBoolean) +{ + return (aBoolean ? TRUE_MARKER : FALSE_MARKER) + ';'; +} + +_CPPropertyList280NorthSerializers["integer"] = function(anInteger) +{ + var string = "" + anInteger; + + return INTEGER_MARKER + ';' + string.length + ';' + string; +} + +_CPPropertyList280NorthSerializers["real"] = function(aFloat) +{ + var string = "" + aFloat; + + return FLOAT_MARKER + ';' + string.length + ';' + string; +} + +_CPPropertyList280NorthSerializers["array"] = function(anArray, serializers) +{ + var index = 0, + count = anArray.length, + string = ARRAY_MARKER + ';'; + + for (; index < count; ++index) + string += _CPPropertyListSerializeObject(anArray[index], serializers); + + return string + END_MARKER + ';'; +} + +_CPPropertyList280NorthSerializers["dictionary"] = function(aDictionary, serializers) +{ + var keys = aDictionary.keys, + index = 0, + count = keys.length, + string = DICTIONARY_MARKER +';'; + + for (; index < count; ++index) + { + var key = keys[index]; + + string += KEY_MARKER + ';' + key.length + ';' + key; + string += _CPPropertyListSerializeObject(dictionary_getValue(aDictionary, key), serializers); + } + + return string + END_MARKER + ';'; +} diff --git a/Objective-J/preprocess.js b/Objective-J/preprocess.js new file mode 100644 index 0000000000..b7e7362145 --- /dev/null +++ b/Objective-J/preprocess.js @@ -0,0 +1,568 @@ +/* + * preprocess.js + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +OBJJParseException = "OBJJParseException"; +OBJJClassNotFoundException = "OBJJClassNotFoundException"; + +var TOKEN_NEW = "new", + TOKEN_SUPER = "super", + TOKEN_CLASS = "class", + TOKEN_IMPORT = "import", + TOKEN_FUNCTION = "function", + TOKEN_SELECTOR = "selector", + TOKEN_IMPLEMENTATION = "implementation", + + TOKEN_PLUS = '+', + TOKEN_MINUS = '-', + TOKEN_COLON = ':', + TOKEN_COMMA = ',', + TOKEN_PERIOD = '.', + TOKEN_ASTERISK = '*', + TOKEN_SEMICOLON = ';', + TOKEN_LESS_THAN = '<', + TOKEN_OPEN_BRACE = '{', + TOKEN_CLOSE_BRACE = '}', + TOKEN_GREATER_THAN = '>', + TOKEN_OPEN_BRACKET = '[', + TOKEN_DOUBLE_QUOTE = '"', + TOKEN_PREPROCESSOR = '@', + TOKEN_CLOSE_BRACKET = ']', + TOKEN_QUESTION_MARK = '?', + TOKEN_OPEN_PARENTHESIS = '(', + TOKEN_CLOSE_PARENTHESIS = ')'; + + +// FIXME: This could break with static preinterpretation. +var SUPER_CLASSES = new objj_dictionary(), + CURRENT_SUPER_CLASS = NULL, + CURRENT_CLASS_NAME = NULL; + +var OBJJ_CURRENT_BUNDLE = NULL; + +// FIXME: Used fixed regex +var objj_lexer = function(aString, aSourceFile) +{ + this._index = 0; + this._tokens = (aString + '\n').match(/\/\/.*(\r|\n)?|\/\*(?:.|\n|\r)*?\*\/|\w+\b|[+-]?\d+(([.]\d+)*([eE][+-]?\d+))?|"[^"\\]*(\\.[^"\\]*)*"|'[^'\\]*(\\.[^'\\]*)*'|\s+|./g); + + this.file = aSourceFile; + + return this; +} + +objj_lexer.prototype.next = function() +{ + return this._tokens[this._index++]; +} + +objj_lexer.prototype.previous = function() +{ + return this._tokens[--this._index]; +} + +objj_lexer.prototype.last = function() +{ + if (this._index > 1) + return this._tokens[this._index - 2]; + + return NULL; +} + +objj_lexer.prototype.skip_whitespace= function() +{ + var token; + while((token = this.next()) && (!(/\S/).test(token) || token.substr(0,2) == "//" || token.substr(0,2) == "/*")) ; + + return token; +} + +var objj_preprocess_method = function(tokens, count, array_name) +{ + var token, + selector = "", + parameters = new Array(); + + while((token = tokens.skip_whitespace()) && token != TOKEN_OPEN_BRACE) + { + if (token == TOKEN_COLON) + { + // Colons are part of the selector name + selector += token; + + token = tokens.skip_whitespace(); + + if (token == TOKEN_OPEN_PARENTHESIS) + { + // Swallow parameter/return type. Perhaps later we can use this for debugging? + while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) ; + + token = tokens.skip_whitespace(); + } + + // Since this follows a colon, this must be the parameter name. + parameters[parameters.length] = token; + } + else if (token == TOKEN_OPEN_PARENTHESIS) + // Since :( is handled above, this must be the return type, just swallow it. + while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) ; + // Argument list ", ..." + else if (token == TOKEN_COMMA) + { + // At this point, "..." MUST follow. + if ((token = tokens.skip_whitespace()) != TOKEN_PERIOD || tokens.next() != TOKEN_PERIOD || tokens.next() != TOKEN_PERIOD) + objj_exception_throw(new objj_exception(OBJJParseException, "*** Argument list expected after ','.")); + + // FIXME: Shouldn't allow any more after this. + } + // Build selector name. + else + selector += token; + } + + var i= 0, + length = parameters.length, + selectorDisplayName = "$"+CURRENT_CLASS_NAME+"__"+selector.replace(/:/g, "_"), + preprocessed = array_name + "["+count+"] = new objj_method(sel_registerName(\""+selector+"\"), function "+selectorDisplayName+"(self, _cmd"; + + for(; i < length; ++i) + preprocessed += ", " + parameters[i]; + +#if FIREBUG + // FireBug auto expands and doesn't allow you to resize, so truncate it. + var truncatedSelector = "["+CURRENT_CLASS_NAME+" "+(selector.length > 60 ? selector.substring(0, 60) + "..." : selector)+"]"; + return preprocessed + ")\n{ \"__FIREBUG_FNAME__"+truncatedSelector+"\".length;\n with(self)\n{" + objj_preprocess_tokens(tokens, TOKEN_CLOSE_BRACE, TOKEN_OPEN_BRACE) + "}\n});\n"; +#else + return preprocessed + ")\n{ with(self)\n{" + objj_preprocess_tokens(tokens, TOKEN_CLOSE_BRACE, TOKEN_OPEN_BRACE) + "}\n});\n"; +#endif +} + +var objj_preprocess_implementation= function(tokens) +{ + var token = "", + category = NO, + preprocessed = "", + class_name = tokens.skip_whitespace(), + superclass_name = "Nil", + class_method_count = 0, + instance_method_count = 0; + + if (!(/^\w/).test(class_name)) + objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected class name, found \"" + class_name + "\".")); + + CURRENT_SUPER_CLASS = NULL; + CURRENT_CLASS_NAME = class_name; + + // NOTE: This behavior is currently turned off. + // addWorkingClass(class_name); + + // If we reach an open parenthesis, we are declaring a category. + if((token = tokens.skip_whitespace()) == TOKEN_OPEN_PARENTHESIS) + { + token = tokens.skip_whitespace(); + + if(tokens.skip_whitespace() != TOKEN_CLOSE_PARENTHESIS) + objj_exception_throw(new objj_exception(OBJJParseException, "*** Improper Category Definition for class \""+class_name+"\".")); + + preprocessed += "{\nvar the_class = objj_getClass(\"" + class_name + "\")\n"; + preprocessed += "if(!the_class) objj_exception_throw(new objj_exception(OBJJClassNotFoundException, \"*** Could not find definition for class \\\"" + class_name + "\\\"\"));\n"; + preprocessed += "var meta_class = the_class.isa;"; + + var superclass_name = dictionary_getValue(SUPER_CLASSES, class_name); + + // FIXME: We should have a better solution for this case, although it's actually not much slower than the real case. + if (!superclass_name) + CURRENT_SUPER_CLASS = "objj_getClass(\"" + class_name + "\").super_class"; + else + CURRENT_SUPER_CLASS = "objj_getClass(\"" + superclass_name + "\")"; + } + else + { + // If we reach a colon (':'), then a superclass is being declared. + if(token == TOKEN_COLON) + { + token = tokens.skip_whitespace(); + if (!(/^\w/).test(token)) + objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected class name, found \"" + token + "\".")); + + superclass_name = token; + CURRENT_SUPER_CLASS = "objj_getClass(\"" + superclass_name + "\")"; + + dictionary_setValue(SUPER_CLASSES, class_name, superclass_name); + + token = tokens.skip_whitespace(); + } + + preprocessed += "{var the_class = objj_allocateClassPair(" + superclass_name + ", \"" + class_name + "\"),\nmeta_class = the_class.isa;"; + + // If we are at an opening curly brace ('{'), then we have an ivar declaration. + if (token == TOKEN_OPEN_BRACE) + { + var ivar = true, + ivar_count = 0; + + while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_BRACE) + { + if (token != TOKEN_SEMICOLON && (ivar = !ivar)) + { + if (ivar_count++ == 0) + preprocessed += "class_addIvars(the_class, ["; + else + preprocessed += ", "; + + preprocessed += "new objj_ivar(\"" + token + "\")"; + } + } + + if (ivar_count) + preprocessed += "]);\n"; + + if (!token) + objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected '}'")); + } + else tokens.previous(); + + // We must make a new class object for our class definition. + preprocessed += "objj_registerClassPair(the_class);\n"; + + // Add this class to the current bundle. + preprocessed += "objj_addClassForBundle(the_class, objj_getBundleWithPath(OBJJ_CURRENT_BUNDLE.path));\n"; + } + + while((token = tokens.skip_whitespace())) + { + if(token == TOKEN_PLUS) preprocessed += (class_method_count ? "" : "var class_methods = [];\n") + objj_preprocess_method(tokens, class_method_count++, "class_methods"); + else if(token == TOKEN_MINUS) preprocessed += (instance_method_count ? "" : "var instance_methods = [];\n") + objj_preprocess_method(tokens, instance_method_count++, "instance_methods"); + // Check if we've reached @end... + else if(token == TOKEN_PREPROCESSOR) + { + // The only preprocessor directive we should ever encounter at this point is @end. + if((token = tokens.next()) == "end") + break; + else + objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected \"@end\", found \"@" + token + "\".")); + } + } + + // Do the instance methods first because they could override the class methods if not. + if (instance_method_count) preprocessed += "class_addMethods(the_class, instance_methods);\n"; + if (class_method_count) preprocessed += "class_addMethods(meta_class, class_methods);\n"; + + return preprocessed + '}'; +} + +var objj_preprocess_directive = function(tokens) +{ + // Grab the next token, preprocessor directives follow '@' immediately. + token = tokens.next(); + + // To provide compatibility with Objective-C files, we convert NSString literals into + // toll-freed JavaScript/CPString strings. + if(token.charAt(0) == TOKEN_DOUBLE_QUOTE) return token; + // Currently we simply swallow forward declarations and only provide them to allow + // compatibility with Objective-C files. + else if(token == TOKEN_CLASS) { tokens.skip_whitespace(); return ""; } + // @implementation Class implementations + else if(token == TOKEN_IMPLEMENTATION) return objj_preprocess_implementation(tokens); + // @selector + else if(token == TOKEN_SELECTOR) + { + // Swallow open parenthesis. + if (tokens.skip_whitespace() != TOKEN_OPEN_PARENTHESIS) + objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected ')'")); + return "sel_registerName(\"" + objj_preprocess_tokens(tokens, TOKEN_CLOSE_PARENTHESIS) +"\")"; + } + + return ""; +} + +var objj_preprocess_brackets = function(tokens) +{ + // We maintain two parallel interpretations through the process, + // One which assumes the brackets form a literal array, and + // another that builds the possible message dispatch composed of + // a receiver, a selector, and a number of marg_list. + var literal = '[', + receiver = "", + selector = "", + marg_list = new Array(), + preprocessed = "objj_msgSend"; + + // We keep track of the current iterative token, the previous + // expression, tertiary operations, and parenthesis. + var token = "", + array = false, + previous = "", + + braces = 0, + tertiary = 0, + parenthesis = 0; + + while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_BRACKET) + { + // We should attempt to preprocess our message code only if we are + // not within parenthesis or a tertiary statement. + var preprocess = !braces && !tertiary && !parenthesis && !array; + + // Generally we are unconcerned with whitespace, however the new + // operator is a special case since it could alse be part of an identifier. + if(token == TOKEN_NEW) token = "new "; + // We handle the special case where the receiver is super. In this case, + // use an objj_super object and use objj_msgSendSuper instead of objj_msgSend. + else if(token == TOKEN_SUPER) + { + if (!receiver.length) + { + preprocessed = "objj_msgSendSuper"; + token = "{ receiver:self, super_class:" + CURRENT_SUPER_CLASS + " }"; + } + else + objj_exception_throw(new objj_exception(OBJJParseException, "*** Can't use 'super' in this context.")); + } + else if (token == TOKEN_OPEN_BRACE) ++braces; + else if (token == TOKEN_CLOSE_BRACE) --braces; + // Tertiary expressions have the potential to confuse the preprocessor, + // so keep track of them to avoid misidentifying a colon (':') for an + // argument. + else if(token == TOKEN_QUESTION_MARK) ++tertiary; + // Keep track of expressions within parenthesis. + else if(token == TOKEN_OPEN_PARENTHESIS) ++parenthesis; + else if(token == TOKEN_CLOSE_PARENTHESIS) --parenthesis; + // If we reach a nested bracket, preprocess it first and interpret + // the result as the current token. + else if(token == TOKEN_OPEN_BRACKET) token = objj_preprocess_brackets(tokens); + else if(token == TOKEN_PREPROCESSOR) token = objj_preprocess_directive(tokens); + + // Preprocess tokens only if we're not within parenthesis or in a + // tertiary statement. + if(preprocess) + { + // If we ever reach a comma that is not in a sub expression, + // and we haven't yet begun to construct a selector, then + // we can be sure that this is not a message dispatch, and + // we should simply return so that the lexer can continue + // to preprocess normally. + if(token == TOKEN_COMMA && !selector.length) + array = true;//return literal + token; + // A colon (':') alerts us that we've reached the end of a label. + if(token == TOKEN_COLON) + { + // If the previous token was actually white space, so this + // is an empty label. + var last = tokens.last(); + + if (last && (!(/\S/).test(last) || last.substr(0, 2) == "//" || last.substr(0, 2) == "/*")) + { + selector += ':'; + marg_list[marg_list.length - 1] += previous; + marg_list[marg_list.length] = previous = ""; + } + + // If not the previous token was was part of the label and + // thus should be appended to the selector. + else + { + selector += previous + ":"; + marg_list[marg_list.length] = previous = ""; + } + } + else + { + // If we've already begun building our selector, then the token + // should be applied to the current argument. + if (selector.length) + marg_list[marg_list.length - 1] += previous; + // If not, then it belongs to the receiver. + else + receiver += previous; + + previous = token; + } + } + // If not, add it to previous to be interpreted as one block. + else + { + // We know this colon (':') to match a previous tertiary expression. + // FIXME PARSER_BUG: This does not properly account for interspersed { } and ?: + // https://trac.280north.com/ticket/15 + if(token == TOKEN_COLON && !braces) + --tertiary; + previous += token; + } + + // The literal interpretation is always handled in the same + // manner, simply aggregate the tokens. + literal += token; + } + + // If we have a selector, then add the remaining string to the argument. + if (selector.length) marg_list[marg_list.length - 1] += previous; + // If not, check whether the final character of our proposed receiver + // is an operator or the new keyword. Also check that the previous + // expression does not begin with a parenthesis, since this means it + // definitely can't be a selector. + // FIXME: There is probably a more concise and efficient way to represent + // these regular expressions. + // FIXME: Should the second expression be the same as the first. For example, + // array[i%] becomes arrayobjj_msgSend(i,"%") if not. This is an error either + // way though. https://trac.280north.com/ticket/6 + else if(!array && receiver.length && !((/[\:\+\-\*\/\=\<\>\&\|\!\.\%]/).test(receiver.charAt(receiver.length - 1))) && + receiver != "new " && !(/[\+\-\*\/\=\<\>\&\|\!\.\[\^\(]/).test(previous.charAt(0))) + selector = previous; + // If we did not build a selector through the parsing process, then we + // are either a single entry literal array, or an array index, and so + // we should simply return our literal string. + else return literal + ']'; + + // NOTE: For now, turn this behavior off. + // Classes act much like keywords since they are not directly manipulatable. + // We thus check whether it exists in our class hash or working hash, and if + // instead use a reference to it if it does. + // if (tokens.containsClass(receiver) || objj_getClass(receiver)) receiver = "objj_getClass(\"" + receiver + "\")"; + + // The first two arguments are always the receiver and the selector. + preprocessed += '(' + receiver + ", \"" + sel_registerName(selector) + "\""; + + // Populate the remaining parameters with the provided arguments. + var i = 0, + length = marg_list.length; + + for(; i < length; ++i) + preprocessed += ", " + marg_list[i]; + + // Return the fully preprocessed message dispatch. + return preprocessed + ')'; +} + +function objj_preprocess_tokens(tokens, terminator, instigator, segment) +{//if (window.p) alert("objj_preprocess_tokens"); + var count = 0, + token = "", + fragments = [], + preprocessed = ""; + + while((token = tokens.next()) && ((token != terminator) || count)) + { + if (instigator) + { + if (token == instigator) ++count; + else if (token == terminator) --count; + } + + // We convert import statements into objj_request_import function calls, converting + // the the file paths to use the proper search arguments. + if(token == TOKEN_IMPORT) + { + if ((/[^\s]/).test(preprocessed)) + fragments.push(fragment_create_code(preprocessed, OBJJ_CURRENT_BUNDLE, tokens.file)); + + preprocessed = ""; + + var path = "", + token = tokens.skip_whitespace(), + isLocal = token != TOKEN_LESS_THAN; + + if(token == TOKEN_LESS_THAN) + { + while((token= tokens.next()) && token != TOKEN_GREATER_THAN) path+= token; + if(!token) objj_throw("Parser Error - Unterminated import statement."); + } + else if(token.charAt(0) == TOKEN_DOUBLE_QUOTE) path= token.substr(1, token.length-2); + else + objj_exception_throw(new objj_exception(OBJJParseException, "*** Expecting '<' or '\"', found \"" + token + "\".")); + + fragments.push(fragment_create_file(path, NULL, isLocal, YES, tokens.file)); + } + // Safari can't handle function declarations of the form function [name]([arguments]) { } + // in evals. It requires them to be in the form [name] = function([arguments]) { }. So we + // need to find these and fix them. + else if(token == TOKEN_FUNCTION) + {//if (window.p) alert("function"); + var accumulator= ""; + + // Following the function identifier we can either have an open parenthesis or an identifier: + while((token = tokens.next()) && token != TOKEN_OPEN_PARENTHESIS && !(/^\w/).test(token)) + accumulator += token; + + // If the next token is an open parenthesis, we have a standard function and we don't have to + // change it: + if(token == TOKEN_OPEN_PARENTHESIS) + preprocessed+= "function"+accumulator+'('; + // If it's not a parenthesis, we know we have a non-supported function declaration, so fix it: + else + { + preprocessed += token + "= function"; + +#if FIREBUG + var functionName = token; + + // Skip everything until the next close parenthesis. + while((token = tokens.next()) && token != TOKEN_CLOSE_PARENTHESIS) + preprocessed += token; + + // Don't forget the last token! + preprocessed += token; + + // Skip everything until the next open curly brace. + while((token = tokens.next()) && token != TOKEN_OPEN_BRACE) + preprocessed += token; + + // Place the open curly brace as well, and the function name + preprocessed += token + "\n \"__FIREBUG_FNAME__" + functionName + "\".length;\n"; +#endif + } + } + // If we reach an @ symbol, we are at a preprocessor directive. + else if(token == TOKEN_PREPROCESSOR) + preprocessed+= objj_preprocess_directive(tokens); + // If we reach a bracket, we will either be preprocessing a message send, a literal + // array, or an array index. + else if(token == TOKEN_OPEN_BRACKET) + preprocessed += objj_preprocess_brackets(tokens); + // If not simply append the token. + else + preprocessed += token; + } + + if (preprocessed.length && (/[^\s]/).test(preprocessed)) + fragments.push(fragment_create_code(preprocessed, OBJJ_CURRENT_BUNDLE, tokens.file)); + + if (!segment) + return fragments.length ? fragments[0].info : ""; + + return fragments; +} + +function objj_preprocess(aString, aBundle, aSourceFile) +{ + try + { + OBJJ_CURRENT_BUNDLE = aBundle; + + return objj_preprocess_tokens(new objj_lexer(aString, aSourceFile), nil, nil, YES); + } + catch (anException) + { + objj_exception_report(anException, aSourceFile); + } + + return []; +} diff --git a/Objective-J/runtime.js b/Objective-J/runtime.js new file mode 100644 index 0000000000..17231cf427 --- /dev/null +++ b/Objective-J/runtime.js @@ -0,0 +1,571 @@ +/* + * runtime.js + * Objective-J + * + * Created by Francisco Tolmasky. + * Copyright 2008, 280 North, Inc. + * + * 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 + */ + +var CLS_CLASS = 0x1, + CLS_META = 0x2, + CLS_INITIALIZED = 0x4, + CLS_INITIALIZING = 0x8; + +#define CHANGEINFO(aClass, aSetInfoMask, aClearInfoMask) aClass.info = (aClass.info | (aSetInfoMask)) & ~(aClearInfoMask) +#define GETINFO(aClass, anInfoMask) (aClass.info & (anInfoMask)) +#define SETINFO(aClass, anInfoMask) CHANGEINFO(aClass, anInfoMask, 0) +#define CLEARINFO(aClass, anInfoMask) CHANGEINFO(aClass, 0, anInfoMask) +#define ISMETA(aClass) (GETINFO(aClass, CLS_META)) +#define GETMETA(aClass) (ISMETA(aClass) ? aClass : aClass.isa) +#define ISINITIALIZED(aClass) GETINFO(GETMETA(aClass), CLS_INITIALIZED) + +function objj_ivar(/*String*/ aName, /*String*/ aType) +{ + this.name = aName; + this.type = aType; +} + +function objj_method(/*String*/ aName, /*IMP*/ anImplementation, /*String*/ types) +{ + this.name = aName; + this.method_imp = anImplementation; + this.types = types; +} + +function objj_class() +{ + this.isa = NULL; + + this.super_class = NULL; + this.sub_classes = []; + + this.name = NULL; + this.info = 0; + this.ivars = []; + + this.method_list = []; + this.method_hash = {}; + + this.method_store = function() { }; + this.method_dtable = this.method_store.prototype; + + this.allocator = function() { }; + this.__address = -1; +} + +function objj_object() +{ + this.isa = NULL; + this.__address = -1; +} + +// Addressing Objects + +var OBJECT_COUNT = 0; + +function _objj_generateObjectHash() +{ + return OBJECT_COUNT++; +} + +#define _objj_generateObjectHash() (OBJECT_COUNT++) + +// Working with Classes + +function class_getName(/*Class*/ aClass) +{ + if (aClass == Nil) + return ""; + + return aClass.name; +} + +function class_isMetaClass(/*Class*/ aClass) +{ + if (!aClass) + return NO; + + return ISMETA(aClass); +} + +function class_getSuperclass(/*Class*/ aClass) +{ + if (aClass == Nil) + return Nil; + + return aClass.super_class; +} + +function class_setSuperclass(/*Class*/ aClass, /*Class*/ aSuperClass) +{ + // FIXME: implement. +} + +function class_isMetaClass(/*Class*/ aClass) +{ + return ISMETA(aClass); +} + +function class_addIvar(/*Class*/ aClass, /*String*/ aName, /*String*/ aType) +{ + var thePrototype = aClass.allocator.prototype; + + if (typeof thePrototype[aName] != "undefined") + return NO; + + aClass.ivars.push(new objj_ivar(aName, aType)); + thePrototype[aName] = NULL; + + return YES; +} + +function class_addIvars(/*Class*/ aClass, /*Array*/ivars) +{ + var index = 0, + count = ivars.length, + thePrototype = aClass.allocator.prototype; + + for (; index < count; ++index) + { + var ivar = ivars[index], + name = ivar.name; + + if (typeof thePrototype[name] == "undefined") + { + aClass.ivars.push(ivar); + thePrototype[name] = NULL; + } + } +} + +function class_copyIvarList(/*Class*/ aClass) +{ + return aClass.ivars.slice(0); +} + +//#define class_copyIvarList(aClass) (aClass.ivars.slice(0)) + +function class_addMethod(/*Class*/ aClass, /*SEL*/ aName, /*IMP*/ anImplementation, /*String*/types) +{ + if (aClass.method_hash[aName]) + return NO; + + var method = new objj_method(aName, anImplementation, aType); + + aClass.method_list.push(method); + aClass.method_dtable[aName] = method; + + // FIXME: Should this be done here? + // If this is a root class... + if (!ISMETA(aClass) && GETMETA(aClass).isa == GETMETA(aClass)) + class_addMethods(GETMETA(aClass), methods); + + return YES; +} + +function class_addMethods(/*Class*/ aClass, /*Array*/ methods) +{ + var index = 0, + count = methods.length, + + method_list = aClass.method_list, + method_dtable = aClass.method_dtable; + + for (; index < count; ++index) + { + var method = methods[index]; + + if (aClass.method_hash[method.name]) + continue; + + method_list.push(method); + method_dtable[method.name] = method; + } + + // If this is a root class... + if (!ISMETA(aClass) && GETMETA(aClass).isa == GETMETA(aClass)) + class_addMethods(GETMETA(aClass), methods); +} + +function class_getInstanceMethod(/*Class*/ aClass, /*SEL*/ aSelector) +{ + if (!aClass || !aSelector) + return NULL; + + var method = aClass.method_dtable[aSelector]; + + return method ? method : NULL; +} + +function class_getClassMethod(/*Class*/ aClass, /*SEL*/ aSelector) +{ + if (!aClass || !aSelector) + return NULL; + + var method = GETMETA(aClass).method_dtable[aSelector]; + + return method ? method : NULL; +} + +function class_copyMethodList(/*Class*/ aClass) +{ + return aClass.method_list.slice(0); +} + +var _class_initialize = function(/*Class*/ aClass) +{ + var meta = GETMETA(aClass); + + if (GETINFO(aClass, CLS_META)) + aClass = objj_getClass(aClass.name); + + if (aClass.super_class && !ISINITIALIZED(aClass.super_class)) + _class_initialize(aClass.super_class); + + if (!GETINFO(meta, CLS_INITIALIZED) && !GETINFO(meta, CLS_INITIALIZING)) + { + SETINFO(meta, CLS_INITIALIZING); + + objj_msgSend(aClass, "initialize"); + + CHANGEINFO(meta, CLS_INITIALIZED, CLS_INITIALIZING); + } +} + +var _objj_forward = new objj_method("forward", function(self, _cmd) +{ + return objj_msgSend(self, "forward::", _cmd, arguments); +}); + +// I think this forward:: may need to be a common method, instead of defined in CPObject. +#define CLASS_GET_METHOD_IMPLEMENTATION(aMethodImplementation, aClass, aSelector)\ + if (!ISINITIALIZED(aClass))\ + _class_initialize(aClass);\ + \ + var method = aClass.method_dtable[aSelector];\ + \ + if (!method)\ + method = _objj_forward;\ + \ + aMethodImplementation = method.method_imp; + +function class_getMethodImplementation(/*Class*/ aClass, /*SEL*/ aSelector) +{ + CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aClass, aSelector); + + return implementation; +} + +// Adding Classes + +var GLOBAL_NAMESPACE = this, + REGISTERED_CLASSES = {}; + +function objj_allocateClassPair(/*Class*/ superclass, /*String*/ aName) +{ + var classObject = new objj_class(), + metaClassObject = new objj_class(), + rootClassObject = classObject; + + // If we don't have a superclass, we are the root class. + if (superclass) + { + rootClassObject = superclass; + + while (rootClassObject.superclass) + rootClassObject = rootClassObject.superclass; + + // Give our current allocator all the instance variables of our super class' allocator. + classObject.allocator.prototype = new superclass.allocator; + + // "Inheret" parent methods. + classObject.method_store.prototype = new superclass.method_store; + classObject.method_dtable = classObject.method_store.prototype; + + metaClassObject.method_store.prototype = new superclass.isa.method_store; + metaClassObject.method_dtable = metaClassObject.method_store.prototype; + + // Set up the actual class hierarchy. + classObject.super_class = superclass; + metaClassObject.super_class = superclass.isa; + } + else + classObject.allocator.prototype = new objj_object(); + + classObject.isa = metaClassObject; + classObject.name = aName; + classObject.info = CLS_CLASS; + classObject.__address = _objj_generateObjectHash(); + + metaClassObject.isa = rootClassObject.isa; + metaClassObject.name = aName; + metaClassObject.info = CLS_META; + metaClassObject.__address = _objj_generateObjectHash(); + + return classObject; +} + +function objj_registerClassPair(/*Class*/ aClass) +{ + GLOBAL_NAMESPACE[aClass.name] = aClass; + REGISTERED_CLASSES[aClass.name] = aClass; +} + +// Instantiating Classes + +function class_createInstance(/*Class*/ aClass) +{ + if (!aClass) + objj_exception_throw(new objj_exception(OBJJNilClassException, "*** Attempting to create object with Nil class.")); + + var object = new aClass.allocator; + + object.__address = _objj_generateObjectHash(); + object.isa = aClass; + + return object; +} + +// Opera 9.5.1 has a bug where prototypes "inheret" members from instances when "with" is used. +// Given that the Opera team is so fond of bug-testing instead of version-testing, we'll go +// ahead and do that. + +var prototype_bug = function() { } + +prototype_bug.prototype.member = false; + +with (new prototype_bug()) + member = true; + +// If the bug exists, go down the slow path. +if (new prototype_bug().member) +{ + +var fast_class_createInstance = class_createInstance; + +class_createInstance = function(/*Class*/ aClass) +{ + var object = fast_class_createInstance(aClass); + + if (object) + { + var theClass = object.isa, + actualClass = theClass; + + while (theClass) + { + var ivars = theClass.ivars; + count = ivars.length; + + while (count--) + object[ivars[count].name] = NULL; + + theClass = theClass.super_class; + } + + object.isa = actualClass; + } + + return object; +} + +} + +// Working with Instances + +function object_getClassName(/*id*/ anObject) +{ + if (!anObject) + return ""; + + var theClass = anObject.isa; + + return theClass ? class_getName(theClass) : ""; +} + +//objc_getClassList +function objj_lookUpClass(/*String*/ aName) +{ + var theClass = REGISTERED_CLASSES[aName]; + + return theClass ? theClass : Nil; +} + +function objj_getClass(/*String*/ aName) +{ + var theClass = REGISTERED_CLASSES[aName]; + + if (!theClass) + { + // class handler callback??? + } + + return theClass ? theClass : Nil; +} + +//objc_getRequiredClass +function objj_getMetaClass(/*String*/ aName) +{ + var theClass = objj_getClass(aName); + + return GETMETA(theClass); +} + +// Working with Instance Variables + +function ivar_getName(anIvar) +{ + return anIvar.name; +} + +function ivar_getTypeEncoding(anIvar) +{ + return anIvar.type; +} + +// Sending Messages + +function objj_msgSend(/*id*/ aReceiver, /*SEL*/ aSelector) +{ + if (aReceiver == nil) + return nil; + +#if DEBUG + objj_debug_backtrace.push("[" + GETMETA(aReceiver).name + " " + aSelector + "]"); + + try + { + var result = class_getMethodImplementation(aReceiver.isa, aSelector).apply(aReceiver, arguments); + } + catch (anException) + { + CPLog.error("Exception " + anException + " in [" + GETMETA(aReceiver).name + " " + aSelector + "]"); + objj_debug_print_backtrace(); + } + + objj_debug_backtrace.pop(); + + return result; +#else + CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aReceiver.isa, aSelector); + + return implementation.apply(aReceiver, arguments); +#endif +} + +function objj_msgSendSuper(/*id*/ aSuper, /*SEL*/ aSelector) +{ +#if DEBUG + objj_debug_backtrace.push("[" + GETMETA(aSuper.receiver).name + " " + aSelector + "]"); +#endif + var super_class = aSuper.super_class; + + arguments[0] = aSuper.receiver; + +#if !DEBUG + CLASS_GET_METHOD_IMPLEMENTATION(var implementation, super_class, aSelector); + + return implementation.apply(aSuper.receiver, arguments); +#else + try + { + var result = class_getMethodImplementation(super_class, aSelector).apply(aSuper.receiver, arguments); + } + catch (anException) + { + CPLog.error("Exception " + anException + " in [" + GETMETA(aSuper.receiver).name + " " + aSelector + "]"); + objj_debug_print_backtrace(); + } + + objj_debug_backtrace.pop(); + + return result; +#endif +} + +#if DEBUG +// FIXME: This could be much better. +var objj_debug_backtrace = []; + +function objj_debug_print_backtrace() +{ + CPLog.trace(objj_debug_backtrace_string()); +} + +function objj_debug_backtrace_string() +{ + var i = objj_debug_backtrace.length, + backtrace = ""; + + while (i--) + backtrace += objj_debug_backtrace[i] + "\n"; + + return backtrace; +} +#endif + +// Working with Methods + +function method_getName(/*Method*/ aMethod) +{ + return aMethod.name; +} + +function method_getImplementation(/*Method*/ aMethod) +{ + return aMethod.method_imp; +} + +function method_setImplementation(/*Method*/ aMethod, /*IMP*/ anImplementation) +{ + var oldImplementation = aMethod.method_imp; + + aMethod.method_imp = anImplementation; + + return oldImplementation; +} + +function method_exchangeImplementations(/*Method*/ lhs, /*Method*/ rhs) +{ + var lhs_imp = method_getImplementation(lhs), + rhs_imp = method_getImplementation(rhs); + + method_setImplementation(lhs, rhs_imp); + method_setImplementation(rhs, lhs_imp); +} + +// Working with Selectors + +function sel_getName(aSelector) +{ + return aSelector ? aSelector : ""; +} + +function sel_getUid(/*String*/ aName) +{ + return aName; +} + +function sel_isEqual(/*SEL*/ lhs, /*SEL*/ rhs) +{ + return lhs == rhs; +} + +function sel_registerName(aName) +{ + return aName; +} diff --git a/Objective-J/static.js b/Objective-J/static.js new file mode 100644 index 0000000000..c25353e484 --- /dev/null +++ b/Objective-J/static.js @@ -0,0 +1,70 @@ + +OBJJUnrecognizedFormatException = "OBJJUnrecognizedFormatException"; + +var STATIC_MAGIC_NUMBER = "@STATIC", + MARKER_PATH = "p", + MARKER_CODE = "c", + MARKER_IMPORT_STD = 'I', + MARKER_IMPORT_LOCAL = 'i'; + +var STATIC_EXTENSION = "sj"; + +function objj_preprocess_file(aFilePath, fileContents) +{ + // Preprocess contents into fragments. + var fragments = objj_preprocess(fileContents, { path:"/x" }, { path:aFilePath}), + index = 0, + count = fragments.length, + preprocessed = MARKER_PATH + ';' + aFilePath.length() + ';' + aFilePath; + + // Writer preprocessed fragments out. + for (; index < count; ++index) + { + var fragment = fragments[index]; + + if (IS_FILE(fragment)) + preprocessed += (IS_LOCAL(fragment) ? MARKER_IMPORT_LOCAL : MARKER_IMPORT_STD) + ';' + GET_PATH(fragment).length + ';' + GET_PATH(fragment); + else + preprocessed += MARKER_CODE + ';' + GET_CODE(fragment).length + ';' + GET_CODE(fragment); + } + + return preprocessed; +} + +function objj_decompile(aString, bundle) +{ + var stream = new objj_markedStream(aString); + + if (stream.magicNumber() != STATIC_MAGIC_NUMBER) + objj_exception_throw(new objj_exception(OBJJUnrecognizedFormatException, "*** Could not recognize executable code format.")); + + if (stream.version() != 1.0) + objj_exception_throw(new objj_exception(OBJJUnrecognizedFormatException, "*** Could not recognize executable code format.")); + + var file = NULL, + files = []; + + while (marker = stream.getMarker()) + { + var text = stream.getString(); + + switch (marker) + { + case MARKER_PATH: file = new objj_file(); + file.path = DIRECTORY(bundle.path) + text; + file.bundle = bundle; + file.fragments = []; + + files.push(file); + break; + case MARKER_CODE: file.fragments.push(fragment_create_code(text, bundle, file)); + break; + case MARKER_IMPORT_STD: file.fragments.push(fragment_create_file(text, bundle, NO, YES, file)); + break; + case MARKER_IMPORT_LOCAL: file.fragments.push(fragment_create_file(text, bundle, YES, YES, file)); + break; + } + } + + return files; +} diff --git a/README b/README new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Tools/Install/build.xml b/Tools/Install/build.xml new file mode 100644 index 0000000000..d4dc9ad453 --- /dev/null +++ b/Tools/Install/build.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tools/Install/install-tools b/Tools/Install/install-tools new file mode 100755 index 0000000000..5adacedabc --- /dev/null +++ b/Tools/Install/install-tools @@ -0,0 +1,59 @@ +#!/bin/sh + +usage() +{ + echo "$0 [--prefix install_directory]" + exit 1 +} + +INSTALL_DIR="/usr/local" + +# Parse parameters +while [ $# -ge 1 ]; do + case $1 in + --prefix) + INSTALL_DIR=$2 + shift + ;; + *) + usage + ;; + esac + shift +done + +mkdir -p $INSTALL_DIR/bin +mkdir -p $INSTALL_DIR/share + +cp -fR objj/ $INSTALL_DIR/share/objj + +ln -sf $INSTALL_DIR/share/objj/bin/objj $INSTALL_DIR/bin/objj +ln -sf $INSTALL_DIR/share/objj/bin/objjc $INSTALL_DIR/bin/objjc +ln -sf $INSTALL_DIR/share/objj/bin/steam $INSTALL_DIR/bin/steam +ln -sf $INSTALL_DIR/share/objj/bin/bake $INSTALL_DIR/bin/bake + +cat < + + +@implementation AppController : CPObject +{ +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], + contentView = [theWindow contentView]; + + var label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; + + [label setStringValue:@"Hello World!"]; + [label setFont:[CPFont boldSystemFontOfSize:24.0]]; + + [label sizeToFit]; + + [label setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin]; + [label setFrameOrigin:CGPointMake((CGRectGetWidth([contentView bounds]) - CGRectGetWidth([label frame])) / 2.0, (CGRectGetHeight([contentView bounds]) - CGRectGetHeight([label frame])) / 2.0)]; + + [contentView addSubview:label]; + + [theWindow orderFront:self]; + + // Uncomment the following line to turn on the standard menu bar. + //[CPMenu setMenuBarVisible:YES]; +} + +@end \ No newline at end of file diff --git a/Tools/NewApplication/Info.plist b/Tools/NewApplication/Info.plist new file mode 100644 index 0000000000..92ab8b53d5 --- /dev/null +++ b/Tools/NewApplication/Info.plist @@ -0,0 +1,12 @@ + + + + + CPApplicationDelegateClass + AppController + CPBundleName + Hello World + CPPrincipalClass + CPApplication + + diff --git a/Tools/NewApplication/build.xml b/Tools/NewApplication/build.xml new file mode 100644 index 0000000000..5a9377c2bd --- /dev/null +++ b/Tools/NewApplication/build.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tools/NewApplication/index.html b/Tools/NewApplication/index.html new file mode 100644 index 0000000000..61892d8f77 --- /dev/null +++ b/Tools/NewApplication/index.html @@ -0,0 +1,36 @@ + + + + + + + Hello World + + + + + + + + +
+
+ +
+
+ + + \ No newline at end of file diff --git a/Tools/NewApplication/main.j b/Tools/NewApplication/main.j new file mode 100755 index 0000000000..6357f21dc8 --- /dev/null +++ b/Tools/NewApplication/main.j @@ -0,0 +1,17 @@ +// +// main.j +// PhotoAlbum +// +// Created by Francisco Tolmasky on July 18th, 2008. +// Copyright 2005 - 2008, 280 North, Inc. All rights reserved. +// + +import +import + +import "AppController.j" + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +} diff --git a/Tools/NibApp/Info.plist b/Tools/NibApp/Info.plist new file mode 100644 index 0000000000..ca4dfaaab6 --- /dev/null +++ b/Tools/NibApp/Info.plist @@ -0,0 +1,25 @@ + + + + + CPBundleDocumentTypes + + + CFBundleTypeExtensions + + MyDocument + + CPBundleTypeName + MyDocument + CPBundleTypeRole + Editor + CPDocumentClass + MyDocument + + + CPBundleName + MyApplication + CPPrincipalClass + CPApplication + + diff --git a/Tools/NibApp/MyDocument.j b/Tools/NibApp/MyDocument.j new file mode 100644 index 0000000000..964c9ca487 --- /dev/null +++ b/Tools/NibApp/MyDocument.j @@ -0,0 +1,33 @@ +// +// Document.j +// Editor +// +// Created by Francisco Tolmasky on May 21, 2008. +// Copyright 2005 - 2008, 280 North, Inc. All rights reserved. +// + +import +import + + +@implementation MyDocument : CPDocument +{ + CPTextField _textField; +} + +- (void)windowControllerDidLoadNib:(CPWindowController)aWindowController +{ + [super windowControllerDidLoadNib:aWindowController]; + + var nib = [[CPNib alloc] initWithContentsOfURL:@"MainMenu.cib"]; + + var x = [nib instantiateNibWithExternalNameTable:nil]; + + [x setBackgroundColor:[CPColor blueColor]]; + + //alert([[x subviews] count]); + + [[[aWindowController window] contentView] addSubview:x]; +} + +@end diff --git a/Tools/NibApp/Resources/spinner.gif b/Tools/NibApp/Resources/spinner.gif new file mode 100644 index 0000000000..0d2567cd01 Binary files /dev/null and b/Tools/NibApp/Resources/spinner.gif differ diff --git a/Tools/NibApp/index.html b/Tools/NibApp/index.html new file mode 100644 index 0000000000..774cb7e915 --- /dev/null +++ b/Tools/NibApp/index.html @@ -0,0 +1,34 @@ + + + + + + + Loading... + + + + + + + + +
+ +
+ + + \ No newline at end of file diff --git a/Tools/NibApp/main.j b/Tools/NibApp/main.j new file mode 100755 index 0000000000..d92a85b6f9 --- /dev/null +++ b/Tools/NibApp/main.j @@ -0,0 +1,20 @@ +// +// main.j +// Editor +// +// Created by Francisco Tolmasky on May 21, 2008. +// Copyright 2005 - 2008, 280 North, Inc. All rights reserved. +// + +import +import + +import "MyDocument.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +} + +window.setTimeout(main, 0); \ No newline at end of file diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Info.plist b/Tools/SubEthaEdit/Objective-J.mode/Contents/Info.plist new file mode 100644 index 0000000000..380234629c --- /dev/null +++ b/Tools/SubEthaEdit/Objective-J.mode/Contents/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleGetInfoString + 1.0, Copyright Arcaeum, LLC + CFBundleIdentifier + SEEMode.Objective-J + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Objective-J + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + NSHumanReadableCopyright + © 2006 Arcaeum, LLC +http://www.codingmonkeys.de + + diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ChangeLog.txt b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ChangeLog.txt new file mode 100644 index 0000000000..8c5e05b33e --- /dev/null +++ b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ChangeLog.txt @@ -0,0 +1,25 @@ +Changes with Version 1.6 (SEE 2.5.1) + *) Uncomment script had wrong icon. + +Changes with Version 1.5 (SEE 2.5) + + *) Added Scripts: NSLog with Function Call, Insert Call To Super, Open Counterpart, Open File In Project, Open Xcode Project, Compile Using Xcode, Un/Comment Selected Lines, Lookup in AppKiDo. + *) AUTOCOMPLETE: added missing constants (AddressBook, NSError) + +Changes with Version 1.4 (SEE 2.3) + + *) SYMBOLS: Fixed SEE-490. + *) AUTOCOMPLETE: Added important Carbon keywords + +Changes with Version 1.3 + + *) SYNTAX: Added some missing Classes and Constants. HYR-587. + +Changes with Version 1.2 + + *) SYNTAX: Fixed preprocessor end regex. HYR-410. + +Changes with Version 1.1 + + *) SYNTAX: Changed Character to state to fix HYR-371. + *) Adding additional Cocoa classes, constants and functions diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/English.lproj/InfoPlist.strings b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000..baacc4cd01 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/English.lproj/InfoPlist.strings differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/English.lproj/Localizable.strings b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/English.lproj/Localizable.strings new file mode 100644 index 0000000000..70562d3ce8 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/English.lproj/Localizable.strings differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/German.lproj/InfoPlist.strings b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/German.lproj/InfoPlist.strings new file mode 100644 index 0000000000..20262ab290 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/German.lproj/InfoPlist.strings differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/German.lproj/Localizable.strings b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/German.lproj/Localizable.strings new file mode 100644 index 0000000000..b8c92b075e Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/German.lproj/Localizable.strings differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ModeSettings.xml b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ModeSettings.xml new file mode 100644 index 0000000000..ca848ad169 --- /dev/null +++ b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ModeSettings.xml @@ -0,0 +1,8 @@ + + + + + j + J + + diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/RegexSymbols.xml b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/RegexSymbols.xml new file mode 100644 index 0000000000..38256d22fa --- /dev/null +++ b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/RegexSymbols.xml @@ -0,0 +1,56 @@ + + + + + + { + } + + + + (^[ \t]*[-+][^(-;]*\([A-Za-z0-9 *_]*\)[A-Za-z0-9_ \t]+[^{;]*) + + (:) *\([^\)]*\) *[a-zA-Z0-9]* + \1 + ([\n\r\t]| +|\([^\)]*\)) + + + + + + (?<=[\n\r]|^)\s*function\s+([^{]*)(?=\s*\{) + + \([^\)]*\) + () + [\n\r] + + [ \t]+ + + + + + + (@implementation[^[:cntrl:]{]*) + + ([\n\r]| +) + + + + + + (@interface[^[:cntrl:]{]*|@protocol[^[:cntrl:]{]*) + + ([\n\r]| +) + + + + + + ^<<<<<<<[^=]*======[^>]*>>>>>>> + + .* + Versioning conflict! + + + + \ No newline at end of file diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/10-InsertNSLogWithFunctionCall.scpt b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/10-InsertNSLogWithFunctionCall.scpt new file mode 100644 index 0000000000..8174f7b1e1 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/10-InsertNSLogWithFunctionCall.scpt differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/20-InsertCallToSuper.scpt b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/20-InsertCallToSuper.scpt new file mode 100644 index 0000000000..392374083a Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/20-InsertCallToSuper.scpt differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/30-OpenCounterpart.scpt b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/30-OpenCounterpart.scpt new file mode 100644 index 0000000000..d17f9ddf70 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/30-OpenCounterpart.scpt differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/40-OpenFileInProject.scpt b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/40-OpenFileInProject.scpt new file mode 100644 index 0000000000..5b4d6a48b2 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/40-OpenFileInProject.scpt differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/45-OpenXcodeProject.scpt b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/45-OpenXcodeProject.scpt new file mode 100644 index 0000000000..6000a61d73 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/45-OpenXcodeProject.scpt differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/50-CompileUsingXcode.scpt b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/50-CompileUsingXcode.scpt new file mode 100644 index 0000000000..1b2f94c78c Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/50-CompileUsingXcode.scpt differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/60-un_comment_lines.scpt b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/60-un_comment_lines.scpt new file mode 100644 index 0000000000..6021abf934 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/60-un_comment_lines.scpt differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/Lookup in AppKiDo.scpt b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/Lookup in AppKiDo.scpt new file mode 100644 index 0000000000..db81cff3c4 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/Lookup in AppKiDo.scpt differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/shell/insert_super.py b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/shell/insert_super.py new file mode 100755 index 0000000000..c6917e613e --- /dev/null +++ b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/Scripts/shell/insert_super.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +"""Inserts a [super method] call suitable for the current method. + Inserted as a snippet with the incoming arguments used as defaults. + + The default behavior is to add brackets if there is no "[" to the left + of the caret (ignoring the tab trigger). This can be changed by modifying + the useBrackets line below. + + The command uses heuristics to find which method implementation is + being edited. It should be reasonably tolerant to various coding styles, + including different bracket and indentation styles. +""" + +import sys, os, re, itertools + +protoRe = re.compile(r"^\s*(\+|-)\s*\([^)]+\)((\n|[^{])*)[^;]\{", re.M) + +nlines = int(os.environ["LINE_NUMBER"])-1 +interestingLines = [l for (count, l) in itertools.takewhile(lambda (n, l): n <= nlines, enumerate(sys.stdin))] +invokationLine = interestingLines[-1] + +needsBracket = True +postfix = ";" + +protos = protoRe.findall(''.join(interestingLines)) + +if len(protos) == 0: + sys.exit(1) + +lastProtoSelWithTypes = re.sub(r'\s+', ' ', protos[-1][1]) + +counter = itertools.count(1) +def replFunc(match): + return '%s' % ( match.groups()[0]) + +methodCall = re.sub(r'\([^)]+\)\s*(([A-Za-z0-9_][A-Za-z0-9_]*))', replFunc, lastProtoSelWithTypes) + +brackets = needsBracket and ('[', ']') or ('', '') +outString = '%ssuper %s%s%s' % (brackets[0], methodCall.strip(), brackets[1], postfix) + +sys.stdout.write(outString) diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/SyntaxDefinition.xml b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/SyntaxDefinition.xml new file mode 100644 index 0000000000..e36d0813aa --- /dev/null +++ b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/SyntaxDefinition.xml @@ -0,0 +1,2833 @@ + + + + + + Objective-C + + + + + + + + + + + + (?<=[^\w\d]|^)(((([0-9]+\.[0-9]*)|(\.[0-9]+))([eE][+\-]?[0-9]+)?[fFlL]?)|((([1-9][0-9]*)|0[0-7]*|(0[xX][0-9a-fA-F]+))(([uU][lL]?)|([lL][uU]?))?))(?=[^\w\d]|$) + + + + ([A-Za-z0-9_]+ *)\( + + + + class + const + enum + export + extends + + abstract + boolean + byte + + debugger + + final + + goto + implements + + interface + + native + package + private + protected + public + + static + synchronized + throws + transient + volatile + + + + __proto__ + break + case + catch + continue + default + delete + do + else + false + finally + for + function + if + in + instanceof + new + null + prototype + return + switch + this + throw + true + try + typeof + var + while + with + + + + id + NULL + nil + Nil + void + self + super + YES + NO + IBAction + IBOutlet + int + long + float + double + char + unichar + unsigned + signed + BOOL + SEL + Class + short + + + + @class + @end + @implementation + @interface + @private + @protected + @protocol + @public + @defs + @encode + @selector + + + + [^:]\([^)]*\)[ ]*([A-Za-z0-9_]+)[ ]*[{;] + \)[ ]*([A-Za-z0-9_]+:) + ([A-Za-z0-9_]+:) + ([A-Za-z0-9_]+)] + [\-\+][ ]*\([A-Za-z0-9_]+\)[ ]*([A-Za-z0-9_]+) + + + + CPAccessibility + CPActionCell + CPAffineTransform + CPAppleEventDescriptor + CPAppleEventManager + CPAppleScript + CPApplication + CPArchiver + CPArray + CPAssertionHandler + CPAttributedString + CPAutoreleasePool + CPBezierPath + CPBitmapImageRep + CPBox + CPBrowser + CPBrowserCell + CPBundle + CPButton + CPButtonCell + CPCachedImageRep + CPCalendarDate + CPCell + CPChangeSpelling + CPCharacterSet + CPClassDescription + CPClipView + CPCloneCommand + CPCloseCommand + CPCoder + CPCoding + CPColor + CPColorList + CPColorPanel + CPColorPicker + CPColorPickingCustom + CPColorPickingDefault + CPColorWell + CPComboBox + CPComboBoxCell + CPComboBoxCellDataSource + CPComboBoxDataSource + CPComparisonMethods + CPConditionLock + CPConnection + CPControl + CPCopying + CPCountCommand + CPCountedSet + CPCreateCommand + CPCursor + CPCustomImageRep + CPData + CPDate + CPDateFormatter + CPDecimalNumber + CPDecimalNumberBehaviors + CPDecimalNumberHandler + CPDeleteCommand + CPDeserializer + CPDictionary + CPDirectoryEnumerator + CPDistantObject + CPDistantObjectRequest + CPDistributedLock + CPDistributedNotificationCenter + CPDocument + CPDocumentController + CPDraggingDestination + CPDraggingInfo + CPDraggingSource + CPDrawer + CPEPSImageRep + CPEnumerator + CPEvent + CPException + CPExistsCommand + CPFileHandle + CPFileManager + CPFileWrapper + CPFont + CPFontManager + CPFontPanel + CPForm + CPFormCell + CPFormatter + CPGetCommand + CPGlyphInfo + CPGraphicsContext + CPHelpManager + CPHost + CPIgnoreMisspelledWords + CPImageCell + CPImageRep + CPImageView + CPIndexSpecifier + CPImage + CPInputManager + CPInputServerMouseTracker + CPInputServiceProvider + CPInputServer + CPInvocation + CPKeyValueCoding + CPKeyedArchiver + CPKeyedUnarchiver + CPLayoutManager + CPLock + CPLocking + CPLogicalTest + CPMachBootstrapServer + CPMachPort + CPMatrix + CPMenu + CPMenuItem + CPMenuItemCell + CPMenuValidation + CPMenuView + CPMessagePort + CPMessagePortNameServer + CPMethodSignature + CPMiddleSpecifier + CPMoveCommand + CPMovie + CPMovieView + CPMutableArray + CPMutableAttributedString + CPMutableCharacterSet + CPMutableCopying + CPMutableData + CPMutableDictionary + CPMutableParagraphStyle + CPMutableSet + CPMutableString + CPNameSpecifier + CPNetService + CPNetServiceBrowser + CPNibAwaking + CPNibConnector + CPNibControlConnector + CPNibOutletConnector + CPNotificationCenter + CPNotificationQueue + CPNotification + CPNull + CPNumber + CPNumberFormatter + CPObjCTypeSerializationCallBack + CPObject + CPOpenGLContext + CPOpenGLPixelFormat + CPOpenGLView + CPOpenPanel + CPOutlineView + CPOutlineViewDataSource + CPPDFImageRep + CPPICTImageRep + CPPageLayout + CPPanel + CPParagraphStyle + CPPasteboard + CPPipe + CPPopUpButton + CPPopUpButtonCell + CPPort + CPPortCoder + CPPortMessage + CPPortNameServer + CPPositionalSpecifier + CPPrintInfo + CPPrintOperation + CPPrintPanel + CPPrinter + CPProcessInfo + CPProgressIndicator + CPPropertyListSerialization + CPPropertySpecifier + CPProtocolChecker + CPProxy + CPQuickDrawView + CPQuitCommand + CPRandomSpecifier + CPRangeSpecifier + CPRecursiveLock + CPRelativeSpecifier + CPResponder + CPRulerMarker + CPRulerView + CPRunLoop + CPSavePanel + CPScanner + CPScreen + CPScriptClassDescription + CPScriptCoercionHandler + CPScriptCommand + CPScriptCommandDescription + CPScriptExecutionContext + CPScriptKeyValueCoding + CPScriptObjectSpecifier + CPScriptObjectSpecifiers + CPScriptSuiteRegistry + CPScriptWhoseTest + CPScriptingComparisonMethods + CPScrollView + CPScroller + CPSecureTextField + CPSecureTextFieldCell + CPSerializer + CPServicesRequests + CPSet + CPSetCommand + CPSimpleHorizontalTypesetter + CPSlider + CPSliderCell + CPSocketPort + CPSocketPortNameServer + CPSound + CPSpecifierTest + CPSpellChecker + CPSpellServer + CPSplitView + CPStatusBar + CPStatusItem + CPStepper + CPStepperCell + CPString + CPTabView + CPTabViewItem + CPTableColumn + CPTableDataSource + CPTableHeaderCell + CPTableHeaderView + CPTableView + CPTask + CPText + CPTextAttachment + CPTextAttachmentCell + CPTextContainer + CPTextField + CPTextFieldCell + CPTextInput + CPTextStorage + CPTextStorageScripting + CPTextTab + CPTextView + CPThread + CPTimeZone + CPTimer + CPToolTipOwner + CPToolbar + CPToolbarItem + CPToolbarItemValidation + CPTypesetter + CPURL + CPURLHandle + CPURLHandleClient + CPUnarchiver + CPUndoManager + CPUniqueIDSpecifier + CPUserDefaults + CPUserInterfaceValidations + CPValidatedUserInterfaceItem + CPValue + CPView + CPWhoseSpecifier + CPWindow + CPWindowController + CPWindowScripting + CPWorkspace + + CPError + CPCachedURLResponse + CPHTTPCookie + CPHTTPCookieStorage + CPHTTPURLResponse + CPIndexSet + CPInputStream + CPMutableIndexSet + CPMutableURLRequest + CPOutputStream + CPSortDescriptor + CPStream + CPURLAuthenticationChallenge + CPURLCache + CPURLConnection + CPURLCredential + CPURLCredentialStorage + CPURLDownload + CPURLProtectionSpace + CPURLProtocol + CPURLRequest + CPURLResponse + CPValueTransformer + CPXMLParser + + ABActionDelegate + WebFrameLoadDelegate + WebPolicyDelegate + WebResourceLoadDelegate + ABImageClient + CPEditor + CPEditorRegistration + CPFontPanelValidation + CPGlyphStorage + CPKeyValueBindingCreation + CPPlaceholders + CPObjCTypeSerialization + CPJavaClassesForBundle + CPJavaClassesFromPath + CPJavaSetupVirtualMachine + CPCopyHashTableWithZone + CPCreateHashTable + CPCreateHashTableWithZone + CPEnumerateHashTable + CPFreeHashTable + CPHashInsert + CPHashInsertIfAbsent + CPHashInsertKnownAbsent + CPHashRemove + CPNextHashEnumeratorItem + CPResetHashTable + CPAllMapTableKeys + CPAllMapTableValues + CPCopyMapTableWithZone + CPCreateMapTable + CPCreateMapTableWithZone + CPEnumerateMapTable + CPFreeMapTable + CPMapGet + CPMapInsert + CPMapInsertIfAbsent + CPMapInsertKnownAbsent + CPMapMember + CPMapRemove + CPNextMapEnumeratorPair + CPResetMapTable + CPAllocateObject + CPCopyObject + CPDeallocateObject + CPDecrementExtraRefCountWasZero + CPExtraRefCount + CPIncrementExtraRefCount + CPDivideRect + CPGetUncaughtExceptionHandler + CPSetUncaughtExceptionHandler + CPAllocateMemoryPages + CPCopyMemoryPages + CPCreateZone + CPDeallocateMemoryPages + CPDefaultMallocZone + CPLogPageSize + CPPageSize + CPRecycleZone + CPRoundDownToMultipleOfPageSize + CPRoundUpToMultipleOfPageSize + CPSetZoneName + CPZoneCalloc + CPZoneFree + CPZoneMalloc + CPZoneName + CPZoneRealloc + CPDecNumberBehaviors + CPURLAuthenticationChallengeSender + CPURLClient + CPURLProtocolClient + WebDocumentRepresentation + WebDocumentSearching + WebOpenPanelResultListener + WebPolicyDecisionListener + WebUIDelegate + WebDocumentText + WebDocumentView + + CPATSTypesetter + CPAlert + CPAnimation + CPArrayController + CPAttributeDescription + CPCIImageRep + CPColorSpace + CPController + CPDatePicker + CPDatePickerCell + CPEntityDescription + CPFetchRequest + CPFetchedPropertyDescription + CPFontDescriptor + CPGlyphGenerator + CPLevelIndicator + CPLevelIndicatorCell + CPManagedObject + CPManagedObjectContext + CPManagedObjectID + CPManagedObjectModel + CPNib + CPObjectController + CPOpenGLPixelBuffer + CPPersistentDocument + CPPersistentStoreCoordinator + CPPropertyDescription + CPRelationshipDescription + CPSearchField + CPSearchFieldCell + CPSegmentedCell + CPSegmentedControl + CPShadow + CPSpeechRecognizer + CPSpeechSynthesizer + CPTextBlock + CPTextList + CPTextTable + CPTextTableBlock + CPTokenField + CPTokenFieldCell + CPTreeController + CPUserDefaultsController + CPViewAnimation + + CPCalendar + CPCoercionHandler + CPComparisonPredicate + CPCompoundPredicate + CPConstantString + CPDateComponents + CPExceptionHandler + CPExpression + CPIndexPath + CPJavaConfiguration + CPJavaVirtualMachine + CPLocale + CPMailDelivery + CPMetadataItem + CPMetadataQuery + CPMetadataQueryAttributeValueTuple + CPMetadataQueryResultGroup + CPObjectSpecifier + CPPredicate + CPPreferencePane + CPSimpleCString + CPWhoseTest + CPXMLDTD + CPXMLDTDNode + CPXMLDocument + CPXMLElement + CPXMLNode + + + + CPAffineTransformStruct + CPApplicationTerminateReply + CPBackingStoreType + CPBezelStyle + CPBezierPathElement + CPBitmapImageFileType + CPBorderType + CPBoxType + CPButtonType + CPCellAttribute + CPCellImagePosition + CPCellState + CPCellType + CPCharacterCollection + CPControlSize + CPControlTint + CPDocumentChangeType + CPDragOperation + CPDrawerState + CPEventType + CPFocusRingPlacement + CPFontAction + CPFontTraitMask + CPGlyph + CPGlyphInscription + CPGlyphLayoutMode + CPGlyphRelation + CPGradientType + CPImageAlignment + CPImageCacheMode + CPImageFrameStyle + CPImageInterpolation + CPImageLoadStatus + CPImageRepLoadStatus + CPImageScaling + CPInterfaceStyle + CPLayoutDirection + CPLayoutStatus + CPLineBreakMode + CPLineCapStyle + CPLineJoinStyle + CPLineMovementDirection + CPLineSweepDirection + CPMatrixMode + CPModalSession + CPMultibyteGlyphPacking + CPOpenGLContextAuxiliary + CPOpenGLContextParameter + CPOpenGLPixelFormatAttribute + CPOpenGLPixelFormatAuxiliary + CPPopUpArrowPosition + CPPrinterTableStatus + CPPrintingOrientation + CPPrintingPageOrder + CPPrintingPaginationMode + CPProgressIndicatorStyle + CPProgressIndicatorThickness + CPQTMovieLoopMode + CPRequestUserAttentionType + CPRulerOrientation + CPSaveOperationType + CPScreenAuxiliaryOpaque + CPScrollArrowPosition + CPScrollerArrow + CPScrollerPart + CPSelectionAffinity + CPSelectionDirection + CPSelectionGranularity + CPTabState + CPTabViewItemAuxiliaryOpaque + CPTabViewType + CPTableViewDropOperation + CPTextAlignment + CPTextFieldBezelStyle + CPTextTabType + CPTickMarkPosition + CPTIFFCompression + CPTitlePosition + CPToolbarDisplayMode + CPToolbarSizeMode + CPToolTipTag + CPTrackingRectTag + CPTypesetterBehavior + CPUsableScrollerParts + CPWindingRule + CPWindowAuxiliaryOpaque + CPWindowButton + CPWindowOrderingMode + CPWritingDirection + CPCalculationError + CPComparisonResult + CPHashEnumerator + CPHashTable + CPInsertionPosition + CPMapEnumerator + CPMapTable + CPNetServicesError + CPNotificationCoalescing + CPNotificationSuspensionBehavior + CPObjCValue + CPPoint + CPZeroPoint + CPPointArray + CPPointPointer + CPPostingStyle + CPPropertyListFormat + CPPropertyListMutabilityOptions + CPRange + CPRangePointer + CPRect + CPRectEdge + CPZeroRect + CPRectArray + CPRectPointer + CPRelativePosition + CPDecimal + CPDecimalMaxSize + CPRoundingMode + CPSaveOptions + CPSearchPathDomainMask + CPSize + CPZeroSize + CPSizeArray + CPSizePointer + CPSwappedDouble + CPSwappedFloat + CPTestComparisonOperation + CPTimeInterval + CPUncaughtExceptionHandler + CPURLHandleStatus + CPWhoseSubelementIdentifier + CPZone + CPNotFound + CPOpenStepUnicodeReservedBase + CPStringEncoding + CPCompositingOperation + + + + CPDecimalMaxSize + CPDecimalNoScale + CPMaximumStringLength + CPNotAnIntMapKey + CPNotAPointerMapKey + CPCocoaErrorDomain + CPURLErrorDomain + CFStreamErrorDomain + CPFilePathErrorKey + CPErrorFailingURLStringKey + CPPOSIXErrorDomain + CPOSStatusErrorDomain + CPMachErrorDomain + CPUnderlyingErrorKey + CPLocalizedDescriptionKey + CPLocalizedFailureReasonErrorKey + CPLocalizedRecoverySuggestionErrorKey + CPLocalizedRecoveryOptionsErrorKey + CPRecoveryAttempterErrorKey + CPStringEncodingErrorKey + CPURLErrorKey + CPFilePathErrorKey + CPURLCredentialStorageChangedNotification + CPUserDefaultsDidChangeNotification + CPWillBecomeMultiThreadedNotification + CPDidBecomeSingleThreadedNotification + CPThreadWillExitNotification + CPUndoManagerCheckpointNotification + CPUndoManagerWillUndoChangeNotification + CPUndoManagerWillRedoChangeNotification + CPUndoManagerDidUndoChangeNotification + CPUndoManagerDidRedoChangeNotification + CPUndoManagerDidOpenUndoGroupNotification + CPUndoManagerWillCloseUndoGroupNotification + CPPortDidBecomeInvalidNotification + CPTaskDidTerminateNotification + CPJavaWillSetupVirtualMachineNotification + CPJavaDidSetupVirtualMachineNotification + CPJavaWillCreateVirtualMachineNotification + CPJavaDidCreateVirtualMachineNotification + CPAppleEventManagerWillProcessFirstEventNotification + CPBundleDidLoadNotification + CPClassDescriptionNeededForClassNotification + CPConnectionDidDieNotification + CPConnectionDidInitializeNotification + CPFileHandleReadCompletionNotification + CPFileHandleReadToEndOfFileCompletionNotification + CPFileHandleConnectionAcceptedNotification + CPFileHandleDataAvailableNotification + CPHTTPCookieManagerAcceptPolicyChangedNotification + CPHTTPCookieManagerCookiesChangedNotification + BPF_MODE_DISABLED + BPF_MODE_INPUT + BPF_MODE_INPUT_OUTPUT + BPF_MODE_OUTPUT + DRFileForkData + DRFileForkResource + DRFilesystemInclusionMaskHFSPlus + DRFilesystemInclusionMaskISO9660 + DRFilesystemInclusionMaskJoliet + DRFilesystemInclusionMaskUDF + DRSetupPanelDeviceSelectionChangedNotification + DRSetupPanelSelectedDeviceKey + FFCAP_ET_CONSTANTFORCE + FFCAP_ET_CUSTOMFORCE + FFCAP_ET_DAMPER + FFCAP_ET_FRICTION + FFCAP_ET_INERTIA + FFCAP_ET_RAMPFORCE + FFCAP_ET_SAWTOOTHDOWN + FFCAP_ET_SAWTOOTHUP + FFCAP_ET_SINE + FFCAP_ET_SPRING + FFCAP_ET_SQUARE + FFCAP_ET_TRIANGLE + FFEFF_CARTESIAN + FFEFF_POLAR + FFEFF_SPHERICAL + IFNET_CSUM_FRAGMENT + IFNET_CSUM_IP + IFNET_CSUM_TCP + IFNET_CSUM_UDP + IFNET_FAMILY_ANY + IFNET_FAMILY_BOND + IFNET_FAMILY_DISC + IFNET_FAMILY_ETHERNET + IFNET_FAMILY_FAITH + IFNET_FAMILY_FIREWIRE + IFNET_FAMILY_GIF + IFNET_FAMILY_LOOPBACK + IFNET_FAMILY_MDECAP + IFNET_FAMILY_PPP + IFNET_FAMILY_PVC + IFNET_FAMILY_SLIP + IFNET_FAMILY_STF + IFNET_FAMILY_TUN + IFNET_FAMILY_VLAN + IFNET_IP_FRAGMENT + IFNET_VLAN_MTU + IFNET_VLAN_TAGGING + IMCapabilityAudioConference + IMCapabilityDirectIM + IMCapabilityFileSharing + IMCapabilityFileTransfer + IMCapabilityText + IMCapabilityVideoConference + IMPersonAVBusyKey + IMPersonCapabilitiesKey + IMPersonEmailKey + IMPersonFirstNameKey + IMPersonIdleSinceKey + IMPersonInfoChangedNotification + IMPersonLastNameKey + IMPersonPictureDataKey + IMPersonScreenNameKey + IMPersonServiceNameKey + IMPersonStatus + IMPersonStatusAvailable + IMPersonStatusAway + IMPersonStatusChangedNotification + IMPersonStatusIdle + IMPersonStatusKey + IMPersonStatusMessageKey + IMPersonStatusOffline + IMPersonStatusUnknown + IMServiceStatus + IMServiceStatusChangedNotification + IMServiceStatusDisconnected + IMServiceStatusLoggedIn + IMServiceStatusLoggedOut + IMServiceStatusLoggingIn + IMServiceStatusLoggingOut + IMStatusImagesChangedAppearanceNotification + ISyncChangePropertyActionKey + ISyncChangePropertyClear + ISyncChangePropertyNameKey + ISyncChangePropertySet + ISyncChangePropertyValueKey + ISyncChangeType + ISyncChangeTypeAdd + ISyncChangeTypeDelete + ISyncChangeTypeModify + ISyncClientTypeApplication + ISyncClientTypeDevice + ISyncClientTypePeer + ISyncClientTypeServer + ISyncInvalidEntityException + ISyncInvalidRecordException + ISyncInvalidRecordIdentifiersKey + ISyncInvalidRecordReasonsKey + ISyncInvalidRecordsKey + ISyncRecordEntityNameKey + ISyncServerUnavailableException + ISyncSessionCancelledException + ISyncSessionUnavailableException + ISyncStatus + ISyncStatusCancelled + ISyncStatusErrors + ISyncStatusFailed + ISyncStatusNever + ISyncStatusRunning + ISyncStatusSuccess + ISyncStatusWarnings + ISyncUnsupportedEntityException + InstallerDirectionBackward + InstallerDirectionForward + InstallerDirectionUndefined + MBUF_BCAST + MBUF_CSUM_DID_DATA + MBUF_CSUM_DID_IP + MBUF_CSUM_IP_GOOD + MBUF_CSUM_PSEUDO_HDR + MBUF_CSUM_REQ_IP + MBUF_CSUM_REQ_TCP + MBUF_CSUM_REQ_UDP + MBUF_DONTWAIT + MBUF_EOR + MBUF_EXT + MBUF_FIRSTFRAG + MBUF_FRAG + MBUF_LASTFRAG + MBUF_MCAST + MBUF_MT_ATABLE + MBUF_MT_CONTROL + MBUF_MT_DATA + MBUF_MT_FREE + MBUF_MT_FTABLE + MBUF_MT_HEADER + MBUF_MT_HTABLE + MBUF_MT_IFADDR + MBUF_MT_OOBDATA + MBUF_MT_PCB + MBUF_MT_RIGHTS + MBUF_MT_RTABLE + MBUF_MT_SOCKET + MBUF_MT_SONAME + MBUF_PKTHDR + MBUF_PROMISC + MBUF_WAITOK + CPAFMAscender + CPAFMCapHeight + CPAFMCharacterSet + CPAFMDescender + CPAFMEncodingScheme + CPAFMFamilyName + CPAFMFontName + CPAFMFormatVersion + CPAFMFullName + CPAFMItalicAngle + CPAFMMappingScheme + CPAFMNotice + CPAFMUnderlinePosition + CPAFMUnderlineThickness + CPAFMVersion + CPAFMWeight + CPAFMXHeight + CPASCIIStringEncoding + CPAWTEventType + CPAbortModalException + CPAbortPrintingException + CPAboveBottom + CPAboveTop + CPAccessibilityApplicationActivatedNotification + CPAccessibilityApplicationDeactivatedNotification + CPAccessibilityApplicationHiddenNotification + CPAccessibilityApplicationRole + CPAccessibilityApplicationShownNotification + CPAccessibilityAttributedStringForRangeParameterizedAttribute + CPAccessibilityBoundsForRangeParameterizedAttribute + CPAccessibilityBrowserRole + CPAccessibilityBusyIndicatorRole + CPAccessibilityButtonRole + CPAccessibilityCancelAction + CPAccessibilityCancelButtonAttribute + CPAccessibilityCheckBoxRole + CPAccessibilityChildrenAttribute + CPAccessibilityClearButtonAttribute + CPAccessibilityCloseButtonAttribute + CPAccessibilityCloseButtonSubrole + CPAccessibilityColorWellRole + CPAccessibilityColumnRole + CPAccessibilityColumnTitlesAttribute + CPAccessibilityColumnsAttribute + CPAccessibilityComboBoxRole + CPAccessibilityConfirmAction + CPAccessibilityContentsAttribute + CPAccessibilityDecrementAction + CPAccessibilityDecrementArrowSubrole + CPAccessibilityDecrementButtonAttribute + CPAccessibilityDecrementPageSubrole + CPAccessibilityDefaultButtonAttribute + CPAccessibilityDialogSubrole + CPAccessibilityDisclosedByRowAttribute + CPAccessibilityDisclosedRowsAttribute + CPAccessibilityDisclosingAttribute + CPAccessibilityDocumentAttribute + CPAccessibilityDrawerCreatedNotification + CPAccessibilityDrawerRole + CPAccessibilityEditedAttribute + CPAccessibilityEnabledAttribute + CPAccessibilityErrorCodeExceptionInfo + CPAccessibilityException + CPAccessibilityExpandedAttribute + CPAccessibilityFilenameAttribute + CPAccessibilityFloatingWindowSubrole + CPAccessibilityFocusedAttribute + CPAccessibilityFocusedUIElementAttribute + CPAccessibilityFocusedUIElementChangedNotification + CPAccessibilityFocusedWindowAttribute + CPAccessibilityFocusedWindowChangedNotification + CPAccessibilityFrontmostAttribute + CPAccessibilityGroupRole + CPAccessibilityGrowAreaAttribute + CPAccessibilityGrowAreaRole + CPAccessibilityHeaderAttribute + CPAccessibilityHelpAttribute + CPAccessibilityHiddenAttribute + CPAccessibilityHorizontalOrientationValue + CPAccessibilityHorizontalScrollBarAttribute + CPAccessibilityImageRole + CPAccessibilityIncrementAction + CPAccessibilityIncrementArrowSubrole + CPAccessibilityIncrementButtonAttribute + CPAccessibilityIncrementPageSubrole + CPAccessibilityIncrementorRole + CPAccessibilityLineForIndexParameterizedAttribute + CPAccessibilityLinkRole + CPAccessibilityListRole + CPAccessibilityMainAttribute + CPAccessibilityMainWindowAttribute + CPAccessibilityMainWindowChangedNotification + CPAccessibilityMatteContentUIElementAttribute + CPAccessibilityMatteRole + CPAccessibilityMaxValueAttribute + CPAccessibilityMenuBarAttribute + CPAccessibilityMenuBarRole + CPAccessibilityMenuButtonRole + CPAccessibilityMenuItemRole + CPAccessibilityMenuRole + CPAccessibilityMinValueAttribute + CPAccessibilityMinimizeButtonAttribute + CPAccessibilityMinimizeButtonSubrole + CPAccessibilityMinimizedAttribute + CPAccessibilityModalAttribute + CPAccessibilityNextContentsAttribute + CPAccessibilityNumberOfCharactersAttribute + CPAccessibilityOrientationAttribute + CPAccessibilityOutlineRole + CPAccessibilityOutlineRowSubrole + CPAccessibilityOverflowButtonAttribute + CPAccessibilityParentAttribute + CPAccessibilityPickAction + CPAccessibilityPopUpButtonRole + CPAccessibilityPositionAttribute + CPAccessibilityPressAction + CPAccessibilityPreviousContentsAttribute + CPAccessibilityProgressIndicatorRole + CPAccessibilityProxyAttribute + CPAccessibilityRTFForRangeParameterizedAttribute + CPAccessibilityRadioButtonRole + CPAccessibilityRadioGroupRole + CPAccessibilityRaiseAction + CPAccessibilityRangeForIndexParameterizedAttribute + CPAccessibilityRangeForLineParameterizedAttribute + CPAccessibilityRangeForPositionParameterizedAttribute + CPAccessibilityRoleAttribute + CPAccessibilityRoleDescriptionAttribute + CPAccessibilityRowRole + CPAccessibilityRowsAttribute + CPAccessibilityRulerRole + CPAccessibilityScrollAreaRole + CPAccessibilityScrollBarRole + CPAccessibilitySearchButtonAttribute + CPAccessibilitySearchFieldSubrole + CPAccessibilitySearchMenuAttribute + CPAccessibilitySecureTextFieldSubrole + CPAccessibilitySelectedAttribute + CPAccessibilitySelectedChildrenAttribute + CPAccessibilitySelectedColumnsAttribute + CPAccessibilitySelectedRowsAttribute + CPAccessibilitySelectedTextAttribute + CPAccessibilitySelectedTextRangeAttribute + CPAccessibilitySheetCreatedNotification + CPAccessibilitySheetRole + CPAccessibilityShownMenuAttribute + CPAccessibilitySizeAttribute + CPAccessibilitySliderRole + CPAccessibilitySortDirectionAttribute + CPAccessibilitySplitGroupRole + CPAccessibilitySplitterRole + CPAccessibilitySplittersAttribute + CPAccessibilityStandardWindowSubrole + CPAccessibilityStaticTextRole + CPAccessibilityStringForRangeParameterizedAttribute + CPAccessibilityStyleRangeForIndexParameterizedAttribute + CPAccessibilitySubroleAttribute + CPAccessibilitySystemDialogSubrole + CPAccessibilitySystemFloatingWindowSubrole + CPAccessibilitySystemWideRole + CPAccessibilityTabGroupRole + CPAccessibilityTableRole + CPAccessibilityTableRowSubrole + CPAccessibilityTabsAttribute + CPAccessibilityTextAreaRole + CPAccessibilityTextFieldRole + CPAccessibilityTitleAttribute + CPAccessibilityTitleUIElementAttribute + CPAccessibilityToolbarButtonAttribute + CPAccessibilityToolbarButtonSubrole + CPAccessibilityToolbarRole + CPAccessibilityTopLevelUIElementAttribute + CPAccessibilityUIElementDestroyedNotification + CPAccessibilityUnknownRole + CPAccessibilityUnknownSubrole + CPAccessibilityValueAttribute + CPAccessibilityValueChangedNotification + CPAccessibilityValueIndicatorRole + CPAccessibilityVerticalOrientationValue + CPAccessibilityVerticalScrollBarAttribute + CPAccessibilityVisibleCharacterRangeAttribute + CPAccessibilityVisibleChildrenAttribute + CPAccessibilityVisibleColumnsAttribute + CPAccessibilityVisibleRowsAttribute + CPAccessibilityWindowAttribute + CPAccessibilityWindowCreatedNotification + CPAccessibilityWindowDeminiaturizedNotification + CPAccessibilityWindowMiniaturizedNotification + CPAccessibilityWindowMovedNotification + CPAccessibilityWindowResizedNotification + CPAccessibilityWindowRole + CPAccessibilityWindowsAttribute + CPAccessibilityZoomButtonAttribute + CPAccessibilityZoomButtonSubrole + CPAddTraitFontAction + CPAdminApplicationDirectory + CPAdobeCNS + CPAdobeGB + CPAdobeJapan + CPAdobeKorea + CPAffectedObjectsErrorKey + CPAffectedStoresErrorKey + CPAlertAlternateReturn + CPAlertDefaultReturn + CPAlertErrorReturn + CPAlertFirstButtonReturn + CPAlertOtherReturn + CPAlertSecondButtonReturn + CPAlertThirdButtonReturn + CPAllApplicationsDirectory + CPAllDomainsMask + CPAllLibrariesDirectory + CPAllPredicateModifier + CPAllScrollerParts + CPAlphaFirstBitmapFormat + CPAlphaNonpremultipliedBitmapFormat + CPAlphaShiftKeyMask + CPAlternateKeyMask + CPAnchoredSearch + CPAndPredicateType + CPAnimationBlocking + CPAnimationEaseIn + CPAnimationEaseInOut + CPAnimationEaseOut + CPAnimationEffectDisappearingItemDefault + CPAnimationEffectPoof + CPAnimationLinear + CPAnimationNonblocking + CPAnimationNonblockingThreaded + CPAnyEventMask + CPAnyPredicateModifier + CPAnyType + CPAppKitDefined + CPAppKitDefinedMask + CPAppKitIgnoredException + CPAppKitVirtualMemoryException + CPAppleEventManagerSuspensionID + CPAppleEventTimeOutDefault + CPAppleEventTimeOutNone + CPApplicationActivatedEventType + CPApplicationDeactivatedEventType + CPApplicationDefined + CPApplicationDefinedMask + CPApplicationDelegateReplyCancel + CPApplicationDelegateReplyFailure + CPApplicationDelegateReplySuccess + CPApplicationDidBecomeActiveNotification + CPApplicationDidChangeScreenParametersNotification + CPApplicationDidFinishLaunchingNotification + CPApplicationDidHideNotification + CPApplicationDidResignActiveNotification + CPApplicationDidUnhideNotification + CPApplicationDidUpdateNotification + CPApplicationDirectory + CPApplicationFileType + CPApplicationSupportDirectory + CPApplicationWillBecomeActiveNotification + CPApplicationWillFinishLaunchingNotification + CPApplicationWillHideNotification + CPApplicationWillResignActiveNotification + CPApplicationWillTerminateNotification + CPApplicationWillUnhideNotification + CPApplicationWillUpdateNotification + CPArgumentEvaluationScriptError + CPArgumentsWrongScriptError + CPAscendingPageOrder + CPAsciiWithDoubleByteEUCGlyphPacking + CPAtBottom + CPAtTop + CPAttachmentAttributeName + CPAttachmentCharacter + CPAttributeType + CPAutoPagination + CPAutosaveOperation + CPBMPFileType + CPBackTabCharacter + CPBackgroundColorAttributeName + CPBackgroundTab + CPBackingStoreBuffered + CPBackingStoreNonretained + CPBackingStoreRetained + CPBackspaceCharacter + CPBacktabTextMovement + CPBackwardsSearch + CPBadBitmapParametersException + CPBadComparisonException + CPBadRTFColorTableException + CPBadRTFDirectiveException + CPBadRTFFontTableException + CPBadRTFStyleSheetException + CPBaselineOffsetAttributeName + CPBeginFunctionKey + CPBeginsWithComparison + CPBeginsWithPredicateOperatorType + CPBelowBottom + CPBelowTop + CPBevelLineJoinStyle + CPBezelBorder + CPBinaryDataAttributeType + CPBlueControlTint + CPBoldFontMask + CPBooleanAttributeType + CPBorderlessWindowMask + CPBottomTabsBezelBorder + CPBoxOldStyle + CPBoxPrimary + CPBoxSecondary + CPBoxSeparator + CPBreakFunctionKey + CPBrowserAutoColumnResizing + CPBrowserColumnConfigurationDidChangeNotification + CPBrowserIllegalDelegateException + CPBrowserNoColumnResizing + CPBrowserUserColumnResizing + CPButtLineCapStyle + CPCMYKColorSpaceModel + CPCMYKModeColorPanel + CPCachesDirectory + CPCalculationDivideByZero + CPCalculationLossOfPrecision + CPCalculationNoError + CPCalculationOverflow + CPCalculationUnderflow + CPCalibratedBlackColorSpace + CPCalibratedRGBColorSpace + CPCalibratedWhiteColorSpace + CPCancelButton + CPCancelTextMovement + CPCannotCreateScriptCommandError + CPCarriageReturnCharacter + CPCascadeDeleteRule + CPCaseInsensitiveSearch + CPCellAllowsMixedState + CPCellChangesContents + CPCellDisabled + CPCellEditable + CPCellHasImageHorizontal + CPCellHasImageOnLeftOrBottom + CPCellHasOverlappingImage + CPCellHighlighted + CPCellIsBordered + CPCellIsInsetButton + CPCellLightsByBackground + CPCellLightsByContents + CPCellLightsByGray + CPCenterTabStopType + CPCenterTextAlignment + CPChangeAutosaved + CPChangeBackgroundCell + CPChangeBackgroundCellMask + CPChangeCleared + CPChangeDone + CPChangeGrayCell + CPChangeGrayCellMask + CPChangeReadOtherContents + CPChangeUndone + CPCharacterShapeAttributeName + CPCircularBezelStyle + CPCircularSlider + CPClearControlTint + CPClearDisplayFunctionKey + CPClearLineFunctionKey + CPClipPagination + CPClockAndCalendarDatePickerStyle + CPClosableWindowMask + CPClosePathBezierPathElement + CPColorListDidChangeNotification + CPColorListIOException + CPColorListModeColorPanel + CPColorListNotEditableException + CPColorPanelAllModesMask + CPColorPanelCMYKModeMask + CPColorPanelColorDidChangeNotification + CPColorPanelColorListModeMask + CPColorPanelCrayonModeMask + CPColorPanelCustomPaletteModeMask + CPColorPanelGrayModeMask + CPColorPanelHSBModeMask + CPColorPanelRGBModeMask + CPColorPanelWheelModeMask + CPColorPboardType + CPComboBoxSelectionDidChangeNotification + CPComboBoxSelectionIsChangingNotification + CPComboBoxWillDismissNotification + CPComboBoxWillPopUpNotification + CPCommandKeyMask + CPCompositeClear + CPCompositeCopy + CPCompositeDestinationAtop + CPCompositeDestinationIn + CPCompositeDestinationOut + CPCompositeDestinationOver + CPCompositeHighlight + CPCompositePlusDarker + CPCompositePlusLighter + CPCompositeSourceAtop + CPCompositeSourceIn + CPCompositeSourceOut + CPCompositeSourceOver + CPCompositeXOR + CPCompressedFontMask + CPCondensedFontMask + CPConstantValueExpressionType + CPContainerSpecifierError + CPContainsComparison + CPContentsCellMask + CPContextHelpModeDidActivateNotification + CPContextHelpModeDidDeactivateNotification + CPContinuousCapacityLevelIndicatorStyle + CPControlGlyph + CPControlKeyMask + CPControlTextDidBeginEditingNotification + CPControlTextDidChangeNotification + CPControlTextDidEndEditingNotification + CPControlTintDidChangeNotification + CPCoreServiceDirectory + CPCrayonModeColorPanel + CPCriticalAlertStyle + CPCriticalRequest + CPCursorAttributeName + CPCursorPointingDevice + CPCursorUpdate + CPCursorUpdateMask + CPCurveToBezierPathElement + CPCustomColorSpace + CPCustomPaletteModeColorPanel + CPCustomSelectorPredicateOperatorType + CPDateAttributeType + CPDateFormatterBehavior + CPDateFormatterBehaviorDefault + CPDateFormatterFullStyle + CPDateFormatterLongStyle + CPDateFormatterMediumStyle + CPDateFormatterNoStyle + CPDateFormatterShortStyle + CPDayCalendarUnit + CPDecimalAttributeType + CPDecimalTabStopType + CPDefaultControlTint + CPDefaultTokenStyle + CPDeleteCharFunctionKey + CPDeleteCharacter + CPDeleteFunctionKey + CPDeleteLineFunctionKey + CPDeleteRule + CPDemoApplicationDirectory + CPDenyDeleteRule + CPDescendingPageOrder + CPDesktopDirectory + CPDetailedErrorsKey + CPDeveloperApplicationDirectory + CPDeveloperDirectory + CPDeviceBitsPerSample + CPDeviceBlackColorSpace + CPDeviceCMYKColorSpace + CPDeviceColorSpaceName + CPDeviceIsPrinter + CPDeviceIsScreen + CPDeviceNColorSpaceModel + CPDeviceRGBColorSpace + CPDeviceResolution + CPDeviceSize + CPDeviceWhiteColorSpace + CPDirectPredicateModifier + CPDirectSelection + CPDirectoryFileType + CPDisclosureBezelStyle + CPDiscreteCapacityLevelIndicatorStyle + CPDisplayWindowRunLoopOrdering + CPDocFormatTextDocumentType + CPDocModalWindowMask + CPDocumentDirectory + CPDocumentationDirectory + CPDoubleAttributeType + CPDoubleType + CPDownArrowFunctionKey + CPDownTextMovement + CPDragOperationAll + CPDragOperationCopy + CPDragOperationDelete + CPDragOperationEvery + CPDragOperationGeneric + CPDragOperationLink + CPDragOperationMove + CPDragOperationNone + CPDragOperationPrivate + CPDragPboard + CPDraggingException + CPDrawerClosedState + CPDrawerClosingState + CPDrawerDidCloseNotification + CPDrawerDidOpenNotification + CPDrawerOpenState + CPDrawerOpeningState + CPDrawerWillCloseNotification + CPDrawerWillOpenNotification + CPEndFunctionKey + CPEndsWithComparison + CPEndsWithPredicateOperatorType + CPEnterCharacter + CPEqualToComparison + CPEqualToPredicateOperatorType + CPEraCalendarUnit + CPEraDatePickerElementFlag + CPEraserPointingDevice + CPEvaluatedObjectExpressionType + CPEvenOddWindingRule + CPEventTrackingRunLoopMode + CPEverySubelement + CPExecuteFunctionKey + CPExpandedFontMask + CPExpansionAttributeName + CPF + CPFPCurrentField + CPFPPreviewButton + CPFPPreviewField + CPFPRevertButton + CPFPSetButton + CPFPSizeField + CPFPSizeTitle + CPFaxButton + CPFileAppendOnly + CPFileBusy + CPFileContentsPboardType + CPFileCreationDate + CPFileDeviceIdentifier + CPFileExtensionHidden + CPFileGroupOwnerAccountID + CPFileGroupOwnerAccountName + CPFileHFSCreatorCode + CPFileHFSTypeCode + CPFileHandlingPanelCancelButton + CPFileHandlingPanelOKButton + CPFileImmutable + CPFileModificationDate + CPFileOwnerAccountID + CPFileOwnerAccountName + CPFilePosixPermissions + CPFileReferenceCount + CPFileSize + CPFileSystemFileNumber + CPFileSystemFreeNodes + CPFileSystemFreeSize + CPFileSystemNodes + CPFileSystemNumber + CPFileSystemSize + CPFileType + CPFileTypeBlockSpecial + CPFileTypeCharacterSpecial + CPFileTypeDirectory + CPFileTypeRegular + CPFileTypeSocket + CPFileTypeSymbolicLink + CPFileTypeUnknown + CPFilenamesPboardType + CPFilesPromisePboardType + CPFilesystemFileType + CPFindFunctionKey + CPFindPanelActionNext + CPFindPanelActionPrevious + CPFindPanelActionReplace + CPFindPanelActionReplaceAll + CPFindPanelActionReplaceAllInSelection + CPFindPanelActionReplaceAndFind + CPFindPanelActionSelectAll + CPFindPanelActionSelectAllInSelection + CPFindPanelActionSetFindString + CPFindPanelActionShowFindPanel + CPFindPboard + CPFitPagination + CPFixedPitchFontMask + CPFlagsChanged + CPFlagsChangedMask + CPFloatAttributeType + CPFloatType + CPFloatingPointSamplesBitmapFormat + CPFocusRingAbove + CPFocusRingBelow + CPFocusRingOnly + CPFocusRingTypeDefault + CPFocusRingTypeExterior + CPFocusRingTypeNone + CPFontAntialiasedIntegerAdvancementsRenderingMode + CPFontAntialiasedRenderingMode + CPFontAttributeName + CPFontCascadeListAttribute + CPFontCharacterSetAttribute + CPFontClarendonSerifsClass + CPFontCollectionApplicationOnlyMask + CPFontColorAttribute + CPFontDefaultRenderingMode + CPFontFaceAttribute + CPFontFamilyAttribute + CPFontFixedAdvanceAttribute + CPFontFreeformSerifsClass + CPFontIntegerAdvancementsRenderingMode + CPFontMatrixAttribute + CPFontModernSerifsClass + CPFontNameAttribute + CPFontOldStyleSerifsClass + CPFontOrnamentalsClass + CPFontPanelAllModesMask + CPFontPanelCollectionModeMask + CPFontPanelFaceModeMask + CPFontPanelSizeModeMask + CPFontPanelStandardModesMask + CPFontPboard + CPFontPboardType + CPFontSansSerifClass + CPFontScriptsClass + CPFontSizeAttribute + CPFontSlabSerifsClass + CPFontSlantTrait + CPFontSymbolicClass + CPFontSymbolicTrait + CPFontTraitsAttribute + CPFontTransitionalSerifsClass + CPFontUnavailableException + CPFontUnknownClass + CPFontVariationAttribute + CPFontVariationAxisDefaultValueKey + CPFontVariationAxisIdentifierKey + CPFontVariationAxisMaximumValueKey + CPFontVariationAxisMinimumValueKey + CPFontVariationAxisNameKey + CPFontVisibleNameAttribute + CPFontWeightTrait + CPFontWidthTrait + CPForegroundColorAttributeName + CPFormFeedCharacter + CPFoundationVersionWithFileManagerResourceForkSupport + CPFourByteGlyphPacking + CPFunctionExpressionType + CPFunctionKeyMask + CPGIFFileType + CPGeneralPboard + CPGlobalDomain + CPGlyphAbove + CPGlyphAttributeBidiLevel + CPGlyphAttributeElastic + CPGlyphAttributeInscribe + CPGlyphAttributeSoft + CPGlyphBelow + CPGlyphInfoAttributeName + CPGlyphInscribeAbove + CPGlyphInscribeBase + CPGlyphInscribeBelow + CPGlyphInscribeOverBelow + CPGlyphInscribeOverstrike + CPGlyphLayoutAgainstAPoint + CPGlyphLayoutAtAPoint + CPGlyphLayoutWithPrevious + CPGradientConcaveStrong + CPGradientConcaveWeak + CPGradientConvexStrong + CPGradientConvexWeak + CPGradientNone + CPGraphicsContextDestinationAttributeName + CPGraphicsContextPDFFormat + CPGraphicsContextPSFormat + CPGraphicsContextRepresentationFormatAttributeName + CPGraphiteControlTint + CPGrayColorSpaceModel + CPGrayModeColorPanel + CPGreaterThanComparison + CPGreaterThanOrEqualToComparison + CPGreaterThanOrEqualToPredicateOperatorType + CPGreaterThanPredicateOperatorType + CPGrooveBorder + CPHFSFileTypes + CPHPUXOperatingSystem + CPHSBModeColorPanel + CPHTMLPboardType + CPHTMLTextDocumentType + CPHTTPCookieAcceptPolicyAlways + CPHTTPCookieAcceptPolicyNever + CPHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain + CPHandleOtherExceptionMask + CPHandleTopLevelExceptionMask + CPHandleUncaughtExceptionMask + CPHandleUncaughtRuntimeErrorMask + CPHandleUncaughtSystemExceptionMask + CPHangOnOtherExceptionMask + CPHangOnTopLevelExceptionMask + CPHangOnUncaughtExceptionMask + CPHangOnUncaughtRuntimeErrorMask + CPHangOnUncaughtSystemExceptionMask + CPHashTableCallBacks + CPHeavierFontAction + CPHelpButtonBezelStyle + CPHelpFunctionKey + CPHelpKeyMask + CPHighlightModeMatrix + CPHomeFunctionKey + CPHorizontalRuler + CPHourCalendarUnit + CPISO + CPISOLatin + CPIdentityMappingCharacterCollection + CPIllegalSelectorException + CPIllegalTextMovement + CPImageAbove + CPImageAlignBottom + CPImageAlignBottomLeft + CPImageAlignBottomRight + CPImageAlignCenter + CPImageAlignLeft + CPImageAlignRight + CPImageAlignTop + CPImageAlignTopLeft + CPImageAlignTopRight + CPImageBelow + CPImageCacheAlways + CPImageCacheBySize + CPImageCacheDefault + CPImageCacheException + CPImageCacheNever + CPImageCellType + CPImageColorSyncProfileData + CPImageCompressionFactor + CPImageCompressionMethod + CPImageCurrentFrame + CPImageCurrentFrameDuration + CPImageDitherTransparency + CPImageFrameButton + CPImageFrameCount + CPImageFrameGrayBezel + CPImageFrameGroove + CPImageFrameNone + CPImageFramePhoto + CPImageInterlaced + CPImageInterpolationDefault + CPImageInterpolationHigh + CPImageInterpolationLow + CPImageInterpolationNone + CPImageLeft + CPImageLoadStatusCancelled + CPImageLoadStatusCompleted + CPImageLoadStatusInvalidData + CPImageLoadStatusReadError + CPImageLoadStatusUnexpectedEOF + CPImageLoopCount + CPImageOnly + CPImageOverlaps + CPImageRGBColorTable + CPImageRepLoadStatusCompleted + CPImageRepLoadStatusInvalidData + CPImageRepLoadStatusReadingHeader + CPImageRepLoadStatusUnexpectedEOF + CPImageRepLoadStatusUnknownType + CPImageRepLoadStatusWillNeedAllData + CPImageRepMatchesDevice + CPImageRepRegistryDidChangeNotification + CPImageRight + CPInPredicateOperatorType + CPIndexSubelement + CPInformationalAlertStyle + CPInformationalRequest + CPInsertCharFunctionKey + CPInsertFunctionKey + CPInsertLineFunctionKey + CPIntHashCallBacks + CPIntMapKeyCallBacks + CPIntMapValueCallBacks + CPIntType + CPInteger16AttributeType + CPInteger32AttributeType + CPInteger64AttributeType + CPInteger + CPInterfaceStyleDefault + CPInternalScriptError + CPInternalSpecifierError + CPInvalidIndexSpecifierError + CPItalicFontMask + CPJPEG + CPJPEGFileType + CPJapaneseEUCGlyphPacking + CPJapaneseEUCStringEncoding + CPJustifiedTextAlignment + CPKernAttributeName + CPKeyDown + CPKeyDownMask + CPKeyPathExpressionType + CPKeySpecifierEvaluationScriptError + CPKeyUp + CPKeyUpMask + CPKeyValueChangeInsertion + CPKeyValueChangeRemoval + CPKeyValueChangeReplacement + CPKeyValueChangeSetting + CPKeyValueIntersectSetMutation + CPKeyValueMinusSetMutation + CPKeyValueObservingOptionNew + CPKeyValueObservingOptionOld + CPKeyValueSetSetMutation + CPKeyValueUnionSetMutation + CPL + CPLABColorSpaceModel + CPLError + CPLPrepareRequest + CPLStandardGetURL + CPLandscapeOrientation + CPLayoutCantFit + CPLayoutDone + CPLayoutLeftToRight + CPLayoutNotDone + CPLayoutOutOfGlyphs + CPLayoutRightToLeft + CPLeftArrowFunctionKey + CPLeftMouseDown + CPLeftMouseDownMask + CPLeftMouseDragged + CPLeftMouseDraggedMask + CPLeftMouseUp + CPLeftMouseUpMask + CPLeftTabStopType + CPLeftTabsBezelBorder + CPLeftTextAlignment + CPLeftTextMovement + CPLessThanComparison + CPLessThanOrEqualToComparison + CPLessThanOrEqualToPredicateOperatorType + CPLessThanPredicateOperatorType + CPLibraryDirectory + CPLigatureAttributeName + CPLighterFontAction + CPLikePredicateOperatorType + CPLineBorder + CPLineBreakByCharWrapping + CPLineBreakByClipping + CPLineBreakByTruncatingHead + CPLineBreakByTruncatingMiddle + CPLineBreakByTruncatingTail + CPLineBreakByWordWrapping + CPLineDoesntMove + CPLineMovesDown + CPLineMovesLeft + CPLineMovesRight + CPLineMovesUp + CPLineSeparatorCharacter + CPLineSweepDown + CPLineSweepLeft + CPLineSweepRight + CPLineSweepUp + CPLineToBezierPathElement + CPLinearSlider + CPLinkAttributeName + CPListModeMatrix + CPLiteralSearch + CPLocalDomainMask + CPLocalizedDescriptionKey + CPLogOtherExceptionMask + CPLogTopLevelExceptionMask + CPLogUncaughtExceptionMask + CPLogUncaughtRuntimeErrorMask + CPLogUncaughtSystemExceptionMask + CPM + CPMACHOperatingSystem + CPMacOSRomanStringEncoding + CPMacSimpleTextDocumentType + CPMachErrorDomain + CPMacintoshInterfaceStyle + CPManagedObjectContextLockingError + CPManagedObjectExternalRelationshipError + CPManagedObjectMergeError + CPManagedObjectReferentialIntegrityError + CPManagedObjectValidationError + CPMapTableKeyCallBacks + CPMapTableValueCallBacks + CPMatchesPredicateOperatorType + CPMaxXEdge + CPMaxYEdge + CPMenuDidAddItemNotification + CPMenuDidChangeItemNotification + CPMenuDidEndTrackingNotification + CPMenuDidRemoveItemNotification + CPMenuDidSendActionNotification + CPMenuFunctionKey + CPMenuWillSendActionNotification + CPMiddleSubelement + CPMinXEdge + CPMinYEdge + CPMiniControlSize + CPMiniaturizableWindowMask + CPMinuteCalendarUnit + CPMiterLineJoinStyle + CPMixedState + CPModalPanelRunLoopMode + CPModeSwitchFunctionKey + CPMomentaryChangeButton + CPMomentaryLight + CPMomentaryLightButton + CPMomentaryPushButton + CPMomentaryPushInButton + CPMonthCalendarUnit + CPMouseEntered + CPMouseEnteredMask + CPMouseExited + CPMouseExitedMask + CPMouseMoved + CPMouseMovedMask + CPMouseTracker + CPMoveToBezierPathElement + CPNEXTSTEPStringEncoding + CPNamedColorSpace + CPNarrowFontMask + CPNativeShortGlyphPacking + CPNaturalTextAlignment + CPNetServicesActivityInProgress + CPNetServicesBadArgumentError + CPNetServicesCancelledError + CPNetServicesCollisionError + CPNetServicesInvalidError + CPNetServicesNotFoundError + CPNetServicesTimeoutError + CPNetServicesUnknownError + CPNetworkDomainMask + CPNewNotifyingTextView + CPNewlineCharacter + CPNextFunctionKey + CPNextStepInterfaceStyle + CPNibLoadingException + CPNibOwner + CPNibTopLevelObjects + CPNoActionDeleteRule + CPNoBorder + CPNoCellMask + CPNoFontChangeAction + CPNoImage + CPNoInterfaceStyle + CPNoScriptError + CPNoScrollerParts + CPNoSpecifierError + CPNoSubelement + CPNoTabsBezelBorder + CPNoTabsLineBorder + CPNoTabsNoBorder + CPNoTitle + CPNoTopLevelContainersSpecifierError + CPNoUnderlineStyle + CPNonLossyASCIIStringEncoding + CPNonOwnedPointerHashCallBacks + CPNonOwnedPointerMapKeyCallBacks + CPNonOwnedPointerMapValueCallBacks + CPNonOwnedPointerOrNullMapKeyCallBacks + CPNonRetainedObjectHashCallBacks + CPNonRetainedObjectMapKeyCallBacks + CPNonRetainedObjectMapValueCallBacks + CPNonStandardCharacterSetFontMask + CPNonZeroWindingRule + CPNonactivatingPanelMask + CPNotEqualToPredicateOperatorType + CPNotPredicateType + CPNotificationCoalescingOnName + CPNotificationCoalescingOnSender + CPNotificationDeliverImmediately + CPNotificationNoCoalescing + CPNotificationPostToAllSessions + CPNotificationSuspensionBehaviorCoalesce + CPNotificationSuspensionBehaviorDeliverImmediately + CPNotificationSuspensionBehaviorDrop + CPNotificationSuspensionBehaviorHold + CPNullCellType + CPNullGlyph + CPNullifyDeleteRule + CPNumberFormatterBehavior10_0 + CPNumberFormatterBehavior10_4 + CPNumberFormatterBehavior + CPNumberFormatterBehaviorDefault + CPNumberFormatterCurrencyStyle + CPNumberFormatterDecimalStyle + CPNumberFormatterNoStyle + CPNumberFormatterPadAfterPrefix + CPNumberFormatterPadAfterSuffix + CPNumberFormatterPadBeforePrefix + CPNumberFormatterPadBeforeSuffix + CPNumberFormatterPercentStyle + CPNumberFormatterRoundCeiling + CPNumberFormatterRoundDown + CPNumberFormatterRoundFloor + CPNumberFormatterRoundHalfDown + CPNumberFormatterRoundHalfEven + CPNumberFormatterRoundHalfUp + CPNumberFormatterRoundUp + CPNumberFormatterScientificStyle + CPNumberFormatterSpellOutStyle + CPNumericPadKeyMask + CPNumericSearch + CPOKButton + CPOSF + CPOSStatusErrorDomain + CPObjCArrayType + CPObjCBitfield + CPObjCBoolType + CPObjCCharType + CPObjCDoubleType + CPObjCFloatType + CPObjCLongType + CPObjCLonglongType + CPObjCNoType + CPObjCObjectType + CPObjCPointerType + CPObjCSelectorType + CPObjCShortType + CPObjCStringType + CPObjCStructType + CPObjCUnionType + CPObjCVoidType + CPObjectHashCallBacks + CPObjectMapKeyCallBacks + CPObjectMapValueCallBacks + CPObliquenessAttributeName + CPOffState + CPOldNotifyingTextView + CPOldSelectedCharacterRange + CPOnOffButton + CPOnState + CPOneByteGlyphPacking + CPOnlyScrollerArrows + CPOpenGLCPRasterizationEnable + CPOpenGLCPStateValidation + CPOpenGLCPSurfaceOpacity + CPOpenGLCPSurfaceOrder + CPOpenGLCPSwapInterval + CPOpenGLCPSwapRectangle + CPOpenGLCPSwapRectangleEnable + CPOpenGLGOClearFormatCache + CPOpenGLGOFormatCacheSize + CPOpenGLGOResetLibrary + CPOpenGLGORetainRenderers + CPOpenGLPFAAccelerated + CPOpenGLPFAAccumSize + CPOpenGLPFAAllRenderers + CPOpenGLPFAAlphaSize + CPOpenGLPFAAuxBuffers + CPOpenGLPFAAuxDepthStencil + CPOpenGLPFABackingStore + CPOpenGLPFAClosestPolicy + CPOpenGLPFAColorFloat + CPOpenGLPFAColorSize + CPOpenGLPFACompliant + CPOpenGLPFADepthSize + CPOpenGLPFADoubleBuffer + CPOpenGLPFAFullScreen + CPOpenGLPFAMPSafe + CPOpenGLPFAMaximumPolicy + CPOpenGLPFAMinimumPolicy + CPOpenGLPFAMultiScreen + CPOpenGLPFAMultisample + CPOpenGLPFANoRecovery + CPOpenGLPFAOffScreen + CPOpenGLPFAPixelBuffer + CPOpenGLPFARendererID + CPOpenGLPFARobust + CPOpenGLPFASampleAlpha + CPOpenGLPFASampleBuffers + CPOpenGLPFASamples + CPOpenGLPFAScreenMask + CPOpenGLPFASingleRenderer + CPOpenGLPFAStencilSize + CPOpenGLPFAStereo + CPOpenGLPFASupersample + CPOpenGLPFAVirtualScreenCount + CPOpenGLPFAWindow + CPOperationNotSupportedForKeyScriptError + CPOperationNotSupportedForKeySpecifierError + CPOrPredicateType + CPOrderedAscending + CPOrderedDescending + CPOrderedSame + CPOtherMouseDown + CPOtherMouseDownMask + CPOtherMouseDragged + CPOtherMouseDraggedMask + CPOtherMouseUp + CPOtherMouseUpMask + CPOtherTextMovement + CPOutlineViewColumnDidMoveNotification + CPOutlineViewColumnDidResizeNotification + CPOutlineViewDropOnItemIndex + CPOutlineViewItemDidCollapseNotification + CPOutlineViewItemDidExpandNotification + CPOutlineViewItemWillCollapseNotification + CPOutlineViewItemWillExpandNotification + CPOutlineViewSelectionDidChangeNotification + CPOutlineViewSelectionIsChangingNotification + CPOwnedObjectIdentityHashCallBacks + CPOwnedPointerHashCallBacks + CPOwnedPointerMapKeyCallBacks + CPOwnedPointerMapValueCallBacks + CPPDFPboardType + CPPICTPboardType + CPPLCancelButton + CPPLHeightForm + CPPLImageButton + CPPLOKButton + CPPLOrientationMatrix + CPPLPaperNameButton + CPPLTitleField + CPPLUnitsButton + CPPLWidthForm + CPPNGFileType + CPPOSIXErrorDomain + CPPPCopiesField + CPPPDIncludeNotFoundException + CPPPDIncludeStackOverflowException + CPPPDIncludeStackUnderflowException + CPPPDParseException + CPPPImageButton + CPPPLayoutButton + CPPPNameField + CPPPNameTitle + CPPPNoteField + CPPPNoteTitle + CPPPOptionsButton + CPPPPageChoiceMatrix + CPPPPageRangeFrom + CPPPPageRangeTo + CPPPPaperFeedButton + CPPPPreviewButton + CPPPSaveButton + CPPPScaleField + CPPPStatusField + CPPPStatusTitle + CPPPTitleField + CPPageDownFunctionKey + CPPageUpFunctionKey + CPParagraphSeparatorCharacter + CPParagraphStyleAttributeName + CPPasteboardCommunicationException + CPPatternColorSpace + CPPauseFunctionKey + CPPenPointingDevice + CPPeriodic + CPPeriodicMask + CPPersistentStoreCoordinatorLockingError + CPPersistentStoreIncompatibleSchemaError + CPPersistentStoreIncompleteSaveError + CPPersistentStoreInvalidTypeError + CPPersistentStoreSaveError + CPPersistentStoreTypeMismatchError + CPPlainFileType + CPPlainTextDocumentType + CPPlainTextTokenStyle + CPPointerToStructHashCallBacks + CPPopUpArrowAtBottom + CPPopUpArrowAtCenter + CPPopUpButtonCellWillPopUpNotification + CPPopUpButtonWillPopUpNotification + CPPopUpNoArrow + CPPortraitOrientation + CPPositionAfter + CPPositionBefore + CPPositionBeginning + CPPositionEnd + CPPositionReplace + CPPositiveDoubleType + CPPositiveFloatType + CPPositiveIntType + CPPostASAP + CPPostNow + CPPostScriptPboardType + CPPostWhenIdle + CPPosterFontMask + CPPowerOffEventType + CPPressedTab + CPPrevFunctionKey + CPPrintAllPages + CPPrintBottomMargin + CPPrintCancelJob + CPPrintCopies + CPPrintFaxCoverSheetName + CPPrintFaxHighResolution + CPPrintFaxJob + CPPrintFaxModem + CPPrintFaxReceiverNames + CPPrintFaxReceiverNumbers + CPPrintFaxReturnReceipt + CPPrintFaxSendTime + CPPrintFaxTrimPageEnds + CPPrintFaxUseCoverSheet + CPPrintFirstPage + CPPrintFormName + CPPrintFunctionKey + CPPrintHorizontalPagination + CPPrintHorizontallyCentered + CPPrintJobDisposition + CPPrintJobFeatures + CPPrintLastPage + CPPrintLeftMargin + CPPrintManualFeed + CPPrintMustCollate + CPPrintOperationExistsException + CPPrintOrientation + CPPrintPackageException + CPPrintPagesPerSheet + CPPrintPaperFeed + CPPrintPaperName + CPPrintPaperSize + CPPrintPhotoJobStyleHint + CPPrintPreviewJob + CPPrintPrinter + CPPrintReversePageOrder + CPPrintRightMargin + CPPrintSaveJob + CPPrintSavePath + CPPrintScalingFactor + CPPrintScreenFunctionKey + CPPrintSpoolJob + CPPrintTopMargin + CPPrintVerticalPagination + CPPrintVerticallyCentered + CPPrinterTableError + CPPrinterTableNotFound + CPPrinterTableOK + CPPrintingCancelled + CPPrintingCommunicationException + CPPrintingFailure + CPPrintingReplyLater + CPPrintingSuccess + CPProgressIndicatorBarStyle + CPProgressIndicatorPreferredAquaThickness + CPProgressIndicatorPreferredLargeThickness + CPProgressIndicatorPreferredSmallThickness + CPProgressIndicatorPreferredThickness + CPProgressIndicatorSpinningStyle + CPPropertyListBinaryFormat + CPPropertyListImmutable + CPPropertyListMutableContainers + CPPropertyListMutableContainersAndLeaves + CPPropertyListOpenStepFormat + CPPropertyListXMLFormat + CPProprietaryStringEncoding + CPPushInCell + CPPushInCellMask + CPPushOnPushOffButton + CPQTMovieLoopingBackAndForthPlayback + CPQTMovieLoopingPlayback + CPQTMovieNormalPlayback + CPRGBColorSpaceModel + CPRGBModeColorPanel + CPRTFDPboardType + CPRTFDTextDocumentType + CPRTFPboardType + CPRTFPropertyStackOverflowException + CPRTFTextDocumentType + CPRadioButton + CPRadioModeMatrix + CPRandomSubelement + CPRangeDateMode + CPRatingLevelIndicatorStyle + CPReceiverEvaluationScriptError + CPReceiversCantHandleCommandScriptError + CPRecessedBezelStyle + CPRedoFunctionKey + CPRegularControlSize + CPRegularSquareBezelStyle + CPRelativeAfter + CPRelativeBefore + CPRelevancyLevelIndicatorStyle + CPRemoveTraitFontAction + CPRequiredArgumentsMissingScriptError + CPResetCursorRectsRunLoopOrdering + CPResetFunctionKey + CPResizableWindowMask + CPReturnTextMovement + CPRightArrowFunctionKey + CPRightMouseDown + CPRightMouseDownMask + CPRightMouseDragged + CPRightMouseDraggedMask + CPRightMouseUp + CPRightMouseUpMask + CPRightTabStopType + CPRightTabsBezelBorder + CPRightTextAlignment + CPRightTextMovement + CPRoundBankers + CPRoundDown + CPRoundLineCapStyle + CPRoundLineJoinStyle + CPRoundPlain + CPRoundRectBezelStyle + CPRoundUp + CPRoundedBezelStyle + CPRoundedDisclosureBezelStyle + CPRoundedTokenStyle + CPRulerPboard + CPRulerPboardType + CPRunAbortedResponse + CPRunContinuesResponse + CPRunStoppedResponse + CPSaveAsOperation + CPSaveOperation + CPSaveOptionsAsk + CPSaveOptionsNo + CPSaveOptionsYes + CPSaveToOperation + CPScaleNone + CPScaleProportionally + CPScaleToFit + CPScreenChangedEventType + CPScrollLockFunctionKey + CPScrollWheel + CPScrollWheelMask + CPScrollerArrowsDefaultSetting + CPScrollerArrowsMaxEnd + CPScrollerArrowsMinEnd + CPScrollerArrowsNone + CPScrollerDecrementArrow + CPScrollerDecrementLine + CPScrollerDecrementPage + CPScrollerIncrementArrow + CPScrollerIncrementLine + CPScrollerIncrementPage + CPScrollerKnob + CPScrollerKnobSlot + CPScrollerNoPart + CPSecondCalendarUnit + CPSegmentSwitchTrackingMomentary + CPSegmentSwitchTrackingSelectAny + CPSegmentSwitchTrackingSelectOne + CPSelectByCharacter + CPSelectByParagraph + CPSelectByWord + CPSelectFunctionKey + CPSelectedTab + CPSelectingNext + CPSelectingPrevious + CPSelectionAffinityDownstream + CPSelectionAffinityUpstream + CPShadowAttributeName + CPShadowlessSquareBezelStyle + CPShellCommandFileType + CPShiftJISStringEncoding + CPShiftKeyMask + CPShowControlGlyphs + CPShowInvisibleGlyphs + CPSingleDateMode + CPSingleUnderlineStyle + CPSizeDownFontAction + CPSizeUpFontAction + CPSmallCapsFontMask + CPSmallControlSize + CPSmallIconButtonBezelStyle + CPSmallSquareBezelStyle + CPSolarisOperatingSystem + CPSoundPboardType + CPSpecialPageOrder + CPSplitViewDidResizeSubviewsNotification + CPSplitViewWillResizeSubviewsNotification + CPSquareLineCapStyle + CPStopFunctionKey + CPStreamEventEndEncountered + CPStreamEventErrorOccurred + CPStreamEventHasBytesAvailable + CPStreamEventHasSpaceAvailable + CPStreamEventNone + CPStreamEventOpenCompleted + CPStreamStatusAtEnd + CPStreamStatusClosed + CPStreamStatusError + CPStreamStatusNotOpen + CPStreamStatusOpen + CPStreamStatusOpening + CPStreamStatusReading + CPStreamStatusWriting + CPStrikethroughColorAttributeName + CPStrikethroughStyleAttributeName + CPStringAttributeType + CPStringDrawingDisableScreenFontSubstitution + CPStringDrawingOneShot + CPStringDrawingUsesDeviceMetrics + CPStringDrawingUsesFontLeading + CPStringDrawingUsesLineFragmentOrigin + CPStringEncodingErrorKey + CPStringPboardType + CPStrokeColorAttributeName + CPStrokeWidthAttributeName + CPSunOSOperatingSystem + CPSuperscriptAttributeName + CPSwitchButton + CPSymbolStringEncoding + CPSysReqFunctionKey + CPSystemColorsDidChangeNotification + CPSystemDefined + CPSystemDefinedMask + CPSystemDomainMask + CPSystemFunctionKey + CPTIFFCompressionCCITTFAX + CPTIFFCompressionJPEG + CPTIFFCompressionLZW + CPTIFFCompressionNEXT + CPTIFFCompressionNone + CPTIFFCompressionOldJPEG + CPTIFFCompressionPackBits + CPTIFFException + CPTIFFFileType + CPTIFFPboardType + CPTabCharacter + CPTabColumnTerminatorsAttributeName + CPTabTextMovement + CPTableViewColumnDidMoveNotification + CPTableViewColumnDidResizeNotification + CPTableViewDropAbove + CPTableViewDropOn + CPTableViewFirstColumnOnlyAutoresizingStyle + CPTableViewGridNone + CPTableViewLastColumnOnlyAutoresizingStyle + CPTableViewNoColumnAutoresizing + CPTableViewReverseSequentialColumnAutoresizingStyle + CPTableViewSelectionDidChangeNotification + CPTableViewSelectionIsChangingNotification + CPTableViewSequentialColumnAutoresizingStyle + CPTableViewSolidHorizontalGridLineMask + CPTableViewSolidVerticalGridLineMask + CPTableViewUniformColumnAutoresizingStyle + CPTabletPoint + CPTabletPointEventSubtype + CPTabletPointMask + CPTabletProximity + CPTabletProximityEventSubtype + CPTabletProximityMask + CPTabularTextPboardType + CPTerminateCancel + CPTerminateLater + CPTerminateNow + CPTextBlockAbsoluteValueType + CPTextBlockBaselineAlignment + CPTextBlockBorder + CPTextBlockBottomAlignment + CPTextBlockHeight + CPTextBlockMargin + CPTextBlockMaximumHeight + CPTextBlockMaximumWidth + CPTextBlockMiddleAlignment + CPTextBlockMinimumHeight + CPTextBlockMinimumWidth + CPTextBlockPadding + CPTextBlockPercentageValueType + CPTextBlockTopAlignment + CPTextBlockWidth + CPTextCellType + CPTextDidBeginEditingNotification + CPTextDidChangeNotification + CPTextDidEndEditingNotification + CPTextFieldAndStepperDatePickerStyle + CPTextFieldRoundedBezel + CPTextFieldSquareBezel + CPTextLineTooLongException + CPTextNoSelectionException + CPTextReadException + CPTextReadInapplicableDocumentTypeError + CPTextReadWriteErrorMaximum + CPTextReadWriteErrorMinimum + CPTextSizeMultiplierDocumentOption + CPTextStorageDidProcessEditingNotification + CPTextStorageEditedAttributes + CPTextStorageEditedCharacters + CPTextStorageWillProcessEditingNotification + CPTextTableAutomaticLayoutAlgorithm + CPTextTableFixedLayoutAlgorithm + CPTextViewDidChangeSelectionNotification + CPTextViewDidChangeTypingAttributesNotification + CPTextViewWillChangeNotifyingTextViewNotification + CPTextViews + CPTextWriteException + CPTextWriteInapplicableDocumentTypeError + CPTexturedBackgroundWindowMask + CPTexturedRoundedBezelStyle + CPTexturedSquareBezelStyle + CPThickSquareBezelStyle + CPThickerSquareBezelStyle + CPTickMarkAbove + CPTickMarkBelow + CPTickMarkLeft + CPTickMarkRight + CPTitledWindowMask + CPToggleButton + CPToolTipAttributeName + CPToolbarCustomizeToolbarItemIdentifier + CPToolbarDidRemoveItemNotification + CPToolbarFlexibleSpaceItemIdentifier + CPToolbarPrintItemIdentifier + CPToolbarSeparatorItemIdentifier + CPToolbarShowColorsItemIdentifier + CPToolbarShowFontsItemIdentifier + CPToolbarSpaceItemIdentifier + CPToolbarWillAddItemNotification + CPTopTabsBezelBorder + CPTrackModeMatrix + CPTwoByteGlyphPacking + CPTypedStreamVersionException + CPTypesetterContainerBreakAction + CPTypesetterHorizontalTabAction + CPTypesetterLatestBehavior + CPTypesetterLineBreakAction + CPTypesetterOriginalBehavior + CPTypesetterParagraphBreakAction + CPTypesetterWhitespaceAction + CPTypesetterZeroAdvancementAction + CPURLCacheStorageAllowed + CPURLCacheStorageAllowedInMemoryOnly + CPURLCacheStorageNotAllowed + CPURLCacheStoragePolicy + CPURLCredentialPersistenceForSession + CPURLCredentialPersistenceNone + CPURLCredentialPersistencePermanent + CPURLCredentialStorageInternal + CPURLErrorBadServerResponse + CPURLErrorBadURL + CPURLErrorCancelled + CPURLErrorCannotCloseFile + CPURLErrorCannotConnectToHost + CPURLErrorCannotCreateFile + CPURLErrorCannotFindHost + CPURLErrorCannotLoadFromNetwork + CPURLErrorCannotMoveFile + CPURLErrorCannotOpenFile + CPURLErrorCannotRemoveFile + CPURLErrorCannotWriteToFile + CPURLErrorDNSLookupFailed + CPURLErrorDownloadDecodingFailedMidStream + CPURLErrorDownloadDecodingFailedToComplete + CPURLErrorFileDoesNotExist + CPURLErrorFileIsDirectory + CPURLErrorHTTPTooManyRedirects + CPURLErrorNetworkConnectionLost + CPURLErrorNoPermissionsToReadFile + CPURLErrorNotConnectedToInternet + CPURLErrorRedirectToNonExistentLocation + CPURLErrorResourceUnavailable + CPURLErrorSecureConnectionFailed + CPURLErrorServerCertificateHasBadDate + CPURLErrorServerCertificateHasUnknownRoot + CPURLErrorServerCertificateUntrusted + CPURLErrorTimedOut + CPURLErrorUnknown + CPURLErrorUnsupportedURL + CPURLErrorUserAuthenticationRequired + CPURLErrorUserCancelledAuthentication + CPURLErrorZeroByteResource + CPURLHandleLoadFailed + CPURLHandleLoadInProgress + CPURLHandleLoadSucceeded + CPURLHandleNotLoaded + CPURLPboardType + CPURLRequestCachePolicy + CPURLRequestReloadIgnoringCacheData + CPURLRequestReturnCacheDataDontLoad + CPURLRequestReturnCacheDataElseLoad + CPURLRequestUseProtocolCachePolicy + CPUTF8StringEncoding + CPUnboldFontMask + CPUndefinedAttributeType + CPUnderlineColorAttributeName + CPUnderlinePatternDash + CPUnderlinePatternDashDot + CPUnderlinePatternDashDotDot + CPUnderlinePatternDot + CPUnderlinePatternSolid + CPUnderlineStyleAttributeName + CPUnderlineStyleDouble + CPUnderlineStyleNone + CPUnderlineStyleSingle + CPUnderlineStyleThick + CPUnderlyingErrorKey + CPUndoCloseGroupingRunLoopOrdering + CPUndoFunctionKey + CPUndoManagerDidRedoChangeNotification + CPUndoManagerDidUndoChangeNotification + CPUndoManagerWillCloseUndoGroupNotification + CPUnicodeStringEncoding + CPUnitalicFontMask + CPUnknownColorSpaceModel + CPUnknownKeyScriptError + CPUnknownKeySpecifierError + CPUnknownPageOrder + CPUnknownPointingDevice + CPUnselectCancel + CPUnselectLater + CPUnselectNow + CPUpArrowFunctionKey + CPUpTextMovement + CPUpdateWindowsRunLoopOrdering + CPUserDirectory + CPUserDomainMask + CPUserFunctionKey + CPUserKeyEquivalents + CPUtilityWindowMask + CPVCardPboardType + CPValidationDateTooLateError + CPValidationDateTooSoonError + CPValidationInvalidDateError + CPValidationKeyErrorKey + CPValidationMissingMandatoryPropertyError + CPValidationMultipleErrorsError + CPValidationNumberTooLargeError + CPValidationNumberTooSmallError + CPValidationObjectErrorKey + CPValidationPredicateErrorKey + CPValidationRelationshipDeniedDeleteError + CPValidationRelationshipExceedsMaximumCountError + CPValidationRelationshipLacksMinimumCountError + CPValidationStringPatternMatchingError + CPValidationStringTooLongError + CPValidationStringTooShortError + CPValidationValueErrorKey + CPVariableExpressionType + CPVerticalRuler + CPViaPanelFontAction + CPViewBoundsDidChangeNotification + CPViewFocusDidChangeNotification + CPViewFrameDidChangeNotification + CPViewGlobalFrameDidChangeNotification + CPViewHeightSizable + CPViewMaxXMargin + CPViewMaxYMargin + CPViewMinXMargin + CPViewMinYMargin + CPViewNotSizable + CPViewWidthSizable + CPVoiceAge + CPVoiceDemoText + CPVoiceGender + CPVoiceGenderFemale + CPVoiceGenderMale + CPVoiceGenderNeuter + CPVoiceIdentifier + CPVoiceLanguage + CPVoiceName + CPWantsBidiLevels + CPWarningAlertStyle + CPWeekCalendarUnit + CPWeekdayCalendarUnit + CPWeekdayOrdinalCalendarUnit + CPWheelModeColorPanel + CPWindowAbove + CPWindowBelow + CPWindowCloseButton + CPWindowDidBecomeKeyNotification + CPWindowDidBecomeMainNotification + CPWindowDidChangeScreenNotification + CPWindowDidDeminiaturizeNotification + CPWindowDidEndSheetNotification + CPWindowDidExposeNotification + CPWindowDidMiniaturizeNotification + CPWindowDidMoveNotification + CPWindowDidResignKeyNotification + CPWindowDidResignMainNotification + CPWindowDidResizeNotification + CPWindowDidUpdateNotification + CPWindowDocumentIconButton + CPWindowExposedEventType + CPWindowMiniaturizeButton + CPWindowMovedEventType + CPWindowOut + CPWindowServerCommunicationException + CPWindowToolbarButton + CPWindowWillBeginSheetNotification + CPWindowWillCloseNotification + CPWindowWillMiniaturizeNotification + CPWindowWillMoveNotification + CPWindowZoomButton + CPWindows + CPWindowsCP + CPWindowsNTOperatingSystem + CPWordMLTextDocumentType + CPWordTablesReadException + CPWordTablesWriteException + CPWorkspaceCompressOperation + CPWorkspaceCopyOperation + CPWorkspaceDecompressOperation + CPWorkspaceDecryptOperation + CPWorkspaceDestroyOperation + CPWorkspaceDidLaunchApplicationNotification + CPWorkspaceDidMountNotification + CPWorkspaceDidPerformFileOperationNotification + CPWorkspaceDidTerminateApplicationNotification + CPWorkspaceDidUnmountNotification + CPWorkspaceDidWakeNotification + CPWorkspaceDuplicateOperation + CPWorkspaceEncryptOperation + CPWorkspaceLaunchAllowingClassicStartup + CPWorkspaceLaunchAndDisplayFailures + CPWorkspaceLaunchAndHide + CPWorkspaceLaunchAndHideOthers + CPWorkspaceLaunchAndPrint + CPWorkspaceLaunchAsync + CPWorkspaceLaunchDefault + CPWorkspaceLaunchInhibitingBackgroundOnly + CPWorkspaceLaunchNewInstance + CPWorkspaceLaunchPreferringClassic + CPWorkspaceLaunchWithoutActivation + CPWorkspaceLaunchWithoutAddingToRecents + CPWorkspaceLinkOperation + CPWorkspaceMoveOperation + CPWorkspaceRecycleOperation + CPWorkspaceSessionDidBecomeActiveNotification + CPWorkspaceSessionDidResignActiveNotification + CPWorkspaceWillLaunchApplicationNotification + CPWorkspaceWillPowerOffNotification + CPWorkspaceWillSleepNotification + CPWorkspaceWillUnmountNotification + CPWritingDirectionLeftToRight + CPWritingDirectionNatural + CPWritingDirectionRightToLeft + CPXMLAttributeCDATAKind + CPXMLAttributeDeclarationKind + CPXMLAttributeEntitiesKind + CPXMLAttributeEntityKind + CPXMLAttributeEnumerationKind + CPXMLAttributeIDKind + CPXMLAttributeIDRefKind + CPXMLAttributeIDRefsKind + CPXMLAttributeKind + CPXMLAttributeNMTokenKind + CPXMLAttributeNMTokensKind + CPXMLAttributeNotationKind + CPXMLCommentKind + CPXMLDTDKind + CPXMLDocumentHTMLKind + CPXMLDocumentIncludeContentTypeDeclaration + CPXMLDocumentKind + CPXMLDocumentTextKind + CPXMLDocumentTidyHTML + CPXMLDocumentTidyXML + CPXMLDocumentValidate + CPXMLDocumentXHTMLKind + CPXMLDocumentXMLKind + CPXMLElementDeclarationAnyKind + CPXMLElementDeclarationElementKind + CPXMLElementDeclarationEmptyKind + CPXMLElementDeclarationKind + CPXMLElementDeclarationMixedKind + CPXMLElementDeclarationUndefinedKind + CPXMLElementKind + CPXMLEntityDeclarationKind + CPXMLEntityGeneralKind + CPXMLEntityParameterKind + CPXMLEntityParsedKind + CPXMLEntityPredefined + CPXMLEntityUnparsedKind + CPXMLInvalidKind + CPXMLNamespaceKind + CPXMLNodeCompactEmptyElement + CPXMLNodeExpandEmptyElement + CPXMLNodeIsCDATA + CPXMLNodeOptionsNone + CPXMLNodePreserveAll + CPXMLNodePreserveAttributeOrder + CPXMLNodePreserveCDATA + CPXMLNodePreserveDTD + CPXMLNodePreserveEmptyElements + CPXMLNodePreserveEntities + CPXMLNodePreserveNamespaceOrder + CPXMLNodePreservePrefixes + CPXMLNodePreserveQuotes + CPXMLNodePreserveWhitespace + CPXMLNodePrettyPrint + CPXMLNodeUseDoubleQuotes + CPXMLNodeUseSingleQuotes + CPXMLNotationDeclarationKind + CPXMLParserAttributeHasNoValueError + CPXMLParserAttributeListNotFinishedError + CPXMLParserAttributeListNotStartedError + CPXMLParserAttributeNotFinishedError + CPXMLParserAttributeNotStartedError + CPXMLParserAttributeRedefinedError + CPXMLParserCDATANotFinishedError + CPXMLParserCharacterRefAtEOFError + CPXMLParserCharacterRefInDTDError + CPXMLParserCharacterRefInEpilogError + CPXMLParserCharacterRefInPrologError + CPXMLParserCommentContainsDoubleHyphenError + CPXMLParserCommentNotFinishedError + CPXMLParserConditionalSectionNotFinishedError + CPXMLParserConditionalSectionNotStartedError + CPXMLParserDOCTYPEDeclNotFinishedError + CPXMLParserDelegateAbortedParseError + CPXMLParserDocumentStartError + CPXMLParserElementContentDeclNotFinishedError + CPXMLParserElementContentDeclNotStartedError + CPXMLParserEmptyDocumentError + CPXMLParserEncodingNotSupportedError + CPXMLParserEntityBoundaryError + CPXMLParserEntityIsExternalError + CPXMLParserEntityIsParameterError + CPXMLParserEntityNotFinishedError + CPXMLParserEntityNotStartedError + CPXMLParserEntityRefAtEOFError + CPXMLParserEntityRefInDTDError + CPXMLParserEntityRefInEpilogError + CPXMLParserEntityRefInPrologError + CPXMLParserEntityRefLoopError + CPXMLParserEntityReferenceMissingSemiError + CPXMLParserEntityReferenceWithoutNameError + CPXMLParserEntityValueRequiredError + CPXMLParserEqualExpectedError + CPXMLParserExternalStandaloneEntityError + CPXMLParserExternalSubsetNotFinishedError + CPXMLParserExtraContentError + CPXMLParserGTRequiredError + CPXMLParserInternalError + CPXMLParserInvalidCharacterError + CPXMLParserInvalidCharacterInEntityError + CPXMLParserInvalidCharacterRefError + CPXMLParserInvalidConditionalSectionError + CPXMLParserInvalidDecimalCharacterRefError + CPXMLParserInvalidEncodingError + CPXMLParserInvalidEncodingNameError + CPXMLParserInvalidHexCharacterRefError + CPXMLParserInvalidURIError + CPXMLParserLTRequiredError + CPXMLParserLTSlashRequiredError + CPXMLParserLessThanSymbolInAttributeError + CPXMLParserLiteralNotFinishedError + CPXMLParserLiteralNotStartedError + CPXMLParserMisplacedCDATAEndStringError + CPXMLParserMisplacedXMLDeclarationError + CPXMLParserMixedContentDeclNotFinishedError + CPXMLParserMixedContentDeclNotStartedError + CPXMLParserNAMERequiredError + CPXMLParserNMTOKENRequiredError + CPXMLParserNamespaceDeclarationError + CPXMLParserNoDTDError + CPXMLParserNotWellBalancedError + CPXMLParserNotationNotFinishedError + CPXMLParserNotationNotStartedError + CPXMLParserOutOfMemoryError + CPXMLParserPCDATARequiredError + CPXMLParserParsedEntityRefAtEOFError + CPXMLParserParsedEntityRefInEpilogError + CPXMLParserParsedEntityRefInInternalError + CPXMLParserParsedEntityRefInInternalSubsetError + CPXMLParserParsedEntityRefInPrologError + CPXMLParserParsedEntityRefMissingSemiError + CPXMLParserParsedEntityRefNoNameError + CPXMLParserPrematureDocumentEndError + CPXMLParserProcessingInstructionNotFinishedError + CPXMLParserProcessingInstructionNotStartedError + CPXMLParserPublicIdentifierRequiredError + CPXMLParserSeparatorRequiredError + CPXMLParserSpaceRequiredError + CPXMLParserStandaloneValueError + CPXMLParserStringNotClosedError + CPXMLParserStringNotStartedError + CPXMLParserTagNameMismatchError + CPXMLParserURIFragmentError + CPXMLParserURIRequiredError + CPXMLParserUndeclaredEntityError + CPXMLParserUnfinishedTagError + CPXMLParserUnknownEncodingError + CPXMLParserUnparsedEntityError + CPXMLParserXMLDeclNotFinishedError + CPXMLParserXMLDeclNotStartedError + CPXMLProcessingInstructionKind + CPXMLTextKind + CPYearCalendarUnit + + + + + + ^\s*import + ((?<=[^\\])[\n\r])|(^[\n\r]) + + + + '(?=\\?.') + (\\'')|(.') + + + + /\*" + "\*/ + + + + /\* + \*/ + + + + // + [\n\r] + + + + @?" + (((?<!\\)(\\\\)*)|^)" + + + + diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarAppKiDo.png b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarAppKiDo.png new file mode 100644 index 0000000000..5c9adc58dd Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarAppKiDo.png differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarBuild.tiff b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarBuild.tiff new file mode 100644 index 0000000000..c6294820e4 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarBuild.tiff differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarCounterpart.png b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarCounterpart.png new file mode 100644 index 0000000000..06687bd506 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarCounterpart.png differ diff --git a/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarXcodeProject.png b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarXcodeProject.png new file mode 100644 index 0000000000..0381058f35 Binary files /dev/null and b/Tools/SubEthaEdit/Objective-J.mode/Contents/Resources/ToolbarXcodeProject.png differ diff --git a/Tools/TextMate/Objective-J.tmbundle.zip b/Tools/TextMate/Objective-J.tmbundle.zip new file mode 100644 index 0000000000..629a343b46 Binary files /dev/null and b/Tools/TextMate/Objective-J.tmbundle.zip differ diff --git a/Tools/Utilities/bridge.js b/Tools/Utilities/bridge.js new file mode 100644 index 0000000000..9dc6e9bd29 --- /dev/null +++ b/Tools/Utilities/bridge.js @@ -0,0 +1,476 @@ + //////////// + // window // + //////////// + +if (!this.window) + this.window = this; + + /////////////// + // DOMParser // + /////////////// + +/*function DOMParser() {}; +DOMParser.prototype.parseFromString = function(text, contentType) { + return new DOMDocument( + new Packages.org.xml.sax.InputSource( + new Packages.java.io.StringReader(text))); +};*/ + +function Image() { } + + /////////////////////// + // window.setTimeout // + /////////////////////// + +function setTimeout(code, delay) { + var func; + + if (typeof code == "function") + func = code; + else if (typeof code == "string") + func = function () { eval(code); }; + else + return; + + pendingTimeouts.push(func); +} + +pendingTimeouts = []; + +function pendingTimeout() { + return pendingTimeouts.length > 0; +} + +function serviceTimeout() { + if (pendingTimeout()) { + func = pendingTimeouts.shift(); + return func(); + } +} + +function serviceTimeouts() { + while (pendingTimeout()) { + serviceTimeout(); + } +} + + //////////////////////////// + // alert, prompt, confirm // + //////////////////////////// + +if (typeof alert == "undefined") +{ + alert = function(obj) { + if (typeof debug != "undefined" && debug) { + var result = typeof Packages != "undefined" ? Packages.java.lang.Thread.currentThread().getName() + ": " + obj : String(obj); + + if (typeof print != "undefined") + print(result); + else if (typeof Packages != "undefined") + Packages.java.lang.System.out.println(result); + } + } + confirm = function(obj) { alert(obj); return true; } + prompt = function(obj) { alert(obj); return ""; } +} + + + //////////////////// + // readFile, load // + //////////////////// + +if (typeof readFile == "undefined") { + if (typeof File != "undefined") { + alert("Setting up \"readFile()\" for Spidermonkey"); + this.readFile = function(path) { + var f = new File(path); + + if (!f.canRead) { + //alert("can't read: " + f.path) + return ""; + } + + //alert("reading: " + f.path); + + f.open("read", "text"); + + var result = f.readAll().join("\n"); + + f.close(); + + return result; + } + } + else if (typeof Packages != "undefined") { + alert("Setting up \"readFile()\" for Rhino"); + readFile = function(path, characterCoding) { + var f = new Packages.java.io.File(path); + + if (!f.canRead()) { + //alert("can't read: " + f.path) + return ""; + } + + //alert("reading: " + f.getAbsolutePath()); + + var fis = new Packages.java.io.FileInputStream(f); + + var b = Packages.java.lang.reflect.Array.newInstance(Packages.java.lang.Byte.TYPE, fis.available()); + fis.read(b); + + fis.close(); + + //return String(new Packages.java.lang.String(b)); + if (characterCoding) + return new Packages.java.lang.String(b, characterCoding); + else + return new Packages.java.lang.String(b); + } + } + else { + alert("Warning: No \"readFile\" implementation available.") + } +} + +if (typeof load == "undefined") { + alert("Setting up \"load()\""); + load = function(path) { + return eval(readFile(path)); + } +} + + ///////////// + // inspect // + ///////////// + +function insp(obj) { + for (var i in obj) + alert(i + " ("+(typeof obj[i])+")"); +} +function inspect(obj) { + var a = []; + for (var i in obj) + a.push({name:i, type:(typeof obj[i])}); + a = a.sort(function(a,b) { return a.name.localeCompare(b.name); }); + for (var i = 0; i < a.length; i++) + alert(a[i].name + " ("+a[i].type+")"); +} + +function time(fn) { + var start = new Date(); + + fn(); + + var elapsed = ((new Date()) - start) / 1000; + var minutes = Math.floor(elapsed/60); + var seconds = elapsed - minutes * 60; + + alert("real\t"+minutes+"m"+seconds+"s"); +} + +function recordGlobals() { + GLOBALS_BEFORE = {}; + for (var i in this) + GLOBALS_BEFORE[i] = true; +} +function printGlobalsDiff() { + for (var i in this) + if (GLOBALS_BEFORE[i] == undefined) + alert("NEW: " + i); + for (var i in GLOBALS_BEFORE) + if (this[i] == undefined) + alert("MISSING: " + i); +} + +var hex_lookup = "0123456789abcdef"; +function bytesToHexString(buf) +{ + var buffer = ""; + for (var i = 0 ; i < buf.length; i++) + buffer += hex_lookup[(buf[i] >> 4) & 0x0F] + hex_lookup[buf[i] & 0x0F]; + return buffer; +} + +// Rhino utilities +if (typeof Packages != "undefined") { + alert("Setting up Rhino utilties"); + + jsArrayToJavaArray = function(js_array, type) + { + var java_class = null; + var java_converter = null; + + switch (type || ((js_array && js_array.length > 0) && typeof js_array[0])) + { + case "string": + case "String": + java_class = Packages.java.lang.String; + java_converter = Packages.java.lang.String.valueOf; + break; + case "Boolean": + java_class = Packages.java.lang.Boolean; + java_converter = Packages.java.lang.Boolean.valueOf; + break; + case "boolean": + java_class = Packages.java.lang.Boolean.TYPE; + java_converter = function(input) { return Packages.java.lang.Boolean.valueOf(input).booleanValue(); }; + break; + default: + return null; + } + + if (js_array && js_array.length > 0) + { + var java_array = Packages.java.lang.reflect.Array.newInstance(java_class, js_array.length); + for (var i = 0; i < js_array.length; i++) + java_array[i] = java_converter ? java_converter(js_array[i]) : js_array[i]; + return java_array; + } + + return Packages.java.lang.reflect.Array.newInstance(java_class, 0); + } + + jsObjectToJavaHashMap = function(js_object) + { + var map = Packages.java.util.HashMap(); + for (var i in js_object) + map.put(i, js_object[i]); + return map; + } + + objj_console = function() + { + var br = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(Packages.java.lang.System["in"])); + + keepgoing = true; + while (keepgoing) + { + try { + Packages.java.lang.System.out.print("objj> "); + + var input = br.readLine(); + + var fragments = objj_preprocess(String(input)), + count = fragments.length, + ctx = (new objj_context); + + if (count == 1 && (fragments[0].type & FRAGMENT_CODE)) + { + var fragment = fragments[0]; + var result = eval(fragment.info); + if (result != undefined) + print(result); + } + else if (count > 0) + { + while (count--) + { + var fragment = fragments[count]; + + if (fragment.type & FRAGMENT_FILE) + objj_request_file(fragment.info, (fragment.type & FRAGMENT_LOCAL), NULL); + + ctx.pushFragment(fragment); + } + + ctx.schedule(); + } + + serviceTimeouts(); + } catch (e) { + print(e); + } + } + }; + + var xhr_builder = Packages.javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder(); + xhr_builder.setErrorHandler(function(exception, methodName) { + //print("xml error!"); + throw exception; + }); + + copyInputStreamToOutputStream = function(is, os) + { + var buf = Packages.java.lang.reflect.Array.newInstance(Packages.java.lang.Byte.TYPE, 1024*10); + var len = 0; + while ((len = is.read(buf)) != -1) + { + os.write(buf, 0, len); + } + } + var lineEnd = "\r\n"; + var twoHyphens = "--"; + var boundary = "----------------------------b453b5d52446"; + //var boundary = "----CappuccinoBoundary" + (new Date().getTime()); + multipartRequest = function(method, url, headers, parts) + { + var bufferSize = 1024*10, + buffer = Packages.java.lang.reflect.Array.newInstance(Packages.java.lang.Byte.TYPE, bufferSize); + + var url = new Packages.java.net.URL(url), + connection = url.openConnection(); + + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setRequestMethod(method); + + for (var i in headers) + { + print(i+":"+headers[i]); + connection.setRequestProperty(i, headers[i]); + } + + connection.setRequestProperty("Content-Type", "multipart/form-data; boundary="+ boundary); + + var output = new Packages.java.io.DataOutputStream(connection.getOutputStream()); + + for (var i = 0; parts && i < parts.length; i++) + { + var part = parts[i]; + + output.writeBytes(twoHyphens + boundary + lineEnd); + + if (part.headers) + { + for (var header in part.headers) + { + output.writeBytes(header +": " + part.headers[header] + lineEnd); + } + } + output.writeBytes(lineEnd); + + if (part.data) + { + output.writeBytes(part.data); + } + else if (part.stream) + { + var n; + while ((n = part.stream.read(buffer, 0, bufferSize)) > 0) + { + output.write(buffer, 0, n) + } + } + + output.writeBytes(lineEnd); + } + + output.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); + output.flush(); + output.close(); + + var buffer, + input = new Packages.java.io.DataInputStream(connection.getInputStream()), + result = new Packages.java.lang.StringBuffer(); + + while (null != (buffer = input.readLine())) + result.append(buffer); + + input.close(); + + return String(result.toString()); + } + + parseXMLString = function(string) { + return (Packages.javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder().parse( + new Packages.org.xml.sax.InputSource( + new Packages.java.io.StringReader(string))).getDocumentElement()); + } +} + +// Environment variables + +OBJJ_HOME = null; +if (typeof Packages != "undefined") + OBJJ_HOME = String(Packages.java.lang.System.getenv().get("OBJJ_HOME") || ""); +else if (typeof environment != "undefined") + OBJJ_HOME = environment["OBJJ_HOME"]; + +if (!OBJJ_HOME) +{ + OBJJ_HOME = "/usr/local/share/objj"; + alert("OBJJ_HOME environment variable not set, defaulting to " + OBJJ_HOME); +} + + //////////////////// + // XMLHttpRequest // + //////////////////// + +function XMLHttpRequest() { + this.readyState = 0; + this.responseText = ""; + this.responseXML = null; + this.status = null; + this.statusText = null; + + this.onreadystatechange = null; + + this.method = null; + this.url = null; + this.async = null; + this.username = null; + this.password = null; +} +XMLHttpRequest.prototype.abort = function() { + this.readyState = 0; +} +XMLHttpRequest.prototype.open = function(method, url, async, username, password) { + this.readyState = 1; + + this.method = method; + this.url = url; + this.async = async; + this.username = username; + this.password = password; +} +XMLHttpRequest.prototype.send = function(body) { + this.readyState = 3; + + this.responseText = ""; + this.responseXML = null; + + try { + this.responseText = readFile(this.url); + alert("xhr: " + this.url); + } catch (e) { + alert("read exception: " + this.url); + this.responseText = ""; + this.responseXML = null; + } + + if (this.responseText.length > 0) { + try { + this.responseXML = xhr_builder.parse(new Packages.org.xml.sax.InputSource(new Packages.java.io.StringReader(this.responseText))); + } catch (e) { + this.responseXML = null; + } + this.status = 200; + } + else { + alert("empty file: " + this.url); + this.status = 404; + } + + this.readyState = 4; + + if (this.onreadystatechange) + { + if (this.async) + setTimeout(this.onreadystatechange, 0); + else + this.onreadystatechange(); + } +} +XMLHttpRequest.prototype.getResponseHeader = function(header) { + return (this.readyState < 3) ? "" : ""; +} +XMLHttpRequest.prototype.getAllResponseHeaders = function() { + return (this.readyState < 3) ? null : ""; +} +XMLHttpRequest.prototype.setRequestHeader = function(name, value) { +} + +objj_request_xmlhttp = function() +{ + return new XMLHttpRequest(); +} diff --git a/Tools/Utilities/build.xml b/Tools/Utilities/build.xml new file mode 100644 index 0000000000..5e790434e1 --- /dev/null +++ b/Tools/Utilities/build.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/Utilities/cat b/Tools/Utilities/cat new file mode 100755 index 0000000000..58aec17946 --- /dev/null +++ b/Tools/Utilities/cat @@ -0,0 +1,17 @@ +#!/bin/sh + +files="" +outfile="" + +while [ $# -ge 1 ]; do + case $1 in + -o) shift + outfile=$1 + ;; + + *) files="$files $1" + esac + shift +done + +cat $files > $outfile diff --git a/Tools/Utilities/regex-bridge.js b/Tools/Utilities/regex-bridge.js new file mode 100644 index 0000000000..7f3afe76c2 --- /dev/null +++ b/Tools/Utilities/regex-bridge.js @@ -0,0 +1,76 @@ +importClass(java.util.ArrayList); +importClass(java.util.List); +importClass(java.util.regex.Matcher); +importClass(java.util.regex.Pattern); +importClass(java.util.regex.PatternSyntaxException); + +var CachedRegexData = []; + +function regexDataFromRegex(aRegex) +{ + var source = "", + flags = ""; + + if (typeof aRegex == "string") + { + if (CachedRegexData[aRegex]) + return CachedRegexData[aRegex]; + + string = aRegex; + source = aRegex; + } + else + { + string = aRegex.toString(); + + if (CachedRegexData[string]) + return CachedRegexData[string]; + + var index = string.lastIndexOf('/'); + + source = string.substr(0, index); + flags = string.substr(index + 1); + } + + source = source.replace("\\[\\^\\\\\\d\\]", ".", "g"); + source = source.replace("\\[([^\\]]*)\\\\b([^\\]]*)\\]", "[$1\\\\cH$2]", "g") // [...\b...] -> [...\cH...] + source = source.replace("(?&2 + else + echo "OBJJ_HOME not set, default at $OBJJ_HOME doesn't exist, exiting" 1>&2 + exit 2 + fi +fi + +OBJJ_LIB="$OBJJ_HOME/lib" + +BAKE="$OBJJ_LIB/bake.j" + +# convert paths for Cygwin +UNAME=`uname` +case "$UNAME" in + CYGWIN*) + OBJJ_HOME=`cygpath -w "$OBJJ_HOME"` + BAKE=`cygpath -w "$BAKE"` + ;; + *) + ;; +esac + +objj $BAKE $@ diff --git a/Tools/bake/bake.j b/Tools/bake/bake.j new file mode 100644 index 0000000000..6786306a62 --- /dev/null +++ b/Tools/bake/bake.j @@ -0,0 +1,263 @@ +import +importPackage(Packages.java.io); + +var options = { + base : "trunk", + clean : false, + deploy : false, + deployHost : null, + deployPath : null, + skipUpdate : false, + tag : false, + archive : false, + message : null, + templatePath : OBJJ_HOME + "/lib/bake_template.html" +} + +var targetPath, + checkoutsPath, + buildsPath, + productsPath, + version = Math.round(new Date().getTime() / 1000); + +CPLogRegister(CPLogPrint); + +function readConfig(configFile) +{ + var fileData = String(readFile(configFile)); + if (!fileData) + throw new Error("Couldn't read file: " + configFile); + + var configs = CPJSObjectCreateWithJSON(fileData); + + return configs; +} + +function update() +{ + for (var i = 0; i < options.sources.length; i++) + { + var source = options.sources[i]; + var sourcePath = checkoutsPath + "/" + source.path.match(new RegExp("[^\\/]+$"))[0]; + + switch (source.type) + { + case "git-ssh": + if (new File(sourcePath).isDirectory()) + { + CPLog.debug("git pull (" + sourcePath + ")"); + exec("git pull", null, new File(sourcePath)); + } + else + { + var gitPath = "ssh://" + source.server + source.path; + CPLog.debug("git clone (" + gitPath + ")"); + exec(["git", "clone", gitPath, sourcePath]); + } + break; + default : + CPLog.error("Unimplemented: " + source.type); + } + + } + +} + +function build() +{ + var versionedPath = productsPath + "/" + version + "/" + version; + + for (var i = 0; i < options.sources.length; i++) + { + var source = options.sources[i]; + var sourcePath = checkoutsPath + "/" + source.path.match(/[^\/]+$/)[0]; + + for (var j = 0; j < source.parts.length; j++) + { + var part = source.parts[j]; + var fromPath = sourcePath + "/" + part.src; + var toPath = versionedPath + "/" + part.dst; + + if (part.build) + { + var buildCommand = part.build.replace("BUILD_PATH", buildsPath); + CPLog.debug("Building: " + buildCommand); + + exec(buildCommand, null, new File(fromPath)); + + fromPath = buildsPath + "/" + part.copyFrom; + } + + mkdirs(toPath); + + var rsyncCommand = "rsync -aC " + fromPath + "/. " + toPath; + CPLog.debug("Rsyncing: " + rsyncCommand); + exec(rsyncCommand); + } + } + + var results = exec(["bash", "-c", "find "+versionedPath+" \\( -name \"*.sj\" -or -name \"*.j\" \\) -exec cat {} \\; | wc -c | tr -d \" \""]); + + var filesTotal = parseInt(results.stdout); + if (isNaN(filesTotal)) + filesTotal = 0; + + CPLog.debug("FILES_TOTAL=["+filesTotal+"]"); + + var substitutions = options.templateVars; + substitutions.VERSION = version; + substitutions.FILES_TOTAL = filesTotal; + + var template = readFile(options.templatePath); + if (!template) + throw new Error("Couldn't get template"); + + for (var variable in substitutions) + template = template.replace(new RegExp("\\$" + variable, "g"), substitutions[variable]); + + templateBytes = new java.lang.String(template).getBytes(); + templateOutput = new FileOutputStream(productsPath + "/" + version + "/index.html"); + templateOutput.write(templateBytes, 0, templateBytes.length); + templateOutput.close(); + + exec(["tar", "czf", version+".tar.gz", version], null, new File(productsPath)); +} + +function deploy() +{ + for (var i = 0; i < options.deployments.length; i++) + { + var dep = options.deployments[i]; + + exec(["scp", productsPath + "/" + version + ".tar.gz", dep.host + ":~/" + version + ".tar.gz"]); + exec(["ssh", dep.host, + "tar xzf " + version + ".tar.gz; " + + "mkdir -p " + dep.path + "; " + + "mv " + version + "/" + version + "/ " + dep.path + "/" + version + "; " + + "mv " + version + "/index.html " + dep.path + "/index.html; " + + "rm " + version + ".tar.gz; " + + "rmdir " + version + "; " + + "cd " + dep.path + "; " + + "ln -nsf " + version + " Current"]); + } +} + +function main() +{ + var bakefile = "bakefile", + commandOpts = {}; + + for (var i = 0; i < args.length; i++) + { + switch (args[i]) + { + case "--base": + commandOpts.base = args[++i]; + break; + case "--tag": + commandOpts.tag = true; + break; + case "--archive": + commandOpts.archive = true; + break; + case "--clean": + commandOpts.clean = true; + break; + case "--deploy": + commandOpts.deploy = true; + break; + case "--host": + commandOpts.deployHost = args[++i]; + break; + case "--path": + commandOpts.deployPath = args[++i]; + break; + case "--skip-update": + commandOpts.skipUpdate = true + break; + case "--message": + commandOpts.message = args[++i]; + break; + default: + bakefile = args[i]; + } + } + + try + { + var configOpts = readConfig(bakefile); + + for (var i in configOpts) options[i] = configOpts[i]; + for (var i in commandOpts) options[i] = commandOpts[i]; + + targetPath = pwd() + "/" + bakefile.match(/^[^\.]+/)[0] + ".oven"; + mkdirs(targetPath); + + checkoutsPath = targetPath + "/Checkouts" + mkdirs(checkoutsPath); + + buildsPath = targetPath + "/Build" + mkdirs(buildsPath); + + productsPath = targetPath + "/Products" + mkdirs(productsPath); + + if (!options.skipUpdate) + { + CPLog.info("Updating"); + update(); + } + + CPLog.info("Building"); + build(); + + if (options.deploy) + { + CPLog.info("Deploying"); + deploy(); + } + + } + catch (e) + { + CPLog.error(e); + } +} + +function exec() +{ + var runtime = Packages.java.lang.Runtime.getRuntime() + var p = runtime.exec.apply(runtime, arguments); + + var stdout = ""; + var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getInputStream())); + while (s = reader.readLine()) + { + stdout += s; + CPLog.info("exec: " + s); + } + + var stderr = ""; + var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getErrorStream())); + while (s = reader.readLine()) + { + stdout += s; + CPLog.warn("exec: " + s); + } + + var code = p.waitFor(); + + return { code : code, stdout : stdout, stderr : stderr }; +} + +function pwd() +{ + return String(new File("").getAbsolutePath()); +} + +function mkdirs(path) +{ + return new File(path).mkdirs(); +} + +main(); \ No newline at end of file diff --git a/Tools/bake/bake_template.html b/Tools/bake/bake_template.html new file mode 100644 index 0000000000..5f65e50c7c --- /dev/null +++ b/Tools/bake/bake_template.html @@ -0,0 +1,65 @@ + + + + + + + + Loading... + + + + + + +
+ +
+
+
+

Loading $APPLICATION_NAME...

+
+ + + + + + diff --git a/Tools/bake/build.xml b/Tools/bake/build.xml new file mode 100644 index 0000000000..ca975f6b15 --- /dev/null +++ b/Tools/bake/build.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/bake/example.bakefile b/Tools/bake/example.bakefile new file mode 100644 index 0000000000..be3a8cbc9f --- /dev/null +++ b/Tools/bake/example.bakefile @@ -0,0 +1,49 @@ +{ + "sources" : [ + { + "type" : "git-ssh", + "server" : "git@git.myserver.com", + "path" : "/home/git/cappuccino.git", + "parts" : [ + { + "src" : "Objective-J", + "dst" : "Frameworks/Objective-J", + "build" : "ant -DBuild=BUILD_PATH", + "copyFrom" : "Release/Objective-J" + }, + { + "src" : "Foundation", + "dst" : "Frameworks/Foundation", + "build" : "steam -c Release -b BUILD_PATH", + "copyFrom" : "Release/Foundation" + }, + { + "src" : "AppKit", + "dst" : "Frameworks/AppKit", + "build" : "steam -c Release -b BUILD_PATH", + "copyFrom" : "Release/AppKit" + } + ] + }, + { + "type" : "git-ssh", + "server" : "git@git.myserver.com", + "path" : "/home/git/myapp.git", + "parts" : [ + { + "src" : "Editor/Server", + "dst" : "Server" + } + ] + } + ], + "deployments" : [ + { "host" : "deploy@myserver.com", "path" : "/var/www/mysite/public" } + ], + "templatePath" : "index.html", + "templateVars" : { + "APPLICATION_NAME" : "My Application", + "BACKGROUND_COLOR" : "black", + "TEXT_COLOR" : "black" + } +} diff --git a/Tools/build.xml b/Tools/build.xml new file mode 100644 index 0000000000..a363ec65b9 --- /dev/null +++ b/Tools/build.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tools/nib2cib/HelloWorld.nib/classes.nib b/Tools/nib2cib/HelloWorld.nib/classes.nib new file mode 100644 index 0000000000..c4b887e72b --- /dev/null +++ b/Tools/nib2cib/HelloWorld.nib/classes.nib @@ -0,0 +1,8 @@ + + + + + IBVersion + 1 + + diff --git a/Tools/nib2cib/HelloWorld.nib/info.nib b/Tools/nib2cib/HelloWorld.nib/info.nib new file mode 100644 index 0000000000..2511941ff5 --- /dev/null +++ b/Tools/nib2cib/HelloWorld.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 629 + IBOldestOS + 5 + IBOpenObjects + + 1 + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/Tools/nib2cib/HelloWorld.nib/keyedobjects.nib b/Tools/nib2cib/HelloWorld.nib/keyedobjects.nib new file mode 100644 index 0000000000..f3853becd9 --- /dev/null +++ b/Tools/nib2cib/HelloWorld.nib/keyedobjects.nib @@ -0,0 +1,604 @@ + + + + + $archiver + NSKeyedArchiver + $objects + + $null + + $class + + CF$UID + 49 + + NSAccessibilityConnectors + + CF$UID + 46 + + NSAccessibilityOidsKeys + + CF$UID + 47 + + NSAccessibilityOidsValues + + CF$UID + 48 + + NSClassesKeys + + CF$UID + 38 + + NSClassesValues + + CF$UID + 39 + + NSConnections + + CF$UID + 8 + + NSFontManager + + CF$UID + 0 + + NSFramework + + CF$UID + 5 + + NSNamesKeys + + CF$UID + 32 + + NSNamesValues + + CF$UID + 33 + + NSNextOid + 5 + NSObjectsKeys + + CF$UID + 10 + + NSObjectsValues + + CF$UID + 31 + + NSOidsKeys + + CF$UID + 40 + + NSOidsValues + + CF$UID + 41 + + NSRoot + + CF$UID + 2 + + NSVisibleWindows + + CF$UID + 6 + + + + $class + + CF$UID + 4 + + NSClassName + + CF$UID + 3 + + + NSObject + + $classes + + NSCustomObject + NSObject + + $classname + NSCustomObject + + IBCocoaFramework + + $class + + CF$UID + 7 + + NS.objects + + + + $classes + + NSMutableSet + NSSet + NSObject + + $classname + NSMutableSet + + + $class + + CF$UID + 9 + + NS.objects + + + + $classes + + NSMutableArray + NSArray + NSObject + + $classname + NSMutableArray + + + $class + + CF$UID + 30 + + NS.objects + + + CF$UID + 11 + + + CF$UID + 13 + + + CF$UID + 16 + + + + + $class + + CF$UID + 4 + + NSClassName + + CF$UID + 12 + + + NSApplication + + $class + + CF$UID + 29 + + NSClassName + + CF$UID + 28 + + NSFrameSize + + CF$UID + 26 + + NSNextResponder + + CF$UID + 14 + + NSSubviews + + CF$UID + 15 + + NSSuperview + + CF$UID + 27 + + NSWindow + + CF$UID + 14 + + NSvFlags + 268 + + $null + + $class + + CF$UID + 9 + + NS.objects + + + CF$UID + 16 + + + + + $class + + CF$UID + 25 + + NSCell + + CF$UID + 18 + + NSEnabled + + NSFrame + + CF$UID + 17 + + NSNextResponder + + CF$UID + 13 + + NSSuperview + + CF$UID + 13 + + NSWindow + + CF$UID + 14 + + NSvFlags + 268 + + {{166, 124}, {154, 19}} + + $class + + CF$UID + 24 + + NSAlternateContents + + CF$UID + 23 + + NSButtonFlags + -2038152961 + NSButtonFlags2 + 164 + NSCellFlags + -2080244224 + NSCellFlags2 + 134217728 + NSContents + + CF$UID + 19 + + NSControlView + + CF$UID + 16 + + NSKeyEquivalent + + CF$UID + 23 + + NSPeriodicDelay + 400 + NSPeriodicInterval + 75 + NSSupport + + CF$UID + 20 + + + Hello World + + $class + + CF$UID + 22 + + NSName + + CF$UID + 21 + + NSSize + 12 + NSfFlags + 16 + + LucidaGrande + + $classes + + NSFont + NSObject + + $classname + NSFont + + + + $classes + + NSButtonCell + NSActionCell + NSCell + NSObject + + $classname + NSButtonCell + + + $classes + + NSButton + NSControl + NSView + NSResponder + NSObject + + $classname + NSButton + + {480, 272} + $null + NSView + + $classes + + NSCustomView + NSView + NSResponder + NSObject + + $classname + NSCustomView + + + $classes + + NSArray + NSObject + + $classname + NSArray + + + $class + + CF$UID + 30 + + NS.objects + + + CF$UID + 2 + + + CF$UID + 2 + + + CF$UID + 13 + + + + + $class + + CF$UID + 30 + + NS.objects + + + CF$UID + 13 + + + CF$UID + 2 + + + CF$UID + 11 + + + CF$UID + 16 + + + + + $class + + CF$UID + 30 + + NS.objects + + + CF$UID + 34 + + + CF$UID + 35 + + + CF$UID + 36 + + + CF$UID + 37 + + + + Custom View + File's Owner + Application + Round Rect Button (Hello World) + + $class + + CF$UID + 30 + + NS.objects + + + + $class + + CF$UID + 30 + + NS.objects + + + + $class + + CF$UID + 30 + + NS.objects + + + CF$UID + 13 + + + CF$UID + 2 + + + CF$UID + 11 + + + CF$UID + 16 + + + + + $class + + CF$UID + 30 + + NS.objects + + + CF$UID + 42 + + + CF$UID + 43 + + + CF$UID + 44 + + + CF$UID + 45 + + + + 1 + 4 + -3 + 2 + + $class + + CF$UID + 9 + + NS.objects + + + + $class + + CF$UID + 30 + + NS.objects + + + + $class + + CF$UID + 30 + + NS.objects + + + + $classes + + NSIBObjectData + NSObject + + $classname + NSIBObjectData + + + $top + + IB.objectdata + + CF$UID + 1 + + + $version + 100000 + + diff --git a/Tools/nib2cib/NSAppKit.j b/Tools/nib2cib/NSAppKit.j new file mode 100644 index 0000000000..47def697d9 --- /dev/null +++ b/Tools/nib2cib/NSAppKit.j @@ -0,0 +1,10 @@ + +import "NSButton.j" +import "NSCell.j" +import "NSControl.j" +import "NSCustomView.j" +import "NSFont.j" +import "NSIBObjectData.j" +import "NSResponder.j" +import "NSSlider.j" +import "NSView.j" diff --git a/Tools/nib2cib/NSArray.j b/Tools/nib2cib/NSArray.j new file mode 100644 index 0000000000..702e15d960 --- /dev/null +++ b/Tools/nib2cib/NSArray.j @@ -0,0 +1,19 @@ + +import + + +@implementation NSArray : CPObject +{ +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [aCoder _decodeArrayOfObjectsForKey:@"NS.objects"]; +} + +@end + +@implementation NSMutableArray : NSArray +{ +} +@end \ No newline at end of file diff --git a/Tools/nib2cib/NSButton.j b/Tools/nib2cib/NSButton.j new file mode 100644 index 0000000000..1a888e512f --- /dev/null +++ b/Tools/nib2cib/NSButton.j @@ -0,0 +1,88 @@ + +import + +import "NSCell.j" +import "NSControl.j" + + +@implementation CPButton (NSCoding) + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + self = [super NS_initWithCoder:aCoder]; + + if (self) + { + var cell = [aCoder decodeObjectForKey:@"NSCell"]; + + [self setBordered:[cell isBordered]]; + [self setBezelStyle:[cell bezelStyle]]; + + [self setTitle:[cell title]]; + } + + return self; +} + +@end + +@implementation NSButton : CPButton +{ +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self NS_initWithCoder:aCoder]; +} + +- (Class)classForKeyedArchiver +{ + return [CPButton class]; +} + +@end + +@implementation NSButtonCell : NSCell +{ + BOOL _isBordered; + unsigned _bezelStyle; + + CPString _title; +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + var buttonFlags = [aCoder decodeIntForKey:@"NSButtonFlags"], + buttonFlags2 = [aCoder decodeIntForKey:@"NSButtonFlags2"]; + + _isBordered = (buttonFlags & 0x00800000) ? YES : NO; + _bezelStyle = (buttonFlags2 & 0x7) | ((buttonFlags2 & 0x20) >> 2); + + + // NSContents for NSButton is actually the title + _title = [aCoder decodeObjectForKey:@"NSContents"]; + } + + return self; +} + +- (BOOL)isBordered +{ + return _isBordered; +} + +- (int)bezelStyle +{ + return _bezelStyle; +} + +- (CPString)title +{ + return _title; +} + +@end diff --git a/Tools/nib2cib/NSCell.j b/Tools/nib2cib/NSCell.j new file mode 100644 index 0000000000..4ac39357ba --- /dev/null +++ b/Tools/nib2cib/NSCell.j @@ -0,0 +1,45 @@ + +import + +import "NSFont.j" + + +@implementation NSCell : CPObject +{ + CPFont _font; + id _contents; +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + { + _font = [aCoder decodeObjectForKey:@"NSFont"]; + _contents = [aCoder decodeObjectForKey:@"NSContents"]; + } + + return self; +} + +- (id)replacementObjectForCoder:(CPCoder)aCoder +{ + return nil; +} + +//- (void)encodeWithCoder:(CPCoder)aCoder +//{ + + +- (CPFont)font +{ + return _font; +} + +- (id)contents +{ + return _contents; +} + +@end diff --git a/Tools/nib2cib/NSControl.j b/Tools/nib2cib/NSControl.j new file mode 100644 index 0000000000..d5f8ccd793 --- /dev/null +++ b/Tools/nib2cib/NSControl.j @@ -0,0 +1,43 @@ + +import + +import "NSCell.j" +import "NSView.j" + + +@implementation CPControl (NSCoding) + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + self = [super NS_initWithCoder:aCoder]; + + if (self) + { + var cell = [aCoder decodeObjectForKey:@"NSCell"]; + + _value = [cell contents]; + + [self setFont:[cell font]]; + [self setEnabled:[aCoder decodeObjectForKey:@"NSEnabled"]]; + } + + return self; +} + +@end + +@implementation NSControl : CPControl +{ +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self NS_initWithCoder:aCoder]; +} + +- (Class)classForKeyedArchiver +{ + return [CPControl class]; +} + +@end diff --git a/Tools/nib2cib/NSCustomView.j b/Tools/nib2cib/NSCustomView.j new file mode 100644 index 0000000000..4ed40b1fdb --- /dev/null +++ b/Tools/nib2cib/NSCustomView.j @@ -0,0 +1,42 @@ + +import + +import "NSView.j" + + +@implementation NSCustomView : CPView +{ + CPString _className; +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super NS_initWithCoder:aCoder]; + + if (self) + _className = [aCoder decodeObjectForKey:@"NSClassName"]; + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [super encodeWithCoder:aCoder]; + + [aCoder encodeObject:_NSCustomViewMapClassName(_className) forKey:@"CPCustomViewClassNameKey"]; +} + +- (CPString)classForKeyedArchiver +{ + return [CPCustomView class]; +} + +@end + +function _NSCustomViewMapClassName(aClassName) +{ + if (aClassName == @"NSView") + return "CPView"; + + return aClassName; +} diff --git a/Tools/nib2cib/NSFont.j b/Tools/nib2cib/NSFont.j new file mode 100644 index 0000000000..46ec5199ff --- /dev/null +++ b/Tools/nib2cib/NSFont.j @@ -0,0 +1,28 @@ + +import + +@implementation CPFont (NSCoding) + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + // FIXME: bold? + return [self _initWithName:[aCoder decodeObjectForKey:@"NSName"] size:[aCoder decodeDoubleForKey:@"NSSize"] bold:NO]; +} + +@end + +@implementation NSFont : CPFont +{ +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self NS_initWithCoder:aCoder]; +} + +- (Class)classForKeyedArchiver +{ + return [CPFont class]; +} + +@end diff --git a/Tools/nib2cib/NSFoundation.j b/Tools/nib2cib/NSFoundation.j new file mode 100644 index 0000000000..944c5cfc54 --- /dev/null +++ b/Tools/nib2cib/NSFoundation.j @@ -0,0 +1,2 @@ + +import "NSArray.j" diff --git a/Tools/nib2cib/NSIBObjectData.j b/Tools/nib2cib/NSIBObjectData.j new file mode 100644 index 0000000000..9953f38cdc --- /dev/null +++ b/Tools/nib2cib/NSIBObjectData.j @@ -0,0 +1,96 @@ + +/*#import "NSIBObjectData.h" +#import +#import +#import +#import +#import +#import "NSNibKeyedUnarchiver.h" +#import "NSCustomObject.h" +#import +#import +#import */ + +import + +import + +@implementation _CPCibObjectData (NSCoding) + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + self = [self init]; + + if (self) + { +/* NSNibKeyedUnarchiver *keyed=(NSNibKeyedUnarchiver *)coder; + NSMutableDictionary *nameTable=[NSMutableDictionary dictionaryWithDictionary:[keyed externalNameTable]]; + NSArray *uids=[keyed decodeArrayOfUidsForKey:@"NSNamesKeys"]; + int i,count; + id owner; + + if((owner=[nameTable objectForKey:NSNibOwner])!=nil) + [nameTable setObject:owner forKey:@"File's Owner"]; + + [nameTable setObject:[NSFontManager sharedFontManager] forKey:@"Font Manager"]; + + + + var count = [_namesValues count]; + + for(i=0;i + + +@implementation CPResponder (NSCoding) + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + self = [super init]; + + if (self) + [self setNextResponder:[aCoder decodeObjectForKey:@"NSNextResponder"]]; + + return self; +} + +@end + +@implementation NSResponder : CPResponder +{ +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self NS_initWithCoder:aCoder]; +} + +- (Class)classForKeyedArchiver +{ + return [CPResponder class]; +} + +@end \ No newline at end of file diff --git a/Tools/nib2cib/NSSlider.j b/Tools/nib2cib/NSSlider.j new file mode 100644 index 0000000000..09ced75ac5 --- /dev/null +++ b/Tools/nib2cib/NSSlider.j @@ -0,0 +1,47 @@ + +import + +import "NSSlider.j" + + +@implementation CPSlider (CPCoding) + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + self = [super NS_initWithCoder:aCoder]; + + if (self) + { + _minValue = [aCoder decodeDoubleForKey:@"NSMinValue"]; + _maxValue = [aCoder decodeDoubleForKey:@"NSMaxValue"]; +/* _knobThickness=8.0; + _numberOfTickMarks=[keyed decodeIntForKey:@"NSNumberOfTickMarks"]; + _tickMarkPosition=[keyed decodeIntForKey:@"NSTickMarkPosition"]; + _allowsTickMarkValuesOnly=[keyed decodeBoolForKey:@"NSAllowsTickMarkValuesOnly"];*/ + } + + return self; +} + +@end + +@implementation NSSlider : CPSlider +{ +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self NS_initWithCoder:aCoder]; +} + +- (Class)classForKeyedArchiver +{ + return [CPSlider class]; +} + +@end + +@implementation NSSliderCell : NSCell +{ +} +@end diff --git a/Tools/nib2cib/NSView.j b/Tools/nib2cib/NSView.j new file mode 100644 index 0000000000..d6d2508f69 --- /dev/null +++ b/Tools/nib2cib/NSView.j @@ -0,0 +1,96 @@ + +import + + +//unsigned int rotatedFromBase:1; +//unsigned int rotatedOrScaledFromBase:1; +//unsigned int autosizing:6; +//unsigned int autoresizeSubviews:1; +//unsigned int wantsGState:1; +//unsigned int needsDisplay:1; +//unsigned int validGState:1; +//unsigned int newGState:1; +//unsigned int noVerticalAutosizing:1; +//unsigned int frameChangeNotesSuspended:1; +//unsigned int needsFrameChangeNote:1; +//unsigned int focusChangeNotesSuspended:1; +//unsigned int boundsChangeNotesSuspended:1; +//unsigned int needsBoundsChangeNote:1; +//unsigned int removingWithoutInvalidation:1; +//unsigned int interfaceStyle0:1; +//unsigned int needsDisplayForBounds:1; +//unsigned int specialArchiving:1; +//unsigned int interfaceStyle1:1; +//unsigned int retainCount:6; +//unsigned int retainCountOverMax:1; +//unsigned int aboutToResize:1; + +@implementation CPView (NSCoding) + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + _frame = CGRectMakeZero(); + + if ([aCoder containsValueForKey:@"NSFrame"]) + _frame = [aCoder decodeRectForKey:@"NSFrame"]; + else if ([aCoder containsValueForKey:@"NSFrameSize"]) + _frame.size = [aCoder decodeSizeForKey:@"NSFrameSize"]; + + self = [super NS_initWithCoder:aCoder]; + + if (self) + { + _bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame)); + + _window = [aCoder decodeObjectForKey:@"NSWindow"]; + _superview = [aCoder decodeObjectForKey:@"NSSuperview"]; + _subviews = [aCoder decodeObjectForKey:@"NSSubviews"]; + + if (!_subviews) + _subviews = []; + + var vFlags = [aCoder decodeIntForKey:@"NSvFlags"]; + + _autoresizingMask = vFlags & (0x3F << 1); + _autoresizesSubviews = vFlags & (1 << 8); + + _hitTests = YES; + _isHidden = NO;//[aCoder decodeObjectForKey:CPViewIsHiddenKey]; + _opacity = 1.0;//[aCoder decodeIntForKey:CPViewOpacityKey]; + + if (YES/*[_superview isFlipped]*/) + { + var height = CGRectGetHeight([self bounds]), + count = [_subviews count]; + + while (count--) + { + var subview = _subviews[count], + frame = [subview frame]; + + [subview setFrameOrigin:CGPointMake(CGRectGetMinX(frame), height - CGRectGetMaxY(frame))]; + } + } + } + + return self; +} + +@end + +@implementation NSView : CPView +{ +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [[CPView alloc] NS_initWithCoder:aCoder]; +} + +- (Class)classForKeyedArchiver +{ + return [CPView class]; +} + +@end + diff --git a/Tools/nib2cib/nib2cib b/Tools/nib2cib/nib2cib new file mode 100755 index 0000000000..194aecc183 --- /dev/null +++ b/Tools/nib2cib/nib2cib @@ -0,0 +1,6 @@ +#!/bin/sh + +SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0") +SELF_DIR=`dirname $SELF_PATH` + +$SELF_DIR/objj $SELF_DIR/../nib2cib/nib2cib.j $@ diff --git a/Tools/nib2cib/nib2cib.j b/Tools/nib2cib/nib2cib.j new file mode 100644 index 0000000000..65c03f233e --- /dev/null +++ b/Tools/nib2cib/nib2cib.j @@ -0,0 +1,83 @@ + +import + +import + +import "NSFoundation.j" +import "NSAppKit.j" + +importClass(java.io.FileWriter); +importClass(java.io.BufferedWriter); + + +function exec(command) +{ + var p = Packages.java.lang.Runtime.getRuntime().exec(jsArrayToJavaArray(command)); + var result = p.waitFor(); + + var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getInputStream())); + while (s = reader.readLine()) + print(s); + + var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getErrorStream())); + while (s = reader.readLine()) + print(s); + + return result; +} + +// Make sure we can read the file +if (!(new Packages.java.io.File(args[0])).canRead()) +{ + print("Could not read file at " + args[0]); + return; +} + +// Compile xib or nib to make sure we have a non-new format nib. +var temporaryNibFile = Packages.java.io.File.createTempFile("temp", ".nib"), + temporaryNibFilePath = temporaryNibFile.getAbsolutePath(); + +temporaryNibFile.deleteOnExit(); + +if (exec(["/usr/bin/ibtool", args[0], "--compile", temporaryNibFilePath])) +{ + print("Could not compile file at " + args[0]); + return; +} + +// Convert from binary plist to XML plist +var temporaryPlistFile = Packages.java.io.File.createTempFile("temp", ".plist"), + temporaryPlistFilePath = temporaryPlistFile.getAbsolutePath(); + +temporaryPlistFile.deleteOnExit(); + +if (exec(["/usr/bin/plutil", "-convert", "xml1", temporaryNibFilePath, "-o", temporaryPlistFilePath])) +{ + print("Could not convert to xml plist for file at " + args[0]); + return; +} + +var data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:temporaryPlistFilePath] returningResponse:nil error:nil]; + +// Minor NSKeyedArchive to CPKeyedArchive conversion. +[data setString:[data string].replace(/\\s*CF\$UID\s*\<\/key\>/g, "CP$UID")]; + + +// Unarchive the NS data +var unarchiver = [[CPKeyedUnarchiver alloc] initForReadingWithData:data], + objectData = [unarchiver decodeObjectForKey:@"IB.objectdata"], + + data = [CPData data], + archiver = [[CPKeyedArchiver alloc] initForWritingWithMutableData:data]; + +// Re-archive the CP data. +[archiver encodeObject:objectData forKey:@"CPCibObjectDataKey"]; +[archiver finishEncoding]; + +var writer = new BufferedWriter(new FileWriter("MainMenu.cib"));//outputPath + name.substring(0, args[0].indexOf(".")) + ".o")); + +writer.write([data string]); + +writer.close(); + + diff --git a/Tools/nib2cib/project.properties b/Tools/nib2cib/project.properties new file mode 100644 index 0000000000..b0555d24f3 --- /dev/null +++ b/Tools/nib2cib/project.properties @@ -0,0 +1,5 @@ + +CPBundleExecutable=nibtocib +CPBundleIdentifier=com.280North.nibtocib +CPBundleName=nibtocib +CPBundlePackageType=280N diff --git a/Tools/objj/build.xml b/Tools/objj/build.xml new file mode 100644 index 0000000000..f6894ba9c4 --- /dev/null +++ b/Tools/objj/build.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/objj/objj b/Tools/objj/objj new file mode 100644 index 0000000000..ad415c0e76 --- /dev/null +++ b/Tools/objj/objj @@ -0,0 +1,51 @@ +#!/bin/sh + +# if OBJJ_HOME isn't set, try to determine it out +if [ -z $OBJJ_HOME ]; then + SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0") + SELF_DIR=`dirname $SELF_PATH` + + export OBJJ_HOME="`dirname $SELF_DIR`/share/objj" + + if [ -d $OBJJ_HOME ]; then + echo "OBJJ_HOME not set, defaulting to $OBJJ_HOME" 1>&2 + else + echo "OBJJ_HOME not set, default at $OBJJ_HOME doesn't exist, exiting" 1>&2 + exit 2 + fi +fi + +OBJJ_LIB="$OBJJ_HOME/lib" +CLASSPATH="$OBJJ_LIB:$OBJJ_LIB/js.jar:$CLASSPATH" + +OBJJ="$OBJJ_LIB/objj.js" +RLWRAP=`which rlwrap` +FRAMEWORKS="$STEAM_BUILD" + +# if there's a "lib" directory, add all the jars from it +if [ -d "lib" ]; then + JARS=`find ./lib -name '*.jar' 2> /dev/null | tr "\n" ":"` + CLASSPATH="$JARS:$CLASSPATH" +fi + +# convert paths for Cygwin +UNAME=`uname` +case "$UNAME" in + CYGWIN*) + OBJJ_HOME=`cygpath -wp "$OBJJ_HOME"` + OBJJ_LIB=`cygpath -wp "$OBJJ_LIB"` + OBJJ=`cygpath -wp "$OBJJ"` + FRAMEWORKS=`cygpath -w "$FRAMEWORKS"` + STEAM_BUILD=`cygpath -w "$STEAM_BUILD"` + CLASSPATH=`cygpath -wp "$CLASSPATH"` + ;; + *) + ;; +esac + +if [ -e $STEAM_BUILD/Objective-J.build/Release/Rhino ]; then + $RLWRAP java -classpath $CLASSPATH objj $OBJJ_LIB $STEAM_BUILD $@ + #$RLWRAP java -classpath $CLASSPATH org.mozilla.javascript.tools.shell.Main $OBJJ $OBJJ_LIB $STEAM_BUILD $@ +else + echo "Missing frameworks at $STEAM_BUILD/Objective-J.build/Release/Rhino" 1>&2 +fi diff --git a/Tools/objj/objj.js b/Tools/objj/objj.js new file mode 100644 index 0000000000..0c975b5980 --- /dev/null +++ b/Tools/objj/objj.js @@ -0,0 +1,47 @@ +debug = false; +args = arguments; + +var helpersBase = arguments[0]; +var STEAM_BUILD = arguments[1]; +arguments.splice(0, 2); + +if (typeof window == "undefined") +{ + print("Loading Objective-J bridge."); + load(helpersBase+'/bridge.js'); +} + +if (typeof objj_import == "undefined") +{ + print("Loading Objective-J."); + load(STEAM_BUILD+'/Objective-J.build/Release/Rhino/Objective-J.js'); +} + +OBJJ_INCLUDE_PATHS = [STEAM_BUILD+'/Release']; + +if (arguments.length > 0) +{ + var main_file = arguments[0]; + arguments.splice(0, 1); + + if (debug) + print("Loading: " + main_file); + + objj_import(main_file, NO); + + serviceTimeouts(); + + if (debug) + print("Done!"); +} +else if (typeof objj_console != "undefined") +{ + if (debug) + print("Starting Objective-J console."); + + objj_console(); +} +else +{ + print("Error: No file provided or console available.") +} diff --git a/Tools/objjc/build.xml b/Tools/objjc/build.xml new file mode 100644 index 0000000000..de9fb7d986 --- /dev/null +++ b/Tools/objjc/build.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/objjc/objjc b/Tools/objjc/objjc new file mode 100644 index 0000000000..f60e89a471 --- /dev/null +++ b/Tools/objjc/objjc @@ -0,0 +1,31 @@ +#!/bin/sh + +# if OBJJ_HOME isn't set, try to determine it out +if [ -z $OBJJ_HOME ]; then + SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0") + SELF_DIR=`dirname $SELF_PATH` + + export OBJJ_HOME="`dirname $SELF_DIR`/share/objj" + + if [ -d $OBJJ_HOME ]; then + echo "OBJJ_HOME not set, defaulting to $OBJJ_HOME" 1>&2 + else + echo "OBJJ_HOME not set, default at $OBJJ_HOME doesn't exist, exiting" 1>&2 + exit 2 + fi +fi + +OBJJ_LIB="$OBJJ_HOME/lib" +CLASSPATH="$OBJJ_LIB:$OBJJ_LIB/js.jar:$CLASSPATH" + +# convert paths for Cygwin +UNAME=`uname` +case "$UNAME" in + CYGWIN*) + CLASSPATH=`cygpath -wp "$CLASSPATH"` + ;; + *) + ;; +esac + +java -classpath $CLASSPATH objjc $@ diff --git a/Tools/objjc/objjc.js b/Tools/objjc/objjc.js new file mode 100644 index 0000000000..3833432d41 --- /dev/null +++ b/Tools/objjc/objjc.js @@ -0,0 +1,106 @@ + +importPackage(java.lang); + +importClass(java.io.File); +importClass(java.io.BufferedReader); +importClass(java.io.FileReader); +importClass(java.io.BufferedWriter); +importClass(java.io.FileWriter); + + +function exec(command) +{ + var p = Packages.java.lang.Runtime.getRuntime().exec(command);//jsArrayToJavaArray(command)); + + var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getInputStream())); + while (s = reader.readLine()) + System.out.println(s); + + var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getErrorStream())); + while (s = reader.readLine()) + System.out.println(s); + + var result = p.waitFor(); + + return result; +} + +function preprocess(aFilePath, outFilePath, gccArgs, shouldObjjPreprocess) +{ + print("Statically Preprocessing " + aFilePath); + + var tmpFile = java.io.File.createTempFile("OBJJC", ""); + tmpFile.deleteOnExit(); + + // -E JUST preprocess. + // -x c Interpret language as C -- closest thing to JavaScript. + // -P Don't generate #line directives + var gccComponents = ["gcc", "-E", "-x", "c", "-P", aFilePath, "-o", shouldObjjPreprocess ? tmpFile.getAbsolutePath() : outFilePath], + index = gccArgs.length; + + // Add custom gcc arguments. + while (index--) + gccComponents.splice(5, 0, gccArgs[index]); + + exec(gccComponents); + + if (!shouldObjjPreprocess) + return; + + // Read file and preprocess it. + var reader = new BufferedReader(new FileReader(tmpFile)), + fileContents = ""; + + // Get contents of the file + while (reader.ready()) + fileContents += reader.readLine() + '\n'; + + reader.close(); + + // Write file. + var writer = new BufferedWriter(new FileWriter(outFilePath)); + + writer.write(objj_preprocess_file(new File(aFilePath).getName(), fileContents)); + + writer.close(); +} + +function main() +{ + var filePaths = [], + outFilePaths = [], + + index = 0, + count = args.length, + + gccArgs = [], + + shouldObjjPreprocess = true; + + for (; index < count; ++index) + { + if (args[index] == "-o") + { + if (++index < count) + outFilePaths.push(args[index]); + } + + else if (args[index].indexOf("-D") == 0) + gccArgs.push(args[index]) + + else if (args[index].indexOf("-U") == 0) + gccArgs.push(args[index]); + + else if (args[index].indexOf("-E") == 0) + shouldObjjPreprocess = false; + + else + filePaths.push(args[index]); + } + + for (index = 0, count = filePaths.length; index < count; ++index) + preprocess(filePaths[index], outFilePaths[index], gccArgs, shouldObjjPreprocess); +} + +args = arguments; +main(); diff --git a/Tools/steam/build.xml b/Tools/steam/build.xml new file mode 100644 index 0000000000..f4cbf2b1bc --- /dev/null +++ b/Tools/steam/build.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/steam/sjheader.txt b/Tools/steam/sjheader.txt new file mode 100644 index 0000000000..57390737c9 --- /dev/null +++ b/Tools/steam/sjheader.txt @@ -0,0 +1 @@ +@STATIC;1.0; \ No newline at end of file diff --git a/Tools/steam/steam b/Tools/steam/steam new file mode 100644 index 0000000000..5195a437d0 --- /dev/null +++ b/Tools/steam/steam @@ -0,0 +1,31 @@ +#!/bin/sh + +# if OBJJ_HOME isn't set, try to determine it out +if [ -z $OBJJ_HOME ]; then + SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0") + SELF_DIR=`dirname $SELF_PATH` + + export OBJJ_HOME="`dirname $SELF_DIR`/share/objj" + + if [ -d $OBJJ_HOME ]; then + echo "OBJJ_HOME not set, defaulting to $OBJJ_HOME" 1>&2 + else + echo "OBJJ_HOME not set, default at $OBJJ_HOME doesn't exist, exiting" 1>&2 + exit 2 + fi +fi + +OBJJ_LIB="$OBJJ_HOME/lib" +CLASSPATH="$OBJJ_LIB:$OBJJ_LIB/js.jar:$CLASSPATH" + +# convert paths for Cygwin +UNAME=`uname` +case "$UNAME" in + CYGWIN*) + CLASSPATH=`cygpath -wp "$CLASSPATH"` + ;; + *) + ;; +esac + +java -classpath $CLASSPATH steam $@ diff --git a/Tools/steam/steam.js b/Tools/steam/steam.js new file mode 100644 index 0000000000..1c0476ba32 --- /dev/null +++ b/Tools/steam/steam.js @@ -0,0 +1,504 @@ + +importPackage(java.lang); +importPackage(java.util); + +importClass(java.io.File); +importClass(java.io.BufferedReader); +importClass(java.io.FileReader); +importClass(java.io.BufferedWriter); +importClass(java.io.FileWriter); +importClass(java.io.SequenceInputStream); + + +function Target(dictionary) +{ + this._name = dictionary_getValue(dictionary, "name"); + + this._exclusions = []; + + var exclusions = dictionary_getValue(dictionary, "Excluded"); + + if (exclusions) + { + var index = 0, + count = exclusions.length; + + for (; index < count; ++index) + { + var file = new File(exclusions[index]).getCanonicalFile(); + + if (file.isDirectory()) + { + var files = getFiles(file); + + if (files.length) + this._exclusions = this._exclusions.concat(files); + } + else + this._exclusions.push(file); + } + } + + return this; +} + +Target.prototype.name = function() +{ + return this._name; +} + +Target.prototype.exclusions = function() +{ + return this._exclusions; +} + +function Configuration(dictionary) +{ + this._name = dictionary_getValue(dictionary, "name"); + + return this; +} + +Configuration.prototype.name = function() +{ + return this._name; +} + +function Project(/*String*/ aFilePath, /*String*/ aBuildPath) +{ + this._file = new File(aFilePath).getCanonicalFile(); + this._root = this._file.getParentFile(); + + // Read the project file (its just a plist). + this._properties = readPlist(this._file); + + // Read existing Info.plist if it exists. + var infoPlist = new File(this._root.getAbsolutePath() + "/Info.plist"); + + if (infoPlist.exists()) + this._infoDictionary = readPlist(new File(this._root.getAbsolutePath() + "/Info.plist")); + else + this._infoDictionary = new objj_dictionary(); + + this.buildPath = aBuildPath; + + // Covert target dictionaries to target objects. + var targetDictionaries = dictionary_getValue(this._properties, "Targets"); + + this._targets = []; + + for (index = 0, count = targetDictionaries.length; index < count; ++index) + this._targets.push(new Target(targetDictionaries[index])); + + this.setActiveTarget(this._targets[0]); + + // Covert target dictionaries to target objects. + var configurationDictionaries = dictionary_getValue(this._properties, "Configurations"); + + this._configurations = []; + + for (index = 0, count = configurationDictionaries.length; index < count; ++index) + this._configurations.push(new Configuration(configurationDictionaries[index])); + + this.setActiveConfiguration(this._configurations[0]); + + // Grab the Resources Directory (if it exists) + this._resources = new File(this._root.getAbsolutePath() + "/Resources/").getCanonicalFile(); + + return this; +} + +Project.prototype.targetWithName = function(/*String*/ aName) +{ + var targets = this._targets, + index = 0, + count = targets.length; + + for (; index < count; ++index) + { + var target = targets[index]; + + if (target.name() == aName) + return target; + } + + return null; +} + +Project.prototype.configurationWithName = function(/*String*/ aName) +{ + var configurations = this._configurations, + index = 0, + count = configurations.length; + + for (; index < count; ++index) + { + var configuration = configurations[index]; + + if (configuration.name() == aName) + return configuration; + } + + return null; +} + +Project.prototype.setActiveTarget = function(/*Target*/ aTarget) +{ + this._activeTarget = aTarget; + + this._buildProducts = null; + this._buildIntermediates = null; + this._buildObjects = null; +} + +Project.prototype.setActiveConfiguration = function(/*Configuration*/ aConfiguration) +{ + this._activeConfiguration = aConfiguration; + + this._buildProducts = null; + this._buildIntermediates = null; + this._buildObjects = null; +} + +Project.prototype.buildProducts = function() +{ + if (!this._buildProducts) + { + this._buildProducts = new File(this.buildPath + '/' + this._activeConfiguration.name() + '/' + this._activeTarget.name()).getCanonicalFile(); + this._buildProducts.mkdirs(); + } + + return this._buildProducts; +} + +Project.prototype.buildIntermediates = function() +{ + if (!this._buildIntermediates) + { + this._buildIntermediates = new File(this.buildPath + '/' + this._activeTarget.name() + ".build/" + this._activeConfiguration.name()).getCanonicalFile(); + this._buildIntermediates.mkdirs(); + } + + return this._buildIntermediates; +} + +Project.prototype.buildObjects = function() +{ + if (!this._buildObjects) + { + this._buildObjects = new File(this.buildIntermediates().getAbsolutePath() + '/' + this._activeTarget.name()).getCanonicalFile(); + this._buildObjects.mkdirs(); + } + + return this._buildObjects; +} + +Project.prototype.build = function() +{ + var jFiles = getFiles(this._root, "j", this._activeTarget.exclusions()), + replacedFiles = []; + hasModifiedJFiles = false; + shouldObjjPreprocess = true, + objjcComponents = ["objjc"]; + + if (!shouldObjjPreprocess) + objjcComponents.push("-E"); + + var buildObjects = this.buildObjects(), + buildProducts = this.buildProducts(); + + for (index = 0, count = jFiles.length; index < count; ++index) + { + var file = jFiles[index], + builtFile = new File(buildObjects.getAbsolutePath() + '/' + getFileNameWithoutExtension(file) + '.o'); + + replacedFiles.push(file.getName() + ""); + + if (builtFile.exists() && file.lastModified() < builtFile.lastModified()) + continue; + else + hasModifiedJFiles = true; + + objjcComponents.push(file.getAbsolutePath()); + + objjcComponents.push("-o"); + objjcComponents.push(buildObjects.getAbsolutePath() + '/' + getFileNameWithoutExtension(file) + '.o'); + } + + exec(objjcComponents, true); + + if (hasModifiedJFiles) + { + var oFiles = getFiles(buildObjects, 'o'), + sjFile = new File(buildProducts.getCanonicalPath() + '/' + this._activeTarget.name() + ".sj"), + + catComponents = [OBJJ_HOME + "/lib/cat", OBJJ_HOME + "/lib/sjheader.txt"]; + + for (index = 0, count = oFiles.length; index < count; ++index) + catComponents.push(oFiles[index].getCanonicalPath()); + + catComponents.push("-o"); + catComponents.push(sjFile.getCanonicalPath()); + + exec(catComponents); + } + + dictionary_setValue(this._infoDictionary, "CPBundleExecutable", this._activeTarget.name() + ".sj"); + dictionary_setValue(this._infoDictionary, "CPBundleReplacedFiles", replacedFiles); + + this.writeInfoPlist(); + + this.copyResources(); + + // FIXME: This should be, if this is an app... + if (dictionary_getValue(this._infoDictionary, "CPBundlePackageType") == "280N") + this.copyIndexFile(); +} + +Project.prototype.clean = function() +{ + exec(["rm", "-rf", this.buildIntermediates().getAbsolutePath()], true); + exec(["rm", "-rf", this.buildProducts().getAbsolutePath()], true); +} + +Project.prototype.copyIndexFile = function() +{ + var indexFile = new File(this._root.getAbsolutePath() + "/index.html").getCanonicalFile(); + + if (indexFile.exists()) + exec(["rsync", "-avz", indexFile.getAbsolutePath(), this.buildProducts().getAbsolutePath()]); +} + +Project.prototype.writeInfoPlist = function() +{ + var writer = new BufferedWriter(new FileWriter(this.buildProducts().getAbsolutePath() + '/' + "Info.plist")); + + writer.write(CPPropertyListCreateXMLData(this._infoDictionary).string); + + writer.close(); +} + +Project.prototype.copyResources = function() +{ + if (this._resources.exists()) + exec(["rsync", "-avz", this._resources.getAbsolutePath() + '/', this.buildProducts().getAbsolutePath() + "/Resources"]); +} + +function main() +{ + var index = 0, + count = arguments.length, + + actions = [], + + targetName = null, + configurationName = null, + + buildPath = null, + projectFilePath = null; + + for (; index < count; ++index) + { + switch (arguments[index]) + { + case "-f": if (index + 1 == count) + throw "-f needs project file."; + + projectFilePath = arguments[++index]; + break; + + case "-t": if (index + 1 == count) + throw "-t needs a target"; + + targetName = arguments[++index]; + break; + + case "-c": if (index + 1 == count) + throw "-c needs configuration"; + + configurationName = arguments[++index]; + break; + + case "-b": if (index + 1 == count) + throw "-b needs a build location"; + + buildPath = arguments[++index]; + break; + + case "clean": + case "build": actions.push(arguments[index]); + break; + } + } + + // If no project file was specified, look for one. + if (!projectFilePath) + { + var candidates = getFiles(new File('.'), "steam"); + + if (candidates.length < 1) + throw "No project file specified or found."; + + projectFilePath = candidates[0]; + } + + // Construct the Build Directory + if (!buildPath) + { + buildPath = System.getenv("STEAM_BUILD"); + + if (!buildPath) + buildPath = "Build"; + } + + var project = new Project(projectFilePath, buildPath); + + if (targetName) + { + var target = project.targetWithName(targetName); + + if (!target) + throw "Target \"" + targetName + "\" not found in project."; + + project.setActiveTarget(target); + } + + if (configurationName) + { + var configuration = project.configurationWithName(configurationName); + + if (!configuration) + throw "Configuration \"" + configurationName + "\" not found in project."; + + project.setActiveConfiguration(configuration); + } + + if (!actions.length) + actions.push("build"); + + for (index = 0, count = actions.length; index < count; ++index) + project[actions[index]](); +} + +function fileArrayContainsFile(/*Array*/ files, /*File*/ aFile) +{ + var index = 0, + count = files.length; + + for (; index < count; ++index) + if (files[index].equals(aFile)) + return true; + + return false; +} + +function getFiles(/*File*/ sourceDirectory, /*String*/ extension, /*Array*/ exclusions) +{ + var matches = [], + files = sourceDirectory.listFiles(); + + if (files) + { + var index = 0, + count = files.length; + + for (; index < count; ++index) + { + var file = files[index].getCanonicalFile(), + name = String(file.getName()); + + if ((!extension || name.substring(name.length - extension.length - 1) == ("." + extension)) && (!exclusions || !fileArrayContainsFile(exclusions, file))) + matches.push(file); + + if (file.isDirectory()) + matches = matches.concat(getFiles(file, extension, exclusions)); + } + } + + return matches; +} + +function readPlist(/*File*/ aFile) +{ + var reader = new BufferedReader(new FileReader(aFile)), + fileContents = ""; + + // Get contents of the file + while (reader.ready()) + fileContents += reader.readLine() + '\n'; + + reader.close(); + + var data = new objj_data(); + data.string = fileContents; + + return new CPPropertyListCreateFromData(data); +} + +function exec(/*Array*/ command, /*Boolean*/ showOutput) +{ + var line = "", + output = "", + + process = Packages.java.lang.Runtime.getRuntime().exec(command),//jsArrayToJavaArray(command)); + reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(process.getInputStream())); + + while (line = reader.readLine()) + { + if (showOutput) + System.out.println(line); + + output += line + '\n'; + } + + reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(process.getErrorStream())); + + while (line = reader.readLine()) + System.out.println(line); + + try + { + if (process.waitFor() != 0) + System.err.println("exit value = " + process.exitValue()); + } + catch (anException) + { + System.err.println(anException); + } + + return output; +} + +function getFileName(aPath) +{ + var index = aPath.lastIndexOf('/'); + + if (index == -1) + return aPath; + + return aPath.substr(index + 1); +} + +function getFileNameWithoutExtension(aFile) +{ + var name = aFile.getName(), + index = name.lastIndexOf('.'); + + if (index == -1 || index == 0) + return name; + + return name.substr(0, index); +} + +function getFileExtension(aPath) +{ + var slash = aPath.lastIndexOf('/'), + period = aPath.lastIndexOf('.'); + + if (period < slash || (period == slash + 1)) + return ""; + + return aPath.substr(period + 1); +} + +main.apply(main, arguments); diff --git a/Tools/steam/steam.xml b/Tools/steam/steam.xml new file mode 100644 index 0000000000..5d994b3c64 --- /dev/null +++ b/Tools/steam/steam.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.xml b/build.xml new file mode 100644 index 0000000000..d0f4f2dec7 --- /dev/null +++ b/build.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common.xml b/common.xml new file mode 100644 index 0000000000..262ef1927d --- /dev/null +++ b/common.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file