Initial commit.
Reviewed by francisco, ross and tom.
@@ -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"
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
[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<hex.length; i++)
|
||||
if(hexCharacters.indexOf(hex.charAt(i)) == -1)
|
||||
return null;
|
||||
|
||||
var red = (hexCharacters.indexOf(hex.charAt(0)) * 16 + hexCharacters.indexOf(hex.charAt(1))) / 255.0;
|
||||
var green = (hexCharacters.indexOf(hex.charAt(2)) * 16 + hexCharacters.indexOf(hex.charAt(3))) / 255.0;
|
||||
var blue = (hexCharacters.indexOf(hex.charAt(4)) * 16 + hexCharacters.indexOf(hex.charAt(5))) / 255.0;
|
||||
|
||||
return [red, green, blue, 1.0];
|
||||
}
|
||||
|
||||
function integerToBytes(integer, length) {
|
||||
if (!length)
|
||||
length = (integer == 0) ? 1 : Math.round((Math.log(integer)/Math.log(2))/8+0.5);
|
||||
|
||||
var bytes = new Array(length);
|
||||
for (var i = length-1; 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
|
||||
@@ -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<cookieValue.length; i++)
|
||||
result.push([CPColor colorWithHexString: cookieValue[i]]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CPArray)saveColorList
|
||||
{
|
||||
var result = [];
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||
for(var i=0; i<_swatches.length; i++)
|
||||
result.push([[[_swatches[i] subviews][0] backgroundColor] hexString]);
|
||||
|
||||
var future = new Date();
|
||||
future.setYear(2019);
|
||||
|
||||
[_swatchCookie setValue: CPJSObjectCreateJSON(result) expires:future domain: nil];
|
||||
}
|
||||
|
||||
- (void)setColorPanel:(CPColorPanel)panel
|
||||
{
|
||||
_colorPanel = panel;
|
||||
}
|
||||
|
||||
- (CPColorPanel)colorPanel
|
||||
{
|
||||
return _colorPanel;
|
||||
}
|
||||
|
||||
- (CPColor)colorAtIndex:(int)index
|
||||
{
|
||||
return [[_swatches[index] subviews][0] backgroundColor];
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor atIndex:(int)index
|
||||
{
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||
[[_swatches[index] subviews][0] setBackgroundColor:aColor];
|
||||
[self saveColorList];
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
if(point.x > [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 <CPDraggingInfo>)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 <CPDraggingInfo>)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
|
||||
@@ -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 <Foundation/Foundation.j>
|
||||
import <AppKit/CPView.j>
|
||||
import <AppKit/CPImage.j>
|
||||
import <AppKit/CPImageView.j>
|
||||
|
||||
|
||||
@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
|
||||
@@ -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 <Foundation/CPString.j>
|
||||
|
||||
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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
|
||||
|
||||
@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
|
||||
@@ -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 <Foundation/CPString.j>
|
||||
import <Foundation/CPArray.j>
|
||||
|
||||
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 };
|
||||
}
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPBundle.j>
|
||||
|
||||
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
|
||||
@@ -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 <AppKit/CPView.j>
|
||||
import <AppKit/CPEvent.j>
|
||||
import <AppKit/CPPasteboard.j>
|
||||
import <AppKit/CPImageView.j>
|
||||
|
||||
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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
|
||||
#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];
|
||||
}
|
||||
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
|
||||
|
||||
@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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
|
||||
import <AppKit/CPFont.j>
|
||||
|
||||
|
||||
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]];
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
import <Foundation/CPBundle.j>
|
||||
import <Foundation/CPRunLoop.j>
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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 <Foundation/Foundation.j>
|
||||
|
||||
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<count; i++)
|
||||
{
|
||||
var swatches = _themes[i].swatches;
|
||||
var containerView = [[CPView alloc] initWithFrame: CPRectMake(0, 1+ 20*i, width, 20)];
|
||||
[containerView setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
for(var j=0; j<swatches.length; j++)
|
||||
{
|
||||
var outerView = [[CPView alloc] initWithFrame: CGRectMake(2+20*j, 1, 18, 18)],
|
||||
innerView = [[_CPColorView alloc] initWithFrame: CGRectInset([outerView bounds], 1.0, 1.0)];
|
||||
|
||||
[innerView setTarget: self];
|
||||
[innerView setAction: @selector(selectedColor:)];
|
||||
[innerView setBackgroundColor: [CPColor colorWithHexString: swatches[j].hexColor]];
|
||||
|
||||
[outerView setBackgroundColor: [CPColor blackColor]];
|
||||
[outerView addSubview: innerView];
|
||||
|
||||
[containerView addSubview: outerView];
|
||||
}
|
||||
|
||||
var label = [[CPTextField alloc] initWithFrame: CPRectMake(102, 0, width - 102, 20)];
|
||||
[label setStringValue: _themes[i].title];
|
||||
[label setFont: [CPFont systemFontOfSize: 11.0]];
|
||||
[label setLineBreakMode: CPLineBreakByTruncatingTail];
|
||||
[label setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
[containerView addSubview: label];
|
||||
|
||||
if(i%2 == 1)
|
||||
[containerView setBackgroundColor: _alternateBGColor];
|
||||
|
||||
[self addSubview: containerView];
|
||||
}
|
||||
|
||||
[self setFrameSize: CPSizeMake(width, (count)*20 + 2)];
|
||||
}
|
||||
|
||||
-(void)selectedColor:(id)sender
|
||||
{
|
||||
[_delegate chooseColor:[sender backgroundColor]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPColorView : CPControl
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
-(id)initWithFrame:(CPRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame: aFrame];
|
||||
|
||||
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
||||
|
||||
var bounds = CPRectMake(0, 0, 15, 15),
|
||||
point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
var outerView = [[CPView alloc] initWithFrame: bounds],
|
||||
innerView = [[CPView alloc] initWithFrame: CGRectInset([outerView bounds], 1.0, 1.0)];
|
||||
|
||||
[innerView setBackgroundColor: [self backgroundColor]];
|
||||
[outerView setBackgroundColor: [CPColor blackColor]];
|
||||
|
||||
[outerView addSubview: innerView];
|
||||
|
||||
[self dragView: outerView
|
||||
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
|
||||
@@ -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 <Foundation/CPCoder.j>
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
|
||||
import <AppKit/CPImage.j>
|
||||
import <AppKit/CPMenu.j>
|
||||
import <AppKit/CPView.j>
|
||||
|
||||
|
||||
@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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPArray.j>
|
||||
import <Foundation/CPDictionary.j>
|
||||
import <Foundation/CPPropertyListSerialization.j>
|
||||
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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 <AppKit/CGGeometry.j>
|
||||
import <AppKit/CPImageView.j>
|
||||
import <AppKit/CPView.j>
|
||||
|
||||
#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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 <Foundation/CPArray.j>
|
||||
|
||||
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() }
|
||||
}
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
|
||||
@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
|
||||
@@ -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 <Foundation/CPBundle.j>
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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 <Foundation/Foundation.j>
|
||||
|
||||
import <AppKit/CPView.j>
|
||||
import <AppKit/CPSlider.j>
|
||||
import <AppKit/CPColorPicker.j>
|
||||
import <AppKit/CPColorPanel.j>
|
||||
|
||||
@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
|
||||
@@ -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 <Foundation/CPString.j>
|
||||
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
|
||||
import <AppKit/CPView.j>
|
||||
|
||||
|
||||
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
|
||||
@@ -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 <Foundation/Foundation.j>
|
||||
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
|
||||
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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
|
||||
import <AppKit/CPImage.j>
|
||||
import <AppKit/CPView.j>
|
||||
|
||||
|
||||
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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
|
||||
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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPURLConnection.j>
|
||||
import <Foundation/CPURLRequest.j>
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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 <Foundation/CPBundle.j>
|
||||
import <Foundation/CPDictionary.j>
|
||||
import <Foundation/CPString.j>
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -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 <Foundation/CPArray.j>
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
|
||||
|
||||
@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
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
|
||||
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
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 = "<object width = \"100%\" height = \"100%\"><param name = \"movie\" value = \"" +
|
||||
aFlashMovie._fileName +
|
||||
"\"></param><param name = \"wmode\" value = \"transparent\"></param><embed src = \"" +
|
||||
aFlashMovie._fileName + "\" type = \"application/x-shockwave-flash\" wmode = \"transparent\" width = \"100%\" height = \"100%\"></embed></object>";
|
||||
}
|
||||
|
||||
- (CPFlashMovie)flashMovie
|
||||
{
|
||||
return _flashMovie;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
|
||||
import <AppKit/CABackingStore.j>
|
||||
|
||||
import <AppKit/CGContext.j>
|
||||
import <AppKit/CGGeometry.j>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPDictionary.j>
|
||||
import <Foundation/CPString.j>
|
||||
|
||||
|
||||
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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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 + '}';
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 = ["<cg_vml_:group coordsize=\"1,1\" coordorigin=\"0,0\" style=\"width:1;height:1;position:absolute"];
|
||||
|
||||
/*if (similarity)
|
||||
{
|
||||
var angle = CGPointMake(1.0, 0.0);
|
||||
|
||||
angle = _CGPointApplyAffineTransform(angle, ctm);
|
||||
|
||||
vml.push(";rotation:", ATAN2(angle.y - ctm.ty, angle.x - ctm.tx) * 180 / PI);
|
||||
}*/
|
||||
|
||||
// Only create a filter if absolutely necessary. This actually
|
||||
// turns out to only be the case if our transform matrix is not
|
||||
// a similarity matrix, that is to say, the transform actually
|
||||
// morphs the shape beyond scaling and rotation.
|
||||
//if (!similarity && (ctm.a != 1.0 || ctm.b || ctm.c || ctm.d != 1.0))
|
||||
{
|
||||
var transformedRect = CGRectApplyAffineTransform(aRect, ctm);
|
||||
|
||||
vml.push( ";padding:0 ", ROUND(_CGRectGetMaxX(transformedRect)), "px ", ROUND(_CGRectGetMaxY(transformedRect)),
|
||||
"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
|
||||
"M11='", ctm.a, "',M12='", ctm.c, "',M21='", ctm.b, "',M22='", ctm.d, "',",
|
||||
"Dx='", ROUND(origin.x), "', Dy='", ROUND(origin.y), "', sizingmethod='clip');");
|
||||
}
|
||||
//else
|
||||
// vml.push(";top:", ROUND(origin.y - 0.5), "px;left:", ROUND(origin.x - 0.5), "px;");
|
||||
|
||||
vml.push( "\"><cg_vml_:image src=\"", anImage._image.src,
|
||||
"\" style=\"width:", _CGRectGetWidth(aRect), "px;height:", _CGRectGetHeight(aRect),
|
||||
"px;\"/></g_vml_:group>");
|
||||
|
||||
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 = [ "<cg_vml_:shape",
|
||||
" fillcolor=\"", gState.fillStyle,
|
||||
"\" filled=\"", VML_TRUTH_TABLE[fill],
|
||||
"\" style=\"position:absolute;width:", W, ";height:", H,
|
||||
";\" coordorigin=\"0 0\" coordsize=\"", Z * W, " ", Z * H,
|
||||
"\" stroked=\"", VML_TRUTH_TABLE[stroke],
|
||||
"\" strokeweight=\"", gState.lineWidth,
|
||||
"\" strokecolor=\"", gState.strokeStyle,
|
||||
"\" path=\""];
|
||||
|
||||
for (; i < count; ++i)
|
||||
{
|
||||
var element = elements[i],
|
||||
type = element.type;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case kCGPathElementMoveToPoint:
|
||||
case kCGPathElementAddLineToPoint: vml.push(VML_ELEMENT_TABLE[type], COORD(element.x), ',', COORD(element.y));
|
||||
break;
|
||||
|
||||
case kCGPathElementAddQuadCurveToPoint: vml.push(VML_ELEMENT_TABLE[type],
|
||||
COORD(element.cpx), ',', COORD(element.cpy), ',',
|
||||
COORD(element.x), ',', COORD(element.y));
|
||||
break;
|
||||
|
||||
case kCGPathElementAddCurveToPoint: vml.push(VML_ELEMENT_TABLE[type],
|
||||
COORD(element.cp1x), ',', COORD(element.cp1y), ',',
|
||||
COORD(element.cp2x), ',', COORD(element.cp2y), ',',
|
||||
COORD(element.x), ',', COORD(element.y));
|
||||
break;
|
||||
|
||||
case kCGPathElementCloseSubpath: vml.push(VML_ELEMENT_TABLE[type]);
|
||||
break;
|
||||
|
||||
case kCGPathElementAddArc: var x = element.x,
|
||||
y = element.y,
|
||||
radius = element.radius,
|
||||
clockwise = element.clockwise ? 1 : 0,
|
||||
endAngle = element.endAngle,
|
||||
startAngle = element.startAngle,
|
||||
|
||||
start = _CGPointMake(x + radius * COS(startAngle), y + radius * SIN(startAngle));
|
||||
|
||||
// If the angle's are equal, then we won't actually draw an arc, but instead
|
||||
// simply move to its start/end to get the proper fill.
|
||||
// We only need this special case for anti-clockwise because start == end is
|
||||
// interpreted as a full circle with anti-clockwise, but empty for clockwise.
|
||||
if (startAngle == endAngle && !clockwise)
|
||||
{
|
||||
vml.push(VML_ELEMENT_TABLE[kCGPathElementMoveToPoint], COORD(start.x), ',', COORD(start.y));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var end = _CGPointMake(x + radius * COS(endAngle), y + radius * SIN(endAngle));
|
||||
|
||||
// Only do the start correction if the angles aren't equal. If they are, then
|
||||
// let the circle be empty.
|
||||
// FIXME: Should this be |star.x - end.x| < 0.125 ?
|
||||
if (clockwise && startAngle != endAngle && _CGPointEqualToPoint(start, end))
|
||||
if (start.x >= 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("<cg_vml_:fill color=\"", gState.fillStyle, "\" opacity=\"", opacity, "\" />");
|
||||
|
||||
if (stroke)
|
||||
vml.push( "<cg_vml_:stroke opacity=\"", opacity,
|
||||
"\" joinstyle=\"", VML_LINEJOIN_TABLE[gState.lineJoin],
|
||||
"\" miterlimit=\"", gState.miterLimit,
|
||||
"\" endcap=\"", VML_LINECAP_TABLE[gState.lineCap],
|
||||
"\" weight=\"", gState.lineWidth, "",
|
||||
"px\" color=\"", gState.strokeStyle,"\" />");
|
||||
|
||||
var shadowColor = gState.shadowColor;
|
||||
//\"", [shadowColor cssString], "\"
|
||||
if (shadowColor)
|
||||
{
|
||||
var shadowOffset = gState.shadowOffset;
|
||||
|
||||
vml.push("<cg_vml_:shadow on=\"t\" offset=\"",
|
||||
shadowOffset.width, "pt ", shadowOffset.height, "pt\" opacity=\"", [shadowColor alphaComponent], "\" color=black />");
|
||||
}
|
||||
|
||||
vml.push("</cg_vml_:shape>");
|
||||
|
||||
aContext.path = NULL;
|
||||
|
||||
if (aContext.buffer != nil)
|
||||
aContext.buffer += vml.join("");
|
||||
else
|
||||
aContext.DOMElement.insertAdjacentHTML("BeforeEnd", vml.join(""));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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>CPBundleIdentifier</key>
|
||||
<string>com.280n.AppKit</string>
|
||||
<key>CPBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>AppKit</string>
|
||||
<key>CPBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -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()
|
||||
@@ -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 <Foundation/CPRunLoop.j>
|
||||
|
||||
#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];
|
||||
@@ -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 <Foundation/CPObject.j>
|
||||
import <Foundation/CPRunLoop.j>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
@@ -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 <Foundation/CPArray.j>
|
||||
import <Foundation/CPObject.j>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 437 B |
|
After Width: | Height: | Size: 471 B |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 393 B |
|
After Width: | Height: | Size: 141 B |
|
After Width: | Height: | Size: 409 B |
|
After Width: | Height: | Size: 378 B |
|
After Width: | Height: | Size: 133 B |
|
After Width: | Height: | Size: 385 B |
|
After Width: | Height: | Size: 230 B |
|
After Width: | Height: | Size: 143 B |
|
After Width: | Height: | Size: 232 B |