Skip to content
Browse files

Initial commit.

Reviewed by francisco, ross and tom.
  • Loading branch information...
0 parents commit a97bb72800a90c65fe25ab1e20b55a9a9f0839e8 @tolmasky tolmasky committed
Sorry, we could not display the entire diff because too many files (376) changed.
60 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"
46 AppKit/AppKit.steam
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>name</key>
+ <string>AppKit</string>
+ <key>Targets</key>
+ <array>
+ <dict>
+ <key>name</key>
+ <string>AppKit</string>
+ <key>Excluded</key>
+ <array>
+ <string>CPOutlineView.j</string>
+ <string>CPTableColumn.j</string>
+ <string>CPTableView.j</string>
+ </array>
+ </dict>
+ </array>
+ <key>Configurations</key>
+ <array>
+ <dict>
+ <key>name</key>
+ <string>Debug</string>
+ <key>Settings</key>
+ <dict>
+ <key>PREPROCESS</key>
+ <true/>
+ <key>FLAGS</key>
+ <string>-DDEBUG</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Release</string>
+ <key>Settings</key>
+ <dict>
+ <key>PREPROCESS</key>
+ <true/>
+ <key>PREINTERPRET</key>
+ <true/>
+ </dict>
+ </dict>
+ </array>
+</dict>
+</plist>
191 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 <Foundation/CPObject.j>
+
+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
552 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 <Foundation/CPBundle.j>
+
+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];
+}
604 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
130 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
546 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 <Foundation/CPArray.j>
+import <Foundation/CPData.j>
+import <Foundation/CPIndexSet.j>
+import <Foundation/CPKeyedArchiver.j>
+import <Foundation/CPKeyedUnarchiver.j>
+
+import <AppKit/CPView.j>
+
+
+@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
394 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 <Foundation/CPObject.j>
+
+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)