Merge branch 'master' of git://github.com/280north/cappuccino
@@ -2,5 +2,5 @@
|
||||
Frameworks
|
||||
Build
|
||||
Demos
|
||||
Aristo
|
||||
./Aristo
|
||||
WebSite
|
||||
|
||||
@@ -29,8 +29,12 @@
|
||||
@import "CPButtonBar.j"
|
||||
@import "CPCheckBox.j"
|
||||
@import "CPCib.j"
|
||||
@import "CPCibConnector.j"
|
||||
@import "CPCibControlConnector.j"
|
||||
@import "CPCibLoading.j"
|
||||
@import "CPCibOutletConnector.j"
|
||||
@import "CPClipView.j"
|
||||
@import "CPCollectionViewItem.j"
|
||||
@import "CPCollectionView.j"
|
||||
@import "CPColor.j"
|
||||
@import "CPColorPanel.j"
|
||||
@@ -57,6 +61,7 @@
|
||||
@import "CPProgressIndicator.j"
|
||||
@import "CPRadio.j"
|
||||
@import "CPResponder.j"
|
||||
@import "CPSearchField.j"
|
||||
@import "CPScrollView.j"
|
||||
@import "CPScroller.j"
|
||||
@import "CPSecureTextField.j"
|
||||
@@ -71,6 +76,7 @@
|
||||
@import "CPToolbar.j"
|
||||
@import "CPToolbarItem.j"
|
||||
@import "CPView.j"
|
||||
@import "CPViewController.j"
|
||||
@import "CPWebView.j"
|
||||
@import "CPWindow.j"
|
||||
@import "CPWindowController.j"
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* CPAccordionView.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2009, 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/CPKeyValueObserving.j>
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
#import "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
@implementation CPAccordionViewItem : CPObject
|
||||
{
|
||||
CPString _identifier @accessors(property=identifier);
|
||||
CPView _view @accessors(property=view);
|
||||
CPString _label @accessors(property=label);
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithIdentifier:@""];
|
||||
}
|
||||
|
||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
[self setIdentifier:anIdentifier];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPAccordionView : CPView
|
||||
{
|
||||
CPInteger _dirtyItemIndex;
|
||||
CPView _itemHeaderPrototype;
|
||||
|
||||
CPMutableArray _items;
|
||||
CPMutableArray _itemViews;
|
||||
CPIndexSet _expandedItemIndexes;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_items = [];
|
||||
_itemViews = [];
|
||||
_expandedItemIndexes = [CPIndexSet indexSet];
|
||||
|
||||
[self setItemHeaderPrototype:[[CPButton alloc] initWithFrame:_CGRectMake(0.0, 0.0, 100.0, 24.0)]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setItemHeaderPrototype:(CPView)aView
|
||||
{
|
||||
_itemHeaderPrototype = aView;
|
||||
}
|
||||
|
||||
- (CPView)itemHeaderPrototype
|
||||
{
|
||||
return _itemHeaderPrototype;
|
||||
}
|
||||
|
||||
- (CPArray)items
|
||||
{
|
||||
return _items;
|
||||
}
|
||||
|
||||
- (void)addItem:(CPAccordionItem)anItem
|
||||
{
|
||||
[self insertItem:anItem atIndex:_items.length];
|
||||
}
|
||||
|
||||
- (void)insertItem:(CPAccordionItem)anItem atIndex:(CPInteger)anIndex
|
||||
{
|
||||
// FIXME: SHIFT ITEMS RIGHT
|
||||
[_expandedItemIndexes addIndex:anIndex];
|
||||
|
||||
var itemView = [[_CPAccordionItemView alloc] initWithAccordionView:self];
|
||||
|
||||
[itemView setIndex:anIndex];
|
||||
[itemView setLabel:[anItem label]];
|
||||
[itemView setContentView:[anItem view]];
|
||||
|
||||
[self addSubview:itemView];
|
||||
|
||||
[_items insertObject:anItem atIndex:anIndex];
|
||||
[_itemViews insertObject:itemView atIndex:anIndex];
|
||||
|
||||
[self _invalidateItemsStartingAtIndex:anIndex];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)removeItem:(CPAccordionItem)anItem
|
||||
{
|
||||
[self removeItemAtIndex:[_items indexOfObjectIdenticalTo:anItem]];
|
||||
}
|
||||
|
||||
- (void)removeItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
// SHIFT ITEMS LEFT
|
||||
[_expandedItemIndexes removeIndex:anIndex];
|
||||
|
||||
[_itemViews[anIndex] removeFromSuperview];
|
||||
|
||||
[_items removeObjectAtIndex:anIndex];
|
||||
[_itemViews removeObjectAtIndex:anIndex];
|
||||
|
||||
[self _invalidateItemsStartingAtIndex:anIndex];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)removeAllItems
|
||||
{
|
||||
var count = _items.length;
|
||||
|
||||
while (count--)
|
||||
[self removeItemAtIndex:count];
|
||||
}
|
||||
|
||||
- (void)expandItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
if (![_itemViews[anIndex] isCollapsed])
|
||||
return;
|
||||
|
||||
[_expandedItemIndexes addIndex:anIndex];
|
||||
[_itemViews[anIndex] setCollapsed:NO];
|
||||
|
||||
[self _invalidateItemsStartingAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)collapseItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
if ([_itemViews[anIndex] isCollapsed])
|
||||
return;
|
||||
|
||||
[_expandedItemIndexes removeIndex:anIndex];
|
||||
[_itemViews[anIndex] setCollapsed:YES];
|
||||
|
||||
[self _invalidateItemsStartingAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)toggleItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
var itemView = _itemViews[anIndex];
|
||||
|
||||
if ([itemView isCollapsed])
|
||||
[self expandItemAtIndex:anIndex];
|
||||
|
||||
else
|
||||
[self collapseItemAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (CPIndexSet)expandedItemIndexes
|
||||
{
|
||||
return _expandedItemIndexes;
|
||||
}
|
||||
|
||||
- (CPIndexSet)collapsedItemIndexes
|
||||
{
|
||||
var indexSet = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, _items.length)];
|
||||
|
||||
[indexSet removeIndexes:_expandedIndexes];
|
||||
|
||||
return indexSet;
|
||||
}
|
||||
|
||||
- (void)_invalidateItemsStartingAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
if (_dirtyItemIndex === CPNotFound)
|
||||
_dirtyItemIndex = anIndex;
|
||||
|
||||
_dirtyItemIndex = MIN(_dirtyItemIndex, anIndex);
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
var width = _CGRectGetWidth([self frame]);
|
||||
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
if (width !== _CGRectGetWidth([self frame]))
|
||||
[self _invalidateItemsStartingAtIndex:0];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (_items.length <= 0)
|
||||
return [self setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), 0.0)];
|
||||
|
||||
if (_dirtyItemIndex === CPNotFound)
|
||||
return;
|
||||
|
||||
_dirtyItemIndex = MIN(_dirtyItemIndex, _items.length - 1);
|
||||
|
||||
var index = _dirtyItemIndex,
|
||||
count = _itemViews.length,
|
||||
width = _CGRectGetWidth([self bounds]),
|
||||
y = index > 0 ? CGRectGetMaxY([_itemViews[index - 1] frame]) : 0.0;
|
||||
|
||||
// Do this now (instead of after looping), so that if we are made dirty again in the middle we don't blow this value away.
|
||||
_dirtyItemIndex = CPNotFound;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var itemView = _itemViews[index];
|
||||
|
||||
[itemView setFrameY:y width:width];
|
||||
|
||||
y = CGRectGetMaxY([itemView frame]);
|
||||
}
|
||||
|
||||
[self setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), y)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPAccordionItemView : CPView
|
||||
{
|
||||
CPAccordionView _accordionView;
|
||||
|
||||
BOOL _isCollapsed @accessors(getter=isCollapsed, setter=setCollapsed:);
|
||||
CPInteger _index @accessors(property=index);
|
||||
CPView _headerView;
|
||||
CPView _contentView;
|
||||
}
|
||||
|
||||
- (id)initWithAccordionView:(CPAccordionView)anAccordionView
|
||||
{
|
||||
self = [super initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_accordionView = anAccordionView;
|
||||
_isCollapsed = NO;
|
||||
|
||||
var bounds = [self bounds];
|
||||
|
||||
_headerView = [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:[_accordionView itemHeaderPrototype]]];
|
||||
|
||||
if ([_headerView respondsToSelector:@selector(setTarget:)] && [_headerView respondsToSelector:@selector(setAction:)])
|
||||
{
|
||||
[_headerView setTarget:self];
|
||||
[_headerView setAction:@selector(toggle:)];
|
||||
}
|
||||
|
||||
[self addSubview:_headerView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)toggle:(id)aSender
|
||||
{
|
||||
[_accordionView toggleItemAtIndex:[self index]];
|
||||
}
|
||||
|
||||
- (void)setLabel:(CPString)aLabel
|
||||
{
|
||||
if ([_headerView respondsToSelector:@selector(setTitle:)])
|
||||
[_headerView setTitle:aLabel];
|
||||
|
||||
else if ([_headerView respondsToSelector:@selector(setLabel:)])
|
||||
[_headerView setLabel:aLabel];
|
||||
|
||||
else if ([_headerView respondsToSelector:@selector(setStringValue:)])
|
||||
[_headerView setStringValue:aLabel];
|
||||
}
|
||||
|
||||
- (void)setContentView:(CPView)aView
|
||||
{
|
||||
if (_contentView === aView)
|
||||
return;
|
||||
|
||||
[_contentView removeObserver:self forKeyPath:@"frame"];
|
||||
|
||||
[_contentView removeFromSuperview];
|
||||
|
||||
_contentView = aView;
|
||||
|
||||
[_contentView addObserver:self forKeyPath:@"frame" options:0 context:NULL];
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
[_accordionView _invalidateItemsStartingAtIndex:[self index]];
|
||||
}
|
||||
|
||||
- (void)setFrameY:(float)aY width:(float)aWidth
|
||||
{
|
||||
var headerHeight = _CGRectGetHeight([_headerView frame]);
|
||||
|
||||
// Size to fit or something?
|
||||
[_headerView setFrameSize:_CGSizeMake(aWidth, headerHeight)];
|
||||
[_contentView setFrameOrigin:_CGPointMake(0.0, headerHeight)];
|
||||
|
||||
if ([self isCollapsed])
|
||||
[self setFrame:_CGRectMake(0.0, aY, aWidth, headerHeight)];
|
||||
|
||||
else
|
||||
{
|
||||
var contentHeight = _CGRectGetHeight([_contentView frame]);
|
||||
|
||||
[_contentView setFrameSize:_CGSizeMake(aWidth, contentHeight)];
|
||||
[self setFrame:_CGRectMake(0.0, aY, aWidth, contentHeight + headerHeight)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
{
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath
|
||||
ofObject:(id)anObject
|
||||
change:(CPDictionary)aChange
|
||||
context:(id)aContext
|
||||
{
|
||||
if (aKeyPath === "frame" && !CGRectEqualToRect([aChange objectForKey:CPKeyValueChangeOldKey], [aChange objectForKey:CPKeyValueChangeNewKey]))
|
||||
[_accordionView _invalidateItemsStartingAtIndex:[self index]];
|
||||
/*
|
||||
else if (aKeyPath === "itemHeaderPrototype")
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -62,12 +62,12 @@ var CPAlertWarningImage,
|
||||
CPAlert is an alert panel that can be displayed modally to present the
|
||||
user with a message and one or more options.
|
||||
|
||||
It can be used to display an information message (<pre>CPInformationalAlertStyle</pre>),
|
||||
a warning message (<pre>CPWarningAlertStyle</pre> - which is the default), or a critical
|
||||
alert (<pre>CPCriticalAlertStyle</pre>). In each case the user can be presented with one
|
||||
or more options by adding buttons using the <pre>addButtonWithTitle:</pre> method.
|
||||
It can be used to display an information message \c CPInformationalAlertStyle,
|
||||
a warning message \c CPWarningAlertStyle (the default), or a critical
|
||||
alert \c CPCriticalAlertStyle. In each case the user can be presented with one
|
||||
or more options by adding buttons using the \c -addButtonWithTitle: method.
|
||||
|
||||
The panel is displayed modally by calling <pre>runModal</pre> and once the user has
|
||||
The panel is displayed modally by calling \c -runModal and once the user has
|
||||
dismissed the panel, a message will be sent to the panel's delegate (if set), informing
|
||||
it which button was clicked (see delegate methods).
|
||||
|
||||
@@ -112,7 +112,7 @@ var CPAlertWarningImage,
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes a <pre>CPAlert</pre> panel with the default alert style (<pre>CPWarningAlertStyle</pre>).
|
||||
Initializes a \c CPAlert panel with the default alert style \c CPWarningAlertStyle.
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
@@ -244,7 +244,7 @@ var CPAlertWarningImage,
|
||||
|
||||
/*!
|
||||
Adds a button with a given title to the receiver.
|
||||
Buttons will be added starting from the right hand side of the <pre>CPAlert</pre> panel.
|
||||
Buttons will be added starting from the right hand side of the \c CPAlert panel.
|
||||
The first button will have the index 0, the second button 1 and so on.
|
||||
|
||||
You really shouldn't need more than 3 buttons.
|
||||
@@ -269,7 +269,7 @@ var CPAlertWarningImage,
|
||||
}
|
||||
|
||||
/*!
|
||||
Displays the <pre>CPAlert</pre> panel as a modal dialog. The user will not be
|
||||
Displays the \c CPAlert panel as a modal dialog. The user will not be
|
||||
able to interact with any other controls until s/he has dismissed the alert
|
||||
by clicking on one of the buttons.
|
||||
*/
|
||||
|
||||
@@ -57,10 +57,10 @@ ACTUAL_FRAME_RATE = 0;
|
||||
@par Delegate Methods
|
||||
|
||||
@delegate -(BOOL)animationShouldStart:(CPAnimation)animation;
|
||||
Called at the beginning of <code>startAnimation</code>.
|
||||
Called at the beginning of \c -startAnimation.
|
||||
@param animation the animation that will start
|
||||
@return <code>YES</code> allows the animation to start.
|
||||
<code>NO</code> stops the animation.
|
||||
@return \c YES allows the animation to start.
|
||||
\c NO stops the animation.
|
||||
|
||||
@delegate -(void)animationDidEnd:(CPAnimation)animation;
|
||||
Called when an animation has completed.
|
||||
@@ -72,7 +72,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
|
||||
@delegate - (float)animation:(CPAnimation)animation valueForProgress:(float)progress;
|
||||
The value from this method will be returned when CPAnimation's
|
||||
<code>currentValue</code> method is called.
|
||||
\c currentValue method is called.
|
||||
@param animation the animation to obtain the curve value for
|
||||
@param progress the current animation progress
|
||||
@return the curve value
|
||||
@@ -154,7 +154,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
/*!
|
||||
Sets the animation's length.
|
||||
@param aDuration the new animation length
|
||||
@throws CPInvalidArgumentException if <code>aDuration</code> is negative
|
||||
@throws CPInvalidArgumentException if \c aDuration is negative
|
||||
*/
|
||||
- (void)setDuration:(CPTimeInterval)aDuration
|
||||
{
|
||||
@@ -175,7 +175,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
/*!
|
||||
Sets the animation frame rate. This is not a guaranteed frame rate. 0 means to go as fast as possible.
|
||||
@param frameRate the new desired frame rate
|
||||
@throws CPInvalidArgumentException if <code>frameRate</code> is negative
|
||||
@throws CPInvalidArgumentException if \c frameRate is negative
|
||||
*/
|
||||
- (void)setFrameRate:(float)frameRate
|
||||
{
|
||||
@@ -211,7 +211,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Starts the animation. The method calls <code>animationShouldStart:</code>
|
||||
Starts the animation. The method calls \c -animationShouldStart:
|
||||
on the delegate (if it implements it) to see if the animation
|
||||
should begin.
|
||||
*/
|
||||
@@ -270,7 +270,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the animation
|
||||
Returns \c YES if the animation
|
||||
is running.
|
||||
*/
|
||||
- (BOOL)isAnimating
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
@import "CPResponder.j"
|
||||
@import "CPDocumentController.j"
|
||||
@import "CPThemeBlend.j"
|
||||
@import "CPCibLoading.j"
|
||||
@import "CPPlatform.j"
|
||||
|
||||
|
||||
var CPMainCibFile = @"CPMainCibFile",
|
||||
CPMainCibFileHumanFriendly = @"Main cib file base name";
|
||||
@@ -48,7 +51,7 @@ CPRunContinuesResponse = -1002;
|
||||
CPApplication is THE way to start up the Cappucino framework for your application to use.
|
||||
Every GUI application has exactly one instance of CPApplication (or of a custom subclass of
|
||||
CPApplication). Your program's main() function can create that instance by calling the
|
||||
<code>CPApplicationMain</code> function. A simple example looks like this:
|
||||
\c CPApplicationMain function. A simple example looks like this:
|
||||
|
||||
<pre>
|
||||
function main(args, namedArgs)
|
||||
@@ -83,6 +86,7 @@ CPRunContinuesResponse = -1002;
|
||||
|
||||
//
|
||||
id _delegate;
|
||||
BOOL _finishedLaunching;
|
||||
|
||||
CPDictionary _namedArgs;
|
||||
CPArray _args;
|
||||
@@ -104,7 +108,7 @@ CPRunContinuesResponse = -1002;
|
||||
|
||||
/*!
|
||||
Initializes the Document based application with basic menu functions.
|
||||
Functions are <code>New, Open, Undo, Redo, Save, Cut, Copy, Paste</code>.
|
||||
Functions are \c New, \c Open, \c Undo, \c Redo, \c Save, \c Cut, \c Copy, \c Paste.
|
||||
@return the initialized application
|
||||
*/
|
||||
- (id)init
|
||||
@@ -234,10 +238,10 @@ CPRunContinuesResponse = -1002;
|
||||
}
|
||||
|
||||
/*!
|
||||
This method is called by <code>run</code> before the event loop begins.
|
||||
This method is called by \c -run before the event loop begins.
|
||||
When it successfully completes, it posts the notification
|
||||
CPApplicationDidFinishLaunchingNotification. If you override
|
||||
<code>finishLaunching</code>, the subclass method should invoke the superclass method.
|
||||
\c -finishLaunching, the subclass method should invoke the superclass method.
|
||||
*/
|
||||
- (void)finishLaunching
|
||||
{
|
||||
@@ -265,19 +269,37 @@ CPRunContinuesResponse = -1002;
|
||||
[defaultCenter
|
||||
postNotificationName:CPApplicationWillFinishLaunchingNotification
|
||||
object:self];
|
||||
|
||||
if (_documentController)
|
||||
|
||||
var filename = window.cpOpeningFilename && window.cpOpeningFilename(),
|
||||
needsUntitled = !!_documentController;
|
||||
|
||||
if ([filename length])
|
||||
{
|
||||
needsUntitled = ![self _openFile:filename];
|
||||
}
|
||||
|
||||
if (needsUntitled && [_delegate respondsToSelector: @selector(applicationShouldOpenUntitledFile:)])
|
||||
needsUntitled = [_delegate applicationShouldOpenUntitledFile:self];
|
||||
|
||||
if (needsUntitled)
|
||||
[_documentController newDocument:self];
|
||||
|
||||
[defaultCenter
|
||||
postNotificationName:CPApplicationDidFinishLaunchingNotification
|
||||
object:self];
|
||||
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
_finishedLaunching = YES;
|
||||
}
|
||||
|
||||
- (void)terminate:(id)aSender
|
||||
{
|
||||
[CPPlatform terminateApplication];
|
||||
}
|
||||
|
||||
/*!
|
||||
Calls <code>finishLaunching</code> method which results in starting
|
||||
Calls \c -finishLaunching method which results in starting
|
||||
the main event loop.
|
||||
*/
|
||||
- (void)run
|
||||
@@ -287,7 +309,7 @@ CPRunContinuesResponse = -1002;
|
||||
|
||||
// Managing the Event Loop
|
||||
/*!
|
||||
Starts a modal event loop for <code>aWindow</code>
|
||||
Starts a modal event loop for \c aWindow
|
||||
@param aWindow the window to start the event loop for
|
||||
*/
|
||||
- (void)runModalForWindow:(CPWindow)aWindow
|
||||
@@ -296,8 +318,8 @@ CPRunContinuesResponse = -1002;
|
||||
}
|
||||
|
||||
/*!
|
||||
Stops the event loop started by <code>runModalForWindow:</code> and
|
||||
sets the code that <code>runModalForWindow:</code> will return.
|
||||
Stops the event loop started by \c -runModalForWindow: and
|
||||
sets the code that \c -runModalForWindow: will return.
|
||||
@param aCode the return code for the modal event
|
||||
*/
|
||||
- (void)stopModalWithCode:(int)aCode
|
||||
@@ -338,7 +360,7 @@ CPRunContinuesResponse = -1002;
|
||||
}
|
||||
|
||||
/*!
|
||||
Aborts the event loop started by <code>runModalForWindow:</code>
|
||||
Aborts the event loop started by \c -runModalForWindow:
|
||||
*/
|
||||
- (void)abortModal
|
||||
{
|
||||
@@ -346,7 +368,7 @@ CPRunContinuesResponse = -1002;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets up a modal session with <code>theWindow</code>.
|
||||
Sets up a modal session with \c theWindow.
|
||||
@param aWindow the window to set up the modal session for
|
||||
*/
|
||||
- (CPModalSession)beginModalSessionForWindow:(CPWindow)aWindow
|
||||
@@ -375,7 +397,7 @@ CPRunContinuesResponse = -1002;
|
||||
|
||||
/*!
|
||||
Returns the window for the current modal session. If there is no
|
||||
modal session, it returns <code>nil</code>.
|
||||
modal session, it returns \c nil.
|
||||
*/
|
||||
- (CPWindow)modalWindow
|
||||
{
|
||||
@@ -390,7 +412,7 @@ CPRunContinuesResponse = -1002;
|
||||
{
|
||||
if ([_mainMenu performKeyEquivalent:anEvent])
|
||||
return YES;
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -400,6 +422,8 @@ CPRunContinuesResponse = -1002;
|
||||
*/
|
||||
- (void)sendEvent:(CPEvent)anEvent
|
||||
{
|
||||
_currentEvent = anEvent;
|
||||
|
||||
// Check if this is a candidate for key equivalent...
|
||||
if ([anEvent type] == CPKeyDown &&
|
||||
[anEvent modifierFlags] & (CPCommandKeyMask | CPControlKeyMask) &&
|
||||
@@ -443,7 +467,7 @@ CPRunContinuesResponse = -1002;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the CPWindow object corresponding to <code>aWindowNumber</code>.
|
||||
Returns the CPWindow object corresponding to \c aWindowNumber.
|
||||
*/
|
||||
- (CPWindow)windowWithWindowNumber:(int)aWindowNumber
|
||||
{
|
||||
@@ -473,7 +497,18 @@ CPRunContinuesResponse = -1002;
|
||||
*/
|
||||
- (void)setMainMenu:(CPMenu)aMenu
|
||||
{
|
||||
_mainMenu = aMenu;
|
||||
if ([aMenu _menuName] === "CPMainMenu")
|
||||
{
|
||||
if (_mainMenu === aMenu)
|
||||
return;
|
||||
|
||||
_mainMenu = aMenu;
|
||||
|
||||
if ([CPPlatform supportsNativeMainMenu])
|
||||
window.cpSetMainMenu(_mainMenu);
|
||||
}
|
||||
else
|
||||
[aMenu _setMenuName:@"CPMainMenu"];
|
||||
}
|
||||
|
||||
- (void)orderFrontColorPanel:(id)aSender
|
||||
@@ -499,7 +534,7 @@ CPRunContinuesResponse = -1002;
|
||||
@param anAction the action to perform.
|
||||
@param anObject the argument for the action
|
||||
method
|
||||
@return <code>YES</code> if the action was performed
|
||||
@return \c YES if the action was performed
|
||||
*/
|
||||
- (BOOL)tryToPerform:(SEL)anAction with:(id)anObject
|
||||
{
|
||||
@@ -524,7 +559,7 @@ CPRunContinuesResponse = -1002;
|
||||
@param anAction the action to send
|
||||
@param aTarget the target for the action
|
||||
@param aSender the action sender
|
||||
@return <code>YES</code>
|
||||
@return \c YES
|
||||
*/
|
||||
- (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender
|
||||
{
|
||||
@@ -540,12 +575,12 @@ CPRunContinuesResponse = -1002;
|
||||
|
||||
/*!
|
||||
Finds a target for the specified action. If the
|
||||
action is <code>nil</code>, returns <code>nil</code>.
|
||||
If the target is not <code>nil</code>, <code>aTarget</code> is
|
||||
returned. Otherwise, it calls <code>targetForAction:</code>
|
||||
action is \c nil, returns \c nil.
|
||||
If the target is not \c nil, \c aTarget is
|
||||
returned. Otherwise, it calls \c -targetForAction:
|
||||
to search for a target.
|
||||
@param anAction the action to find a target for
|
||||
@param aTarget if not <code>nil</code>, this will be returned
|
||||
@param aTarget if not \c nil, this will be returned
|
||||
@aSender not used
|
||||
@return a target for the action
|
||||
*/
|
||||
@@ -573,7 +608,7 @@ CPRunContinuesResponse = -1002;
|
||||
</ol>
|
||||
@param aWindow the window to search for a target
|
||||
@param anAction the action to find a responder to
|
||||
@return the object that responds to the action, or <code>nil</code>
|
||||
@return the object that responds to the action, or \c nil
|
||||
if no matching target was found
|
||||
@ignore
|
||||
*/
|
||||
@@ -625,7 +660,7 @@ CPRunContinuesResponse = -1002;
|
||||
<li>the document controller</li>
|
||||
</ol>
|
||||
@param anAction the action to handle
|
||||
@return a target that can respond, or <code>nil</code>
|
||||
@return a target that can respond, or \c nil
|
||||
if no match could be found
|
||||
*/
|
||||
- (id)targetForAction:(SEL)anAction
|
||||
@@ -667,6 +702,11 @@ CPRunContinuesResponse = -1002;
|
||||
_eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }));
|
||||
}
|
||||
|
||||
- (CPEvent)currentEvent
|
||||
{
|
||||
return _currentEvent;
|
||||
}
|
||||
|
||||
// Managing Sheets
|
||||
|
||||
/*!
|
||||
@@ -730,6 +770,14 @@ CPRunContinuesResponse = -1002;
|
||||
return _namedArgs;
|
||||
}
|
||||
|
||||
- (BOOL)_openFile:(CPString)aFilename
|
||||
{
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(application:openFile:)])
|
||||
return [_delegate application:self openFile:aFilename];
|
||||
else
|
||||
return [_documentController openDocumentWithContentsOfURL:aFilename display:YES error:NULL];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPModalSessionMake = function(aWindow, aStopCode)
|
||||
@@ -755,7 +803,7 @@ var _CPRunModalLoop = function(anEvent)
|
||||
|
||||
/*!
|
||||
Starts the GUI and Cappuccino frameworks. This function should be
|
||||
called from the <code>main()</code> function of your program.
|
||||
called from the \c main() function of your program.
|
||||
@class CPApplication
|
||||
@return void
|
||||
*/
|
||||
@@ -771,14 +819,18 @@ function CPApplicationMain(args, namedArgs)
|
||||
[principalClass sharedApplication];
|
||||
|
||||
//FIXME?
|
||||
if (!args && !namedArgs)
|
||||
if (!args)
|
||||
{
|
||||
var args = [CPApp arguments],
|
||||
searchParams = window.location.search.substring(1).split("&");
|
||||
namedArgs = [CPDictionary dictionary];
|
||||
var args = [CPApp arguments];
|
||||
|
||||
if([args containsObject:"debug"])
|
||||
CPLogRegister(CPLogPopup);
|
||||
}
|
||||
|
||||
if (!namedArgs)
|
||||
{
|
||||
var searchParams = window.location.search.substring(1).split("&");
|
||||
namedArgs = [CPDictionary dictionary];
|
||||
|
||||
for(var i=0; i<searchParams.length; i++)
|
||||
{
|
||||
@@ -804,7 +856,7 @@ var _CPAppBootstrapperActions = nil;
|
||||
|
||||
+ (void)actions
|
||||
{
|
||||
return [@selector(loadDefaultTheme), @selector(loadMainCibFile)];
|
||||
return [@selector(bootstrapPlatform), @selector(loadDefaultTheme), @selector(loadMainCibFile)];
|
||||
}
|
||||
|
||||
+ (void)performActions
|
||||
@@ -823,6 +875,11 @@ var _CPAppBootstrapperActions = nil;
|
||||
[CPApp run];
|
||||
}
|
||||
|
||||
+ (BOOL)bootstrapPlatform
|
||||
{
|
||||
return [CPPlatform bootstrap];
|
||||
}
|
||||
|
||||
+ (BOOL)loadDefaultTheme
|
||||
{
|
||||
var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[[CPBundle bundleForClass:[CPApplication class]] pathForResource:@"Aristo.blend"]];
|
||||
|
||||
@@ -50,52 +50,76 @@ var DefaultLineWidth = 1.0;
|
||||
float _lineWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a new CPBezierPath object.
|
||||
*/
|
||||
+ (CPBezierPath)bezierPath
|
||||
{
|
||||
return [[[self class] alloc] init];
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a new CPBezierPath object initialized with an oval path drawn within a rectangular path.
|
||||
*/
|
||||
+ (CPBezierPath)bezierPathWithOvalInRect:(CGRect)rect
|
||||
{
|
||||
var path = [[self class] bezierPath];
|
||||
var path = [self bezierPath];
|
||||
|
||||
[path appendBezierPathWithOvalInRect:rect];
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a new CPBezierPath object initialized with a rectangular path.
|
||||
*/
|
||||
+ (CPBezierPath)bezierPathWithRect:(CGRect)rect
|
||||
{
|
||||
var path = [[self class] bezierPath];
|
||||
var path = [self bezierPath];
|
||||
|
||||
[path appendBezierPathWithRect:rect];
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/*!
|
||||
Get default line width.
|
||||
*/
|
||||
+ (float)defaultLineWidth
|
||||
{
|
||||
return DefaultLineWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set default line width.
|
||||
*/
|
||||
+ (void)setDefaultLineWidth:(float)width
|
||||
{
|
||||
DefaultLineWidth = width;
|
||||
}
|
||||
|
||||
+ (void)fillRect:(CGRect)rect
|
||||
/*!
|
||||
Fill rectangular path with current fill color.
|
||||
*/
|
||||
+ (void)fillRect:(CGRect)aRect
|
||||
{
|
||||
[[[self class] bezierPathWithRect:rect] fill];
|
||||
[[self bezierPathWithRect:aRect] fill];
|
||||
}
|
||||
|
||||
+ (void)strokeRect:(CGRect)rect
|
||||
/*!
|
||||
Using the current stroke color and default drawing attributes, strokes a counterclockwise path beginning at the rectangle's origin.
|
||||
*/
|
||||
+ (void)strokeRect:(CGRect)aRect
|
||||
{
|
||||
[[[self class] bezierPathWithRect:rect] stroke];
|
||||
[[self bezierPathWithRect:aRect] stroke];
|
||||
}
|
||||
|
||||
/*!
|
||||
Using the current stroke color and default drawing attributes, strokes a line between two points.
|
||||
*/
|
||||
+ (void)strokeLineFromPoint:(CGPoint)point1 toPoint:(CGPoint)point2
|
||||
{
|
||||
var path = [[self class] bezierPath];
|
||||
var path = [self bezierPath];
|
||||
|
||||
[path moveToPoint:point1];
|
||||
[path lineToPoint:point2];
|
||||
@@ -103,6 +127,9 @@ var DefaultLineWidth = 1.0;
|
||||
[path stroke];
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a new CPBezierPath object using the default line width.
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
@@ -114,26 +141,41 @@ var DefaultLineWidth = 1.0;
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Moves the current point to another location.
|
||||
*/
|
||||
- (void)moveToPoint:(CGPoint)point
|
||||
{
|
||||
CGPathMoveToPoint(_path, nil, point.x, point.y);
|
||||
}
|
||||
|
||||
/*!
|
||||
Append a straight line to the path.
|
||||
*/
|
||||
- (void)lineToPoint:(CGPoint)point
|
||||
{
|
||||
CGPathAddLineToPoint(_path, nil, point.x, point.y);
|
||||
}
|
||||
|
||||
/*!
|
||||
Add a cubic Bezier curve to the path.
|
||||
*/
|
||||
- (void)curveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
|
||||
{
|
||||
CGPathAddCurveToPoint(_path, nil, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a line segment between the first and last points in the subpath, closing it.
|
||||
*/
|
||||
- (void)closePath
|
||||
{
|
||||
CGPathCloseSubpath(_path);
|
||||
}
|
||||
|
||||
/*!
|
||||
Draw a line along the path with the current stroke color and default drawing attributes.
|
||||
*/
|
||||
- (void)stroke
|
||||
{
|
||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
@@ -145,6 +187,9 @@ var DefaultLineWidth = 1.0;
|
||||
CGContextStrokePath(ctx);
|
||||
}
|
||||
|
||||
/*!
|
||||
Fill the path with the current fill color.
|
||||
*/
|
||||
- (void)fill
|
||||
{
|
||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
@@ -156,56 +201,88 @@ var DefaultLineWidth = 1.0;
|
||||
CGContextFillPath(ctx);
|
||||
}
|
||||
|
||||
/*!
|
||||
Get the line width.
|
||||
*/
|
||||
- (float)lineWidth
|
||||
{
|
||||
return _lineWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the line width.
|
||||
*/
|
||||
- (void)setLineWidth:(float)lineWidth
|
||||
{
|
||||
_lineWidth = lineWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Get the total number of elements.
|
||||
*/
|
||||
- (unsigned)elementCount
|
||||
{
|
||||
return _path.count;
|
||||
}
|
||||
|
||||
/*!
|
||||
Check if receiver is empty, returns appropriate Boolean value.
|
||||
*/
|
||||
- (BOOL)isEmpty
|
||||
{
|
||||
return CGPathIsEmpty(_path);
|
||||
}
|
||||
|
||||
/*!
|
||||
Get the current point.
|
||||
*/
|
||||
- (CGPoint)currentPoint
|
||||
{
|
||||
return CGPathGetCurrentPoint(_path);
|
||||
}
|
||||
|
||||
/*!
|
||||
Append a series of line segments.
|
||||
*/
|
||||
- (void)appendBezierPathWithPoints:(CPArray)points count:(unsigned)count
|
||||
{
|
||||
CGPathAddLines(_path, nil, points, count);
|
||||
}
|
||||
|
||||
/*!
|
||||
Append a rectangular path.
|
||||
*/
|
||||
- (void)appendBezierPathWithRect:(CGRect)rect
|
||||
{
|
||||
CGPathAddRect(_path, nil, rect);
|
||||
}
|
||||
|
||||
/*!
|
||||
Append an oval path; oval is drawn within the rectangular path.
|
||||
*/
|
||||
- (void)appendBezierPathWithOvalInRect:(CGRect)rect
|
||||
{
|
||||
CGPathAddPath(_path, nil, CGPathWithEllipseInRect(rect));
|
||||
}
|
||||
|
||||
/*!
|
||||
Append a rounded rectangular path.
|
||||
*/
|
||||
- (void)appendBezierPathWithRoundedRect:(CGRect)rect xRadius:(float)xRadius yRadius:(float)yRadius
|
||||
{
|
||||
CGPathAddPath(_path, nil, CGPathWithRoundedRectangleInRect(rect, xRadius, yRadius, YES, YES, YES, YES));
|
||||
}
|
||||
|
||||
/*!
|
||||
Append the contents of a CPBezierPath object.
|
||||
*/
|
||||
- (void)appendBezierPath:(NSBezierPath *)other
|
||||
{
|
||||
CGPathAddPath(_path, nil, other._path);
|
||||
}
|
||||
|
||||
/*!
|
||||
Remove all path elements; clears path.
|
||||
*/
|
||||
- (void)removeAllPoints
|
||||
{
|
||||
_path = CGPathCreateMutable();
|
||||
|
||||
@@ -172,7 +172,7 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
|
||||
// Setting the state
|
||||
/*!
|
||||
Returns <code>YES</code> if the button has a 'mixed' state in addition to on and off.
|
||||
Returns \c YES if the button has a 'mixed' state in addition to on and off.
|
||||
*/
|
||||
- (BOOL)allowsMixedState
|
||||
{
|
||||
@@ -248,9 +248,9 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the button's state to <code>aState</code>.
|
||||
Sets the button's state to \c aState.
|
||||
@param aState Possible states are any of the CPButton globals:
|
||||
<code>CPOffState, CPOnState, CPMixedState</code>
|
||||
\c CPOffState, \c CPOnState, \c CPMixedState
|
||||
*/
|
||||
- (void)setState:(CPInteger)aState
|
||||
{
|
||||
@@ -602,7 +602,7 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
||||
@implementation CPButton (CPCoding)
|
||||
|
||||
/*!
|
||||
Initializes the button by unarchiving data from <code>aCoder</code>.
|
||||
Initializes the button by unarchiving data from \c aCoder.
|
||||
@param aCoder the coder containing the archived CPButton.
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the document view to be <code>aView</code>.
|
||||
@param aView the new document view. It's frame origin will be changed to <code>(0,0)</code> after calling this method.
|
||||
Sets the document view to be \c aView.
|
||||
@param aView the new document view. It's frame origin will be changed to \c (0,0) after calling this method.
|
||||
*/
|
||||
- (void)setDocumentView:(CPView)aView
|
||||
{
|
||||
@@ -95,7 +95,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new point that may be adjusted from <code>aPoint</code>
|
||||
Returns a new point that may be adjusted from \c aPoint
|
||||
to make sure it lies within the document view.
|
||||
@param aPoint
|
||||
@return the adjusted point
|
||||
@@ -128,7 +128,7 @@
|
||||
|
||||
/*!
|
||||
Scrolls the clip view to the specified point. The method
|
||||
sets its bounds origin to <code>aPoint</code>.
|
||||
sets its bounds origin to \c aPoint.
|
||||
*/
|
||||
- (void)scrollToPoint:(CGPoint)aPoint
|
||||
{
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
@import <Foundation/CPKeyedArchiver.j>
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
@import "CPView.j"
|
||||
@import "CPCollectionViewItem.j"
|
||||
|
||||
|
||||
/*!
|
||||
@@ -61,6 +62,7 @@
|
||||
@param indices the indices to obtain drag types
|
||||
@return an array of drag types (CPString)
|
||||
*/
|
||||
|
||||
@implementation CPCollectionView : CPView
|
||||
{
|
||||
CPArray _content;
|
||||
@@ -76,7 +78,9 @@
|
||||
|
||||
CGSize _minItemSize;
|
||||
CGSize _maxItemSize;
|
||||
|
||||
|
||||
CPArray _backgroundColors;
|
||||
|
||||
float _tileWidth;
|
||||
|
||||
BOOL _isSelectable;
|
||||
@@ -93,6 +97,8 @@
|
||||
unsigned _numberOfColumns;
|
||||
|
||||
id _delegate;
|
||||
|
||||
CPEvent _mouseDownEvent;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -109,7 +115,9 @@
|
||||
_itemSize = CGSizeMakeZero();
|
||||
_minItemSize = CGSizeMakeZero();
|
||||
_maxItemSize = CGSizeMakeZero();
|
||||
|
||||
|
||||
[self setBackgroundColors:nil];
|
||||
|
||||
_verticalMargin = 5.0;
|
||||
_tileWidth = -1.0;
|
||||
|
||||
@@ -122,15 +130,16 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the item prototype to <code>anItem</code>
|
||||
Sets the item prototype to \c anItem
|
||||
@param anItem the new item prototype
|
||||
*/
|
||||
- (void)setItemPrototype:(CPCollectionViewItem)anItem
|
||||
{
|
||||
_itemData = [CPKeyedArchiver archivedDataWithRootObject:anItem];
|
||||
_itemForDragging = anItem//[CPKeyedUnarchiver unarchiveObjectWithData:_itemData];
|
||||
_cachedItems = [];
|
||||
_itemData = nil;
|
||||
_itemForDragging = nil;
|
||||
_itemPrototype = anItem;
|
||||
|
||||
|
||||
[self reloadContent];
|
||||
}
|
||||
|
||||
@@ -143,17 +152,24 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a collection view item for <code>anObject</code>.
|
||||
Returns a collection view item for \c anObject.
|
||||
@param anObject the object to be represented.
|
||||
*/
|
||||
- (CPCollectionViewItem)newItemForRepresentedObject:(id)anObject
|
||||
{
|
||||
var item = nil;
|
||||
|
||||
|
||||
if (_cachedItems.length)
|
||||
item = _cachedItems.pop();
|
||||
|
||||
else
|
||||
{
|
||||
if (!_itemData)
|
||||
if (_itemPrototype)
|
||||
_itemData = [CPKeyedArchiver archivedDataWithRootObject:_itemPrototype];
|
||||
|
||||
item = [CPKeyedUnarchiver unarchiveObjectWithData:_itemData];
|
||||
}
|
||||
|
||||
[item setRepresentedObject:anObject];
|
||||
[[item view] setFrameSize:_itemSize];
|
||||
@@ -163,7 +179,7 @@
|
||||
|
||||
// Working with the Responder Chain
|
||||
/*!
|
||||
Returns <code>YES</code> by default.
|
||||
Returns \c YES by default.
|
||||
*/
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
@@ -175,13 +191,13 @@
|
||||
*/
|
||||
- (BOOL)isFirstResponder
|
||||
{
|
||||
return [[self window] firstResponder] == self;
|
||||
return [[self window] firstResponder] === self;
|
||||
}
|
||||
|
||||
// Setting the Content
|
||||
/*!
|
||||
Sets the content of the collection view to the content in <code>anArray</code>.
|
||||
This array can be of any type, and each element will be passed to the <code>setRepresentedObject:</code> method.
|
||||
Sets the content of the collection view to the content in \c anArray.
|
||||
This array can be of any type, and each element will be passed to the \c -setRepresentedObject: method.
|
||||
It's the responsibility of your custom collection view item to interpret the object.
|
||||
@param anArray the content array
|
||||
*/
|
||||
@@ -214,7 +230,7 @@
|
||||
// Setting the Selection Mode
|
||||
/*!
|
||||
Sets whether the user is allowed to select items
|
||||
@param isSelectable <code>YES</code> allows the user to select items.
|
||||
@param isSelectable \c YES allows the user to select items.
|
||||
*/
|
||||
- (void)setSelectable:(BOOL)isSelectable
|
||||
{
|
||||
@@ -233,8 +249,8 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the collection view is
|
||||
selected, and <code>NO</code> otherwise.
|
||||
Returns \c YES if the collection view is
|
||||
selected, and \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)isSelected
|
||||
{
|
||||
@@ -243,7 +259,7 @@
|
||||
|
||||
/*!
|
||||
Sets whether the user may have no items selected. If YES, mouse clicks not on any item will empty the current selection. The first item will also start off as selected.
|
||||
@param shouldAllowMultipleSelection <code>YES</code> allows the user to select multiple items
|
||||
@param shouldAllowMultipleSelection \c YES allows the user to select multiple items
|
||||
*/
|
||||
- (void)setAllowsEmptySelection:(BOOL)shouldAllowEmptySelection
|
||||
{
|
||||
@@ -251,7 +267,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the user can select no items, <code>NO</code> otherwise.
|
||||
Returns \c YES if the user can select no items, \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)allowsEmptySelection
|
||||
{
|
||||
@@ -260,7 +276,7 @@
|
||||
|
||||
/*!
|
||||
Sets whether the user can select multiple items.
|
||||
@param shouldAllowMultipleSelection <code>YES</code> allows the user to select multiple items
|
||||
@param shouldAllowMultipleSelection \c YES allows the user to select multiple items
|
||||
*/
|
||||
- (void)setAllowsMultipleSelection:(BOOL)shouldAllowMultipleSelection
|
||||
{
|
||||
@@ -268,7 +284,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the user can select multiple items, <code>NO</code> otherwise.
|
||||
Returns \c YES if the user can select multiple items, \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)allowsMultipleSelection
|
||||
{
|
||||
@@ -322,13 +338,13 @@
|
||||
|
||||
_items = [];
|
||||
|
||||
if (!_itemData || !_content)
|
||||
if (!_itemPrototype || !_content)
|
||||
return;
|
||||
|
||||
|
||||
var index = 0;
|
||||
|
||||
|
||||
count = _content.length;
|
||||
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
_items.push([self newItemForRepresentedObject:_content[index]]);
|
||||
@@ -520,6 +536,30 @@
|
||||
return _maxItemSize;
|
||||
}
|
||||
|
||||
- (void)setBackgroundColors:(CPArray)backgroundColors
|
||||
{
|
||||
if (_backgroundColors === backgroundColors)
|
||||
return;
|
||||
|
||||
_backgroundColors = backgroundColors;
|
||||
|
||||
if (!_backgroundColors)
|
||||
_backgroundColors = [CPColor whiteColor];
|
||||
|
||||
if ([_backgroundColors count] === 1)
|
||||
[self setBackgroundColor:_backgroundColors[0]];
|
||||
|
||||
else
|
||||
[self setBackgroundColor:nil];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPArray)backgroundColors
|
||||
{
|
||||
return _backgroundColors;
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
if ([_selectionIndexes count] && [anEvent clickCount] == 2 && [_delegate respondsToSelector:@selector(collectionView:didDoubleClickOnItemAtIndex:)])
|
||||
@@ -528,13 +568,16 @@
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
_mouseDownEvent = 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]];
|
||||
|
||||
else if (_allowsEmptySelection)
|
||||
[self setSelectionIndexes:[CPIndexSet indexSet]];
|
||||
}
|
||||
@@ -543,30 +586,32 @@
|
||||
{
|
||||
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]]];
|
||||
if (!_itemForDragging)
|
||||
_itemForDragging = [self newItemForRepresentedObject:_content[[_selectionIndexes firstIndex]]];
|
||||
else
|
||||
[_itemForDragging setRepresentedObject:_content[[_selectionIndexes firstIndex]]];
|
||||
|
||||
var view = [_itemForDragging view];
|
||||
|
||||
var view = [_itemForDragging view],
|
||||
frame = [view frame];
|
||||
|
||||
[view setFrameSize:_itemSize];
|
||||
[view setAlphaValue:0.7];
|
||||
|
||||
|
||||
[self dragView:view
|
||||
at:[[_items[[_selectionIndexes firstIndex]] view] frame].origin
|
||||
offset:CGPointMakeZero()
|
||||
event:anEvent
|
||||
offset:CGSizeMakeZero()
|
||||
event:_mouseDownEvent
|
||||
pasteboard:nil
|
||||
source:self
|
||||
slideBack:YES];
|
||||
@@ -627,98 +672,10 @@
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
Represents an object inside a CPCollectionView.
|
||||
*/
|
||||
@implementation CPCollectionViewItem : CPObject
|
||||
{
|
||||
id _representedObject;
|
||||
|
||||
CPView _view;
|
||||
|
||||
BOOL _isSelected;
|
||||
}
|
||||
|
||||
// Setting the Represented Object
|
||||
/*!
|
||||
Sets the object to be represented by this item.
|
||||
@param anObject the object to be represented
|
||||
*/
|
||||
- (void)setRepresentedObject:(id)anObject
|
||||
{
|
||||
if (_representedObject == anObject)
|
||||
return;
|
||||
|
||||
_representedObject = anObject;
|
||||
|
||||
// FIXME: This should be set up by bindings
|
||||
[_view setRepresentedObject:anObject];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the object represented by this view item
|
||||
*/
|
||||
- (id)representedObject
|
||||
{
|
||||
return _representedObject;
|
||||
}
|
||||
|
||||
// Modifying the View
|
||||
/*!
|
||||
Sets the view that is used represent this object.
|
||||
@param aView the view used to represent this object
|
||||
*/
|
||||
- (void)setView:(CPView)aView
|
||||
{
|
||||
_view = aView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the view that represents this object.
|
||||
*/
|
||||
- (CPView)view
|
||||
{
|
||||
return _view;
|
||||
}
|
||||
|
||||
// Modifying the Selection
|
||||
/*!
|
||||
Sets whether this view item should be selected.
|
||||
@param shouldBeSelected <code>YES</code> makes the item selected. <code>NO</code> deselects it.
|
||||
*/
|
||||
- (void)setSelected:(BOOL)shouldBeSelected
|
||||
{
|
||||
if (_isSelected == shouldBeSelected)
|
||||
return;
|
||||
|
||||
_isSelected = shouldBeSelected;
|
||||
|
||||
// FIXME: This should be set up by bindings
|
||||
[_view setSelected:_isSelected];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the item is currently selected. <code>NO</code> if the item is not selected.
|
||||
*/
|
||||
- (BOOL)isSelected
|
||||
{
|
||||
return _isSelected;
|
||||
}
|
||||
|
||||
// Parent Collection View
|
||||
/*!
|
||||
Returns the collection view of which this item is a part.
|
||||
*/
|
||||
- (CPCollectionView)collectionView
|
||||
{
|
||||
return [_view superview];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
||||
CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey",
|
||||
CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey";
|
||||
CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey",
|
||||
CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey";
|
||||
|
||||
|
||||
@implementation CPCollectionView (CPCoding)
|
||||
@@ -735,13 +692,19 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
||||
_cachedItems = [];
|
||||
|
||||
_itemSize = CGSizeMakeZero();
|
||||
_minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey];
|
||||
_maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey];
|
||||
|
||||
_minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey] || CGSizeMakeZero();
|
||||
_maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey] || CGSizeMakeZero();
|
||||
_verticalMargin = [aCoder decodeFloatForKey:CPCollectionViewVerticalMarginKey];
|
||||
|
||||
_verticalMargin = [aCoder decodeSizeForKey:CPCollectionViewVerticalMarginKey];
|
||||
[self setBackgroundColors:[aCoder decodeObjectForKey:CPCollectionViewBackgroundColorsKey]];
|
||||
|
||||
_tileWidth = -1.0;
|
||||
|
||||
_selectionIndexes = [CPIndexSet indexSet];
|
||||
|
||||
_allowsEmptySelection = YES;
|
||||
_isSelectable = YES;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -751,54 +714,15 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
|
||||
[aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
|
||||
if (!CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
|
||||
[aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
|
||||
|
||||
[aCoder encodeSize:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
|
||||
if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
|
||||
[aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
|
||||
|
||||
[aCoder encodeFloat:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
|
||||
|
||||
[aCoder encodeObject:_backgroundColors forKey:CPCollectionViewBackgroundColorsKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPCollectionViewItemViewKey = @"CPCollectionViewItemViewKey";
|
||||
|
||||
@implementation CPCollectionViewItem (CPCoding)
|
||||
|
||||
/*
|
||||
FIXME Not yet implemented
|
||||
*/
|
||||
- (id)copy
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPCollectionViewItemViewKey = @"CPCollectionViewItemViewKey";
|
||||
|
||||
@implementation CPCollectionViewItem (CPCoding)
|
||||
|
||||
/*!
|
||||
Initializes the view item by unarchiving data from a coder.
|
||||
@param aCoder the coder from which the data will be unarchived
|
||||
@return the initialized collection view item
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_view = [aCoder decodeObjectForKey:CPCollectionViewItemViewKey];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Archives the colletion view item to the provided coder.
|
||||
@param aCoder the coder to which the view item should be archived
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_view forKey:CPCollectionViewItemViewKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* CPCollectionViewItem.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2009, 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 "CPViewController.j"
|
||||
|
||||
/*!
|
||||
Represents an object inside a CPCollectionView.
|
||||
*/
|
||||
@implementation CPCollectionViewItem : CPViewController
|
||||
{
|
||||
BOOL _isSelected;
|
||||
}
|
||||
|
||||
// Setting the Represented Object
|
||||
/*!
|
||||
Sets the object to be represented by this item.
|
||||
@param anObject the object to be represented
|
||||
*/
|
||||
- (void)setRepresentedObject:(id)anObject
|
||||
{
|
||||
[super setRepresentedObject:anObject];
|
||||
|
||||
var view = [self view];
|
||||
|
||||
if ([view respondsToSelector:@selector(setRepresentedObject:)])
|
||||
[view setRepresentedObject:[self representedObject]];
|
||||
}
|
||||
|
||||
// Modifying the Selection
|
||||
/*!
|
||||
Sets whether this view item should be selected.
|
||||
@param shouldBeSelected \c YES makes the item selected. \c NO deselects it.
|
||||
*/
|
||||
- (void)setSelected:(BOOL)shouldBeSelected
|
||||
{
|
||||
shouldBeSelected = !!shouldBeSelected;
|
||||
|
||||
if (_isSelected === shouldBeSelected)
|
||||
return;
|
||||
|
||||
_isSelected = shouldBeSelected;
|
||||
|
||||
var view = [self view];
|
||||
|
||||
if ([view respondsToSelector:@selector(setSelected:)])
|
||||
[view setSelected:[self isSelected]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c YES if the item is currently selected. \c NO if the item is not selected.
|
||||
*/
|
||||
- (BOOL)isSelected
|
||||
{
|
||||
return _isSelected;
|
||||
}
|
||||
|
||||
// Parent Collection View
|
||||
/*!
|
||||
Returns the collection view of which this item is a part.
|
||||
*/
|
||||
- (CPCollectionView)collectionView
|
||||
{
|
||||
return [_view superview];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -46,6 +46,11 @@ var cachedBlackColor,
|
||||
cachedLightGrayColor,
|
||||
cachedDarkGrayColor,
|
||||
cachedWhiteColor,
|
||||
cachedBrownColor,
|
||||
cachedCyanColor,
|
||||
cachedMagentaColor,
|
||||
cachedOrangeColor,
|
||||
cachedPurpleColor,
|
||||
cachedShadowColor,
|
||||
cachedClearColor;
|
||||
|
||||
@@ -53,13 +58,13 @@ var cachedBlackColor,
|
||||
@ingroup appkit
|
||||
@code CPColor
|
||||
|
||||
<code>CPColor</code> can be used to represent color
|
||||
\c CPColor can be used to represent color
|
||||
in an RGB or HSB model with an optional transparency value.</p>
|
||||
|
||||
<p>It also provides some class helper methods that
|
||||
returns instances of commonly used colors.</p>
|
||||
|
||||
<p>The class does not have a <code>set:</code> method
|
||||
<p>The class does not have a \c -set: method
|
||||
like NextStep based frameworks to change the color of
|
||||
the current context. To change the color of the current
|
||||
context, use CGContextSetFillColor().
|
||||
@@ -112,7 +117,7 @@ var cachedBlackColor,
|
||||
|
||||
|
||||
/*!
|
||||
Creates a new color object with <code>white</code> for the RGB components.
|
||||
Creates a new color object with \c white for the RGB components.
|
||||
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
|
||||
|
||||
@param white a float between 0.0 and 1.0
|
||||
@@ -128,7 +133,7 @@ var cachedBlackColor,
|
||||
/*!
|
||||
@deprecated in favor of colorWithWhite:apha:
|
||||
|
||||
Creates a new color object with <code>white</code> for the RGB components.
|
||||
Creates a new color object with \c white for the RGB components.
|
||||
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
|
||||
|
||||
@param white a float between 0.0 and 1.0
|
||||
@@ -291,6 +296,61 @@ var cachedBlackColor,
|
||||
return cachedYellowColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a brown color object (RGBA=[0.6, 0.4, 0.2, 1.0])
|
||||
*/
|
||||
+ (CPColor)brownColor
|
||||
{
|
||||
if (!cachedBrownColor)
|
||||
cachedBrownColor = [[CPColor alloc] _initWithRGBA:[0.6, 0.4, 0.2, 1.0]];
|
||||
|
||||
return cachedBrownColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a cyan color object (RGBA=[0.0, 1.0, 1.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)cyanColor
|
||||
{
|
||||
if (!cachedCyanColor)
|
||||
cachedCyanColor = [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 1.0, 1.0]];
|
||||
|
||||
return cachedCyanColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a magenta color object (RGBA=[1.0, 0.0, 1.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)magentaColor
|
||||
{
|
||||
if (!cachedMagentaColor)
|
||||
cachedMagentaColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 1.0, 1.0]];
|
||||
|
||||
return cachedMagentaColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a orange color object (RGBA=[1.0, 0.5, 0.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)orangeColor
|
||||
{
|
||||
if (!cachedOrangeColor)
|
||||
cachedOrangeColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.5, 0.0, 1.0]];
|
||||
|
||||
return cachedOrangeColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a purple color object (RGBA=[0.5, 0.0, 0.5, 1.0])
|
||||
*/
|
||||
+ (CPColor)purpleColor
|
||||
{
|
||||
if (!cachedPurpleColor)
|
||||
cachedPurpleColor = [[CPColor alloc] _initWithRGBA:[0.5, 0.0, 0.5, 1.0]];
|
||||
|
||||
return cachedPurpleColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a shadow looking color (RGBA=[0.0, 0.0, 0.0, 0.33])
|
||||
*/
|
||||
@@ -315,8 +375,18 @@ var cachedBlackColor,
|
||||
return cachedClearColor;
|
||||
}
|
||||
|
||||
+ (CPColor)alternateSelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]];
|
||||
}
|
||||
|
||||
+ (CPColor)secondarySelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a color using a tile pattern with <code>anImage</code>
|
||||
Creates a color using a tile pattern with \c anImage
|
||||
@param the image to tile
|
||||
@return a tiled image color object
|
||||
*/
|
||||
@@ -666,7 +736,7 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
var hexCharacters = "0123456789ABCDEF";
|
||||
|
||||
/*!
|
||||
Used for the CPColor <code>colorWithHexString:</code> implementation
|
||||
Used for the CPColor \c +colorWithHexString: implementation
|
||||
@ignore
|
||||
@class CPColor
|
||||
@return an array of rgb components
|
||||
|
||||
@@ -58,7 +58,7 @@ CPColorPickerViewHeight = 370;
|
||||
|
||||
CPColorPanel provides a reusable panel that can be used
|
||||
displayed on screen to prompt the user for a color selection. To
|
||||
obtain the panel, call the <code>sharedColorPanel</code> method.
|
||||
obtain the panel, call the \c +sharedColorPanel method.
|
||||
*/
|
||||
@implementation CPColorPanel : CPPanel
|
||||
{
|
||||
@@ -111,7 +111,7 @@ CPColorPickerViewHeight = 370;
|
||||
}
|
||||
|
||||
/*
|
||||
To obtain the color panel, use <code>sharedColorPanel</code>.
|
||||
To obtain the color panel, use \c +sharedColorPanel.
|
||||
@ignore
|
||||
*/
|
||||
- (id)init
|
||||
@@ -137,7 +137,7 @@ CPColorPickerViewHeight = 370;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the color of the panel, and updates the picker. Also posts a <code>CPColorPanelDidChangeNotification</code>.
|
||||
Sets the color of the panel, and updates the picker. Also posts a \c CPColorPanelDidChangeNotification.
|
||||
*/
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
@@ -491,7 +491,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
var future = new Date();
|
||||
future.setYear(2019);
|
||||
|
||||
[_swatchCookie setValue: CPJSObjectCreateJSON(result) expires:future domain: nil];
|
||||
[_swatchCookie setValue: JSON.stringify(result) expires:future domain: nil];
|
||||
}
|
||||
|
||||
- (void)setColorPanel:(CPColorPanel)panel
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
@ingroup appkit
|
||||
@class CPColorPicker
|
||||
|
||||
CPColorPicker is an abstract superclass for all color picker subclasses. If you want a particular color picker, use CPColorPanel's <code>setPickerMode:</code> method. The simplest way to implement your own color picker is to create a subclass of CPColorPicker.
|
||||
CPColorPicker is an abstract superclass for all color picker subclasses. If you want a particular color picker, use CPColorPanel's \c +setPickerMode: method. The simplest way to implement your own color picker is to create a subclass of CPColorPicker.
|
||||
*/
|
||||
@implementation CPColorPicker : CPObject
|
||||
{
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
/*
|
||||
FIXME Not implemented.
|
||||
@return <code>nil</code>
|
||||
@return \c nil
|
||||
@ignore
|
||||
*/
|
||||
- (CPImage)provideNewButtonImage
|
||||
|
||||
@@ -35,7 +35,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
|
||||
CPColorWell is a CPControl for selecting and displaying a single color value. An example of a CPColorWell object (or simply color well) is found in CPColorPanel, which uses a color well to display the current color selection.</p>
|
||||
|
||||
<p>An application can have one or more active CPColorWells. You can activate multiple CPColorWells by invoking the <code>activate:</code> method with <code>NO</code> as its argument. When a mouse-down event occurs on an CPColorWell's border, it becomes the only active color well. When a color well becomes active, it brings up the color panel also.
|
||||
<p>An application can have one or more active CPColorWells. You can activate multiple CPColorWells by invoking the \c -activate: method with \c NO as its argument. When a mouse-down event occurs on an CPColorWell's border, it becomes the only active color well. When a color well becomes active, it brings up the color panel also.
|
||||
*/
|
||||
@implementation CPColorWell : CPControl
|
||||
{
|
||||
@@ -127,7 +127,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
}
|
||||
|
||||
/*!
|
||||
Changes the color of the well to that of <code>aSender</code>.
|
||||
Changes the color of the well to that of \c aSender.
|
||||
@param aSender the object from which to retrieve the color
|
||||
*/
|
||||
- (void)takeColorFrom:(id)aSender
|
||||
@@ -138,7 +138,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
// Activating and Deactivating Color Wells
|
||||
/*!
|
||||
Activates the color well, displays the color panel, and makes the panel's current color the same as its own.
|
||||
If exclusive is <code>YES</code>, deactivates any other CPColorWells. <code>NO</code>, keeps them active.
|
||||
If exclusive is \c YES, deactivates any other CPColorWells. \c NO, keeps them active.
|
||||
@param shouldBeExclusive whether other color wells should be deactivated.
|
||||
*/
|
||||
- (void)activate:(BOOL)shouldBeExclusive
|
||||
@@ -179,7 +179,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the color well is active.
|
||||
Returns \c YES if the color well is active.
|
||||
*/
|
||||
- (BOOL)isActive
|
||||
{
|
||||
@@ -262,7 +262,7 @@ var CPColorWellColorKey = "CPColorWellColorKey",
|
||||
@implementation CPColorWell (CPCoding)
|
||||
|
||||
/*!
|
||||
Initializes the color well by unarchiving data from <code>aCoder</code>.
|
||||
Initializes the color well by unarchiving data from \c aCoder.
|
||||
@param aCoder the coder containing the archived CPColorWell.
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
|
||||
@@ -35,17 +35,18 @@ CPCSSRGBAFeature = 1 << 5;
|
||||
|
||||
CPHTMLCanvasFeature = 1 << 6;
|
||||
CPHTMLContentEditableFeature = 1 << 7;
|
||||
CPHTMLDragAndDropFeature = 1 << 8;
|
||||
|
||||
CPJavascriptInnerTextFeature = 1 << 8;
|
||||
CPJavascriptTextContentFeature = 1 << 9;
|
||||
CPJavascriptClipboardEventsFeature = 1 << 10;
|
||||
CPJavascriptClipboardAccessFeature = 1 << 11;
|
||||
CPJavaScriptCanvasDrawFeature = 1 << 12;
|
||||
CPJavaScriptCanvasTransformFeature = 1 << 13;
|
||||
CPJavascriptInnerTextFeature = 1 << 9;
|
||||
CPJavascriptTextContentFeature = 1 << 10;
|
||||
CPJavascriptClipboardEventsFeature = 1 << 11;
|
||||
CPJavascriptClipboardAccessFeature = 1 << 12;
|
||||
CPJavaScriptCanvasDrawFeature = 1 << 13;
|
||||
CPJavaScriptCanvasTransformFeature = 1 << 14;
|
||||
|
||||
CPVMLFeature = 1 << 14;
|
||||
CPVMLFeature = 1 << 15;
|
||||
|
||||
CPJavascriptRemedialKeySupport = 1 << 15;
|
||||
CPJavascriptRemedialKeySupport = 1 << 16;
|
||||
CPJavaScriptShadowFeature = 1 << 20;
|
||||
|
||||
CPJavaScriptNegativeMouseWheelValues = 1 << 22;
|
||||
@@ -54,7 +55,8 @@ CPJavaScriptMouseWheelValues_8_15 = 1 << 23
|
||||
CPOpacityRequiresFilterFeature = 1 << 24;
|
||||
|
||||
//Internet explorer does not allow dynamically changing the type of an input element
|
||||
CPInputTypeCanBeChangedFeature = 1 << 25;
|
||||
CPInputTypeCanBeChangedFeature = 1 << 25;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -100,15 +102,19 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
// Features we can only be sure of with WebKit (no known independent tests)
|
||||
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||
PLATFORM_FEATURES |= CPHTMLContentEditableFeature;
|
||||
PLATFORM_FEATURES |= CPHTMLDragAndDropFeature;
|
||||
PLATFORM_FEATURES |= CPJavascriptClipboardEventsFeature;
|
||||
PLATFORM_FEATURES |= CPJavascriptClipboardAccessFeature;
|
||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||
|
||||
var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
|
||||
versionEnd = USER_AGENT.indexOf(" ", versionStart),
|
||||
version = parseFloat(USER_AGENT.substring(versionStart, versionEnd), 10);
|
||||
versionString = USER_AGENT.substring(versionStart, versionEnd),
|
||||
versionDivision = versionString.indexOf('.'),
|
||||
majorVersion = parseInt(versionString.substring(0, versionDivision)),
|
||||
minorVersion = parseInt(versionString.substr(versionDivision + 1));
|
||||
|
||||
if(USER_AGENT.indexOf("Plainview") == -1 && version >= 525.14 || USER_AGENT.indexOf("Chrome") != -1)
|
||||
if((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion >= 525 && minorVersion > 14)) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
|
||||
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,9 +100,6 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
BOOL _trackingWasWithinFrame;
|
||||
unsigned _trackingMouseDownFlags;
|
||||
CGPoint _previousTrackingLocation;
|
||||
|
||||
JSObject _ephemeralSubviewsForNames;
|
||||
CPSet _ephereralSubviews;
|
||||
|
||||
CPString _toolTip;
|
||||
}
|
||||
@@ -181,7 +178,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
}
|
||||
|
||||
/*!
|
||||
Causes <code>anAction</code> to be sent to <code>anObject</code>.
|
||||
Causes \c anAction to be sent to \c anObject.
|
||||
@param anAction the action to send
|
||||
@param anObject the object to which the action will be sent
|
||||
*/
|
||||
@@ -304,6 +301,15 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
[self highlight:NO];
|
||||
}
|
||||
|
||||
- (void)setState:(int)state
|
||||
{
|
||||
}
|
||||
|
||||
- (int)nextState
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (unsigned)mouseDownFlags
|
||||
{
|
||||
return _trackingMouseDownFlags;
|
||||
@@ -505,10 +511,12 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
}
|
||||
|
||||
#define BRIDGE(UPPERCASE, LOWERCASE, ATTRIBUTENAME) \
|
||||
/*! Sets the value for ATTRIBUTENAME */\
|
||||
- (void)set##UPPERCASE:(id)aValue\
|
||||
{\
|
||||
[self setValue:aValue forThemeAttribute:ATTRIBUTENAME];\
|
||||
}\
|
||||
/*! Returns the current value for ATTRIBUTENAME */\
|
||||
- (id)LOWERCASE\
|
||||
{\
|
||||
return [self valueForThemeAttribute:ATTRIBUTENAME];\
|
||||
@@ -555,54 +563,6 @@ BRIDGE(ImageScaling, imageScaling, "image-scaling")
|
||||
return [self hasThemeState:CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aViewName
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
|
||||
{
|
||||
return _CGRectMakeZero();
|
||||
}
|
||||
|
||||
- (CPView)layoutEphemeralSubviewNamed:(CPString)aViewName
|
||||
positioned:(CPWindowOrderingMode)anOrderingMode
|
||||
relativeToEphemeralSubviewNamed:(CPString)relativeToViewName
|
||||
{
|
||||
if (!_ephemeralSubviewsForNames)
|
||||
{
|
||||
_ephemeralSubviewsForNames = {};
|
||||
_ephemeralSubviews = [CPSet set];
|
||||
}
|
||||
|
||||
var frame = [self rectForEphemeralSubviewNamed:aViewName];
|
||||
|
||||
if (frame && !_CGRectIsEmpty(frame))
|
||||
{
|
||||
if (!_ephemeralSubviewsForNames[aViewName])
|
||||
{
|
||||
_ephemeralSubviewsForNames[aViewName] = [self createEphemeralSubviewNamed:aViewName];
|
||||
|
||||
[_ephemeralSubviews addObject:_ephemeralSubviewsForNames[aViewName]];
|
||||
|
||||
if (_ephemeralSubviewsForNames[aViewName])
|
||||
[self addSubview:_ephemeralSubviewsForNames[aViewName] positioned:anOrderingMode relativeTo:_ephemeralSubviewsForNames[relativeToViewName]];
|
||||
}
|
||||
|
||||
if (_ephemeralSubviewsForNames[aViewName])
|
||||
[_ephemeralSubviewsForNames[aViewName] setFrame:frame];
|
||||
}
|
||||
else if (_ephemeralSubviewsForNames[aViewName])
|
||||
{
|
||||
[_ephemeralSubviewsForNames[aViewName] removeFromSuperview];
|
||||
|
||||
[_ephemeralSubviews removeObject:_ephemeralSubviewsForNames[aViewName]];
|
||||
delete _ephemeralSubviewsForNames[aViewName];
|
||||
}
|
||||
|
||||
return _ephemeralSubviewsForNames[aViewName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPControlValueKey = "CPControlValueKey",
|
||||
@@ -645,24 +605,8 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
var count = [_subviews count],
|
||||
ephemeral
|
||||
subviews = nil;
|
||||
|
||||
if (count > 0 && [_ephemeralSubviews count] > 0)
|
||||
{
|
||||
subviews = [_subviews.slice(0) copy];
|
||||
|
||||
while (count--)
|
||||
if ([_ephemeralSubviews containsObject:_subviews[count]])
|
||||
_subviews.splice(count, 1);
|
||||
}
|
||||
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
if (subviews)
|
||||
_subviews = subviews;
|
||||
|
||||
if (_value !== nil)
|
||||
[aCoder encodeObject:_value forKey:CPControlValueKey];
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes a cookie with a given name <code>aName</code>.
|
||||
Initializes a cookie with a given name \c aName.
|
||||
@param the name for the cookie
|
||||
*/
|
||||
- (id)initWithName:(CPString)aName
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
@import <Foundation/CPArray.j>
|
||||
|
||||
@import "CPResponder.j"
|
||||
@import "CPViewController.j"
|
||||
@import "CPWindowController.j"
|
||||
|
||||
|
||||
@@ -90,19 +91,23 @@ var CPDocumentUntitledCount = 0;
|
||||
CPDocuments should be used to represent this.
|
||||
*/
|
||||
@implementation CPDocument : CPResponder
|
||||
{
|
||||
CPURL _fileURL;
|
||||
CPString _fileType;
|
||||
CPArray _windowControllers;
|
||||
unsigned _untitledDocumentIndex;
|
||||
{
|
||||
CPWindow _window; // For outlet purposes.
|
||||
CPView _view; // For outlet purposes
|
||||
CPDictionary _viewControllersForWindowControllers;
|
||||
|
||||
BOOL _hasUndoManager;
|
||||
CPUndoManager _undoManager;
|
||||
|
||||
int _changeCount;
|
||||
|
||||
CPURLConnection _readConnection;
|
||||
CPURLRequest _writeRequest;
|
||||
CPURL _fileURL;
|
||||
CPString _fileType;
|
||||
CPArray _windowControllers;
|
||||
unsigned _untitledDocumentIndex;
|
||||
|
||||
BOOL _hasUndoManager;
|
||||
CPUndoManager _undoManager;
|
||||
|
||||
int _changeCount;
|
||||
|
||||
CPURLConnection _readConnection;
|
||||
CPURLRequest _writeRequest;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -116,10 +121,11 @@ var CPDocumentUntitledCount = 0;
|
||||
if (self)
|
||||
{
|
||||
_windowControllers = [];
|
||||
|
||||
_viewControllersForWindowControllers = [CPDictionary dictionary];
|
||||
|
||||
_hasUndoManager = YES;
|
||||
_changeCount = 0;
|
||||
|
||||
|
||||
[self setNextResponder:CPApp];
|
||||
}
|
||||
|
||||
@@ -159,10 +165,10 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
|
||||
[self setFileURL:anAbsoluteURL];
|
||||
[self setFileType:aType];
|
||||
|
||||
[self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -174,8 +180,8 @@ var CPDocumentUntitledCount = 0;
|
||||
@param absoluteContentsURL the location of the document's contents
|
||||
@param aType the type of the contents
|
||||
@param aDelegate this object will receive a callback after the document's contents are loaded
|
||||
@param aDidReadSelector the message selector that will be sent to <code>aDelegate</code>
|
||||
@param aContextInfo passed as the argument to the message sent to the <code>aDelegate</code>
|
||||
@param aDidReadSelector the message selector that will be sent to \c aDelegate
|
||||
@param aContextInfo passed as the argument to the message sent to the \c aDelegate
|
||||
@return the initialized document
|
||||
*/
|
||||
- (id)initForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
|
||||
@@ -184,10 +190,10 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
|
||||
[self setFileURL:anAbsoluteURL];
|
||||
[self setFileType:aType];
|
||||
|
||||
[self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -222,15 +228,68 @@ var CPDocumentUntitledCount = 0;
|
||||
reason:"readFromData:ofType: must be overridden by the document subclass."];
|
||||
}
|
||||
|
||||
- (void)viewControllerWillLoadCib:(CPViewController)aViewController
|
||||
{
|
||||
}
|
||||
|
||||
- (void)viewControllerDidLoadCib:(CPViewController)aViewController
|
||||
{
|
||||
}
|
||||
|
||||
- (CPWindowController)firstEligibleExistingWindowController
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Creating and managing window controllers
|
||||
/*!
|
||||
Creates the window controller for this document.
|
||||
*/
|
||||
- (void)makeWindowControllers
|
||||
{
|
||||
var controller = [[CPWindowController alloc] initWithWindowCibName:nil];
|
||||
|
||||
[self addWindowController:controller];
|
||||
[self makeViewAndWindowControllers];
|
||||
}
|
||||
|
||||
- (void)makeViewAndWindowControllers
|
||||
{
|
||||
var viewCibName = [self viewCibName],
|
||||
viewController = nil,
|
||||
windowController = nil;
|
||||
|
||||
// Create our view controller if we have a cib for it.
|
||||
if ([viewCibName length])
|
||||
viewController = [[CPViewController alloc] initWithCibName:viewCibName bundle:nil owner:self];
|
||||
|
||||
// If we have a view controller, check if we have a free window for it.
|
||||
if (viewController)
|
||||
windowController = [self firstEligibleExistingWindowController];
|
||||
|
||||
// If not, create one.
|
||||
if (!windowController)
|
||||
{
|
||||
var windowCibName = [self windowCibName];
|
||||
|
||||
// From a cib if we have one.
|
||||
if ([windowCibName length])
|
||||
windowController = [[CPWindowController alloc] initWithWindowCibName:windowCibName owner:self];
|
||||
|
||||
// If not you get a standard window capable of displaying multiple documents and view
|
||||
else if (viewController)
|
||||
{
|
||||
var view = [viewController view],
|
||||
theWindow = [[CPWindow alloc] initWithContentRect:[view frame] styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask];
|
||||
|
||||
[theWindow setSupportsMultipleDocuments:YES];
|
||||
|
||||
windowController = [[CPWindowController alloc] initWithWindow:theWindow];
|
||||
}
|
||||
}
|
||||
|
||||
if (windowController)
|
||||
[self addWindowController:windowController];
|
||||
|
||||
if (viewController)
|
||||
[self addViewController:viewController forWindowController:windowController];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -249,20 +308,50 @@ var CPDocumentUntitledCount = 0;
|
||||
- (void)addWindowController:(CPWindowController)aWindowController
|
||||
{
|
||||
[_windowControllers addObject:aWindowController];
|
||||
|
||||
if ([aWindowController document] != self)
|
||||
|
||||
if ([aWindowController document] !== self)
|
||||
{
|
||||
[aWindowController setNextResponder:self];
|
||||
[aWindowController setDocument:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (CPView)view
|
||||
{
|
||||
return _view;
|
||||
}
|
||||
|
||||
- (CPArray)viewControllers
|
||||
{
|
||||
return [_viewControllersForWindowControllers allValues];
|
||||
}
|
||||
|
||||
- (void)addViewController:(CPViewController)aViewController forWindowController:(CPWindowController)aWindowController
|
||||
{
|
||||
// FIXME: exception if we don't own the window controller?
|
||||
[_viewControllersForWindowControllers setObject:aViewController forKey:[aWindowController UID]];
|
||||
|
||||
if ([aWindowController document] === self)
|
||||
[aWindowController setViewController:aViewController];
|
||||
}
|
||||
|
||||
- (void)removeViewController:(CPViewController)aViewController
|
||||
{
|
||||
[_viewControllersForWindowControllers removeObject:aViewController];
|
||||
}
|
||||
|
||||
- (CPViewController)viewControllerForWindowController:(CPWindowController)aWindowController
|
||||
{
|
||||
return [_viewControllersForWindowControllers objectForKey:[aWindowController UID]];
|
||||
}
|
||||
|
||||
// Managing Document Windows
|
||||
/*!
|
||||
Shows all the document's windows.
|
||||
*/
|
||||
- (void)showWindows
|
||||
{
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(setDocument:) withObject:self];
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(showWindow:) withObject:self];
|
||||
}
|
||||
|
||||
@@ -283,6 +372,11 @@ var CPDocumentUntitledCount = 0;
|
||||
return @"Untitled " + _untitledDocumentIndex;
|
||||
}
|
||||
|
||||
- (CPString)viewCibName
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the document's Cib name
|
||||
*/
|
||||
@@ -292,18 +386,18 @@ var CPDocumentUntitledCount = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Called after <code>aWindowController<code> loads the document's Nib file.
|
||||
Called after \c aWindowController loads the document's Nib file.
|
||||
@param aWindowController the controller that loaded the Nib file
|
||||
*/
|
||||
- (void)windowControllerDidLoadNib:(CPWindowController)aWindowController
|
||||
- (void)windowControllerDidLoadCib:(CPWindowController)aWindowController
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Called before <code>aWindowController</code> will load the document's Nib file.
|
||||
Called before \c aWindowController will load the document's Nib file.
|
||||
@param aWindowController the controller that will load the Nib file
|
||||
*/
|
||||
- (void)windowControllerWillLoadNib:(CPWindowController)aWindowController
|
||||
- (void)windowControllerWillLoadCib:(CPWindowController)aWindowController
|
||||
{
|
||||
}
|
||||
|
||||
@@ -340,7 +434,7 @@ var CPDocumentUntitledCount = 0;
|
||||
*/
|
||||
- (void)setFileURL:(CPURL)aFileURL
|
||||
{
|
||||
if (_fileURL == aFileURL)
|
||||
if (_fileURL === aFileURL)
|
||||
return;
|
||||
|
||||
_fileURL = aFileURL;
|
||||
@@ -502,7 +596,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
// Managing Document Status
|
||||
/*!
|
||||
Returns <code>YES</code> if there are any unsaved changes.
|
||||
Returns \c YES if there are any unsaved changes.
|
||||
*/
|
||||
- (BOOL)isDocumentEdited
|
||||
{
|
||||
@@ -548,7 +642,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
// Working with Undo Manager
|
||||
/*!
|
||||
Returns <code>YES</code> if the document has a
|
||||
Returns \c YES if the document has a
|
||||
CPUndoManager.
|
||||
*/
|
||||
- (BOOL)hasUndoManager
|
||||
@@ -558,7 +652,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
/*!
|
||||
Sets whether the document should have a CPUndoManager.
|
||||
@param aFlag <code>YES</code> makes the document have an undo manager
|
||||
@param aFlag \c YES makes the document have an undo manager
|
||||
*/
|
||||
- (void)setHasUndoManager:(BOOL)aFlag
|
||||
{
|
||||
@@ -642,7 +736,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
/*!
|
||||
Returns the document's undo manager. If the document
|
||||
should have one, but the manager is <code>nil</code>, it
|
||||
should have one, but the manager is \c nil, it
|
||||
will be created and then returned.
|
||||
@return the document's undo manager
|
||||
*/
|
||||
@@ -666,8 +760,8 @@ var CPDocumentUntitledCount = 0;
|
||||
// Handling User Actions
|
||||
/*!
|
||||
Saves the document. If the document does not
|
||||
have a file path to save to (<code>fileURL</code>)
|
||||
then <code>saveDocumentAs:</code> will be called.
|
||||
have a file path to save to (\c fileURL)
|
||||
then \c -saveDocumentAs: will be called.
|
||||
@param aSender the object requesting the save
|
||||
*/
|
||||
- (void)saveDocument:(id)aSender
|
||||
|
||||
@@ -78,7 +78,7 @@ var CPSharedDocumentController = nil;
|
||||
method searches documents already open. It does not
|
||||
open the document at the URL if it is not already open.
|
||||
@param aURL the url of the document
|
||||
@return the document, or <code>nil</code> if such a document is not open
|
||||
@return the document, or \c nil if such a document is not open
|
||||
*/
|
||||
- (CPDocument)documentForURL:(CPURL)aURL
|
||||
{
|
||||
@@ -157,7 +157,7 @@ var CPSharedDocumentController = nil;
|
||||
@param anAbsoluteURL the document URL
|
||||
@param absoluteContentsURL the location of the document's contents
|
||||
@param anError not used
|
||||
@return the loaded document or <code>nil</code> if there was an error
|
||||
@return the loaded document or \c nil if there was an error
|
||||
*/
|
||||
- (CPDocument)reopenDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL error:(CPError)anError
|
||||
{
|
||||
@@ -187,7 +187,7 @@ var CPSharedDocumentController = nil;
|
||||
@param aDelegate receives a callback after the load has completed
|
||||
@param aSelector the selector to invoke for the callback
|
||||
@param aContextInfo an object passed as an argument for the callback
|
||||
@return a new document or <code>nil</code> if there was an error
|
||||
@return a new document or \c nil if there was an error
|
||||
*/
|
||||
- (CPDocument)makeDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
|
||||
{
|
||||
@@ -231,7 +231,7 @@ var CPSharedDocumentController = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds <code>aDocument</code> under the control of the receiver.
|
||||
Adds \c aDocument under the control of the receiver.
|
||||
@param aDocument the document to add
|
||||
*/
|
||||
- (void)addDocument:(CPDocument)aDocument
|
||||
@@ -240,7 +240,7 @@ var CPSharedDocumentController = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes <code>aDocument</code> from the control of the receiver.
|
||||
Removes \c aDocument from the control of the receiver.
|
||||
@param aDocument the document to remove
|
||||
*/
|
||||
- (void)removeDocument:(CPDocument)aDocument
|
||||
@@ -296,9 +296,9 @@ var CPSharedDocumentController = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the CPDocument subclass associated with <code>aType</code>.
|
||||
Returns the CPDocument subclass associated with \c aType.
|
||||
@param aType the type of document
|
||||
@return a Cappuccino Class object, or <code>nil</code> if no match was found
|
||||
@return a Cappuccino Class object, or \c nil if no match was found
|
||||
*/
|
||||
- (Class)documentClassForType:(CPString)aType
|
||||
{
|
||||
|
||||
@@ -26,73 +26,28 @@
|
||||
@import <AppKit/CPImageView.j>
|
||||
|
||||
#import "CoreGraphics/CGGeometry.h"
|
||||
#import "Platform/Platform.h"
|
||||
|
||||
|
||||
CPDragOperationNone = 0,
|
||||
CPDragOperationCopy = 1 << 1,
|
||||
CPDragOperationLink = 1 << 1,
|
||||
CPDragOperationGeneric = 1 << 2,
|
||||
CPDragOperationPrivate = 1 << 3,
|
||||
CPDragOperationMove = 1 << 4,
|
||||
CPDragOperationDelete = 1 << 5,
|
||||
CPDragOperationEvery = -1;
|
||||
|
||||
#define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window])
|
||||
|
||||
var CPSharedDragServer = nil;
|
||||
|
||||
var CPDragServerView = nil,
|
||||
CPDragServerSource = nil,
|
||||
CPDragServerWindow = nil,
|
||||
CPDragServerOffset = nil,
|
||||
CPDragServerLocation = nil,
|
||||
CPDragServerPasteboard = nil,
|
||||
CPDragServerDestination = nil,
|
||||
CPDragServerDraggingInfo = nil,
|
||||
CPDragServerPreviousEvent = nil,
|
||||
CPDragServerAutoscrollInterval = nil;
|
||||
|
||||
var CPDragServerIsDraggingImage = NO,
|
||||
|
||||
CPDragServerShouldSendDraggedViewMovedTo = NO,
|
||||
CPDragServerShouldSendDraggedImageMovedTo = NO,
|
||||
|
||||
CPDragServerShouldSendDraggedViewEndedAtOperation = NO,
|
||||
CPDragServerShouldSendDraggedImageEndedAtOperation = NO;
|
||||
|
||||
var CPDragServerPreviousEvent = nil,
|
||||
CPDragServerAutoscrollInterval = nil;
|
||||
/*
|
||||
var CPDragServerAutoscroll = function()
|
||||
{
|
||||
[CPDragServerSource autoscroll:CPDragServerPreviousEvent];
|
||||
}
|
||||
|
||||
var CPDragServerStartDragging = function(anEvent)
|
||||
{
|
||||
CPDragServerUpdateDragging(anEvent);
|
||||
}
|
||||
|
||||
var CPDragServerUpdateDragging = function(anEvent)
|
||||
{
|
||||
// If this is a mouse up, then complete the drag.
|
||||
if([anEvent type] == CPLeftMouseUp)
|
||||
{
|
||||
if (CPDragServerAutoscrollInterval !== nil)
|
||||
clearInterval(CPDragServerAutoscrollInterval);
|
||||
|
||||
CPDragServerAutoscrollInterval = nil;
|
||||
|
||||
CPDragServerLocation = [DRAGGING_WINDOW(CPDragServerDestination) 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 (CPDragServerAutoscrollInterval === nil)
|
||||
{
|
||||
if ([CPDragServerSource respondsToSelector:@selector(autoscroll:)])
|
||||
@@ -101,43 +56,16 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
|
||||
CPDragServerPreviousEvent = anEvent;
|
||||
|
||||
// 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];
|
||||
if (CPDragServerAutoscrollInterval !== nil)
|
||||
clearInterval(CPDragServerAutoscrollInterval);
|
||||
|
||||
var location = [anEvent locationInWindow],
|
||||
operation =
|
||||
bridgeLocation = [[anEvent window] convertBaseToBridge:location];
|
||||
CPDragServerAutoscrollInterval = nil;
|
||||
*/
|
||||
|
||||
// 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 = [DRAGGING_WINDOW(draggingDestination) 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];
|
||||
}
|
||||
var CPSharedDragServer = nil;
|
||||
|
||||
var CPDragServerSource = nil;
|
||||
var CPDragServerDraggingInfo = nil;
|
||||
|
||||
/*
|
||||
CPDraggingInfo is a container of information about a specific dragging session.
|
||||
@@ -147,24 +75,36 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
{
|
||||
}
|
||||
|
||||
- (CPPasteboard)draggingPasteboard
|
||||
{
|
||||
if ([CPPlatform supportsDragAndDrop])
|
||||
return [_CPDOMDataTransferPasteboard DOMDataTransferPasteboard];
|
||||
|
||||
return [[CPDragServer sharedDragServer] draggingPasteboard];
|
||||
}
|
||||
|
||||
- (id)draggingSource
|
||||
{
|
||||
return CPDragServerSource;
|
||||
return [[CPDragServer sharedDragServer] draggingSource];
|
||||
}
|
||||
|
||||
/*
|
||||
- (unsigned)draggingSourceOperationMask
|
||||
*/
|
||||
|
||||
- (CPPoint)draggingLocation
|
||||
{
|
||||
return CPDragServerLocation;
|
||||
return [[CPDragServer sharedDragServer] draggingLocation];
|
||||
}
|
||||
|
||||
- (CPPasteboard)draggingPasteboard
|
||||
- (CPWindow)draggingDestinationWindow
|
||||
{
|
||||
return CPDragServerPasteboard;
|
||||
return DRAGGING_WINDOW([[CPDragServer sharedDragServer] draggingDestination]);
|
||||
}
|
||||
|
||||
- (CPImage)draggedImage
|
||||
{
|
||||
return [CPDragServerView image];
|
||||
return [[self draggedView] image];
|
||||
}
|
||||
|
||||
- (CGPoint)draggedImageLocation
|
||||
@@ -172,22 +112,44 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
return [self draggedViewLocation];
|
||||
}
|
||||
|
||||
- (CGPoint)draggedViewLocation
|
||||
{
|
||||
return [DRAGGING_WINDOW(CPDragServerDestination) convertBridgeToBase:[CPDragServerView frame].origin];
|
||||
}
|
||||
|
||||
- (CPView)draggedView
|
||||
{
|
||||
return CPDragServerView;
|
||||
return [[CPDragServer sharedDragServer] draggedView];
|
||||
}
|
||||
|
||||
- (CGPoint)draggedViewLocation
|
||||
{
|
||||
var dragServer = [CPDragServer sharedDragServer];
|
||||
|
||||
return [DRAGGING_WINDOW([dragServer draggingDestination]) convertPlatformWindowToBase:[[dragServer draggedView] frame].origin];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
CPDraggingSource_draggedImage_endAt_operation_ = 1 << 1,
|
||||
CPDraggingSource_draggedView_movedTo_ = 1 << 2,
|
||||
CPDraggingSource_draggedView_endedAt_operation_ = 1 << 3;
|
||||
|
||||
@implementation CPDragServer : CPObject
|
||||
{
|
||||
CPWindow _dragWindow;
|
||||
CPImageView _imageView;
|
||||
BOOL _isDragging @accessors(readonly, getter=isDragging);
|
||||
|
||||
CPWindow _draggedWindow @accessors(readonly, getter=draggedWindow);
|
||||
CPView _draggedView @accessors(readonly, getter=draggedView);
|
||||
CPImageView _imageView;
|
||||
|
||||
BOOL _isDraggingImage;
|
||||
|
||||
CGSize _draggingOffset @accessors(readonly, getter=draggingOffset);
|
||||
|
||||
CPPasteboard _draggingPasteboard @accessors(readonly, getter=draggingPasteboard);
|
||||
|
||||
id _draggingSource @accessors(readonly, getter=draggingSource);
|
||||
unsigned _implementedDraggingSourceMethods;
|
||||
|
||||
CGPoint _draggingLocation;
|
||||
id _draggingDestination;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -196,9 +158,9 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
*/
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [CPDragServer class])
|
||||
if (self !== [CPDragServer class])
|
||||
return;
|
||||
|
||||
|
||||
CPDragServerDraggingInfo = [[CPDraggingInfo alloc] init];
|
||||
}
|
||||
|
||||
@@ -206,7 +168,7 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
{
|
||||
if (!CPSharedDragServer)
|
||||
CPSharedDragServer = [[CPDragServer alloc] init];
|
||||
|
||||
|
||||
return CPSharedDragServer;
|
||||
}
|
||||
|
||||
@@ -216,16 +178,104 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_dragWindow = [[CPWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:CPBorderlessWindowMask];
|
||||
[_dragWindow setLevel:CPDraggingWindowLevel];
|
||||
_draggedWindow = [[CPWindow alloc] initWithContentRect:_CGRectMakeZero() styleMask:CPBorderlessWindowMask];
|
||||
|
||||
[_draggedWindow setLevel:CPDraggingWindowLevel];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGPoint)draggingLocation
|
||||
{
|
||||
return _draggingLocation
|
||||
}
|
||||
|
||||
- (void)draggingStartedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation
|
||||
{
|
||||
if (_isDraggingImage)
|
||||
{
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedImage:beganAt:)])
|
||||
[_draggingSource draggedImage:[_draggedView image] beganAt:aLocation];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedView:beganAt:)])
|
||||
[_draggingSource draggedView:_draggedView beganAt:aLocation];
|
||||
}
|
||||
|
||||
if (![CPPlatform supportsDragAndDrop])
|
||||
[_draggedWindow orderFront:self];
|
||||
}
|
||||
|
||||
- (void)draggingSourceUpdatedWithGlobalLocation:(CGPoint)aGlobalLocation
|
||||
{
|
||||
if (![CPPlatform supportsDragAndDrop])
|
||||
[_draggedWindow setFrameOrigin:_CGPointMake(aGlobalLocation.x - _draggingOffset.width, aGlobalLocation.y - _draggingOffset.height)];
|
||||
|
||||
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_movedTo_)
|
||||
[_draggingSource draggedImage:[_draggedView image] movedTo:aGlobalLocation];
|
||||
|
||||
else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_movedTo_)
|
||||
[_draggingSource draggedView:_draggedView movedTo:aGlobalLocation];
|
||||
}
|
||||
|
||||
- (CPDragOperation)draggingUpdatedInPlatformWindow:(CPPlatformWindow)aPlatformWindow location:(CGPoint)aLocation
|
||||
{
|
||||
var dragOperation = CPDragOperationCopy;
|
||||
// We have to convert base to bridge since the drag event comes from the source window, not the drag window.
|
||||
var draggingDestination = [aPlatformWindow _dragHitTest:aLocation pasteboard:[CPDragServerDraggingInfo draggingPasteboard]];
|
||||
|
||||
if (draggingDestination)
|
||||
_draggingLocation = [DRAGGING_WINDOW(draggingDestination) convertPlatformWindowToBase:aLocation];
|
||||
|
||||
if(draggingDestination !== _draggingDestination)
|
||||
{
|
||||
if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingExited:)])
|
||||
[_draggingDestination draggingExited:CPDragServerDraggingInfo];
|
||||
|
||||
_draggingDestination = draggingDestination;
|
||||
|
||||
if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingEntered:)])
|
||||
dragOperation = [_draggingDestination draggingEntered:CPDragServerDraggingInfo];
|
||||
}
|
||||
else if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingUpdated:)])
|
||||
dragOperation = [_draggingDestination draggingUpdated:CPDragServerDraggingInfo];
|
||||
|
||||
if (!_draggingDestination)
|
||||
dragOperation = CPDragOperationNone;
|
||||
|
||||
return dragOperation;
|
||||
}
|
||||
|
||||
- (void)draggingEndedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation
|
||||
{
|
||||
[_draggedView removeFromSuperview];
|
||||
|
||||
if (![CPPlatform supportsDragAndDrop])
|
||||
[_draggedWindow orderOut:self];
|
||||
|
||||
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_endAt_operation_)
|
||||
[_draggingSource draggedImage:[_draggedView image] endedAt:aLocation operation:NO];
|
||||
|
||||
else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_endedAt_operation_)
|
||||
[_draggingSource draggedView:_draggedView endedAt:aLocation operation:NO];
|
||||
|
||||
_isDragging = NO;
|
||||
}
|
||||
|
||||
- (void)performDragOperationInPlatformWindow:(CPPlatformWindow)aPlatformWindow
|
||||
{
|
||||
if (_draggingDestination &&
|
||||
(![_draggingDestination respondsToSelector:@selector(prepareForDragOperation:)] || [_draggingDestination prepareForDragOperation:CPDragServerDraggingInfo]) &&
|
||||
(![_draggingDestination respondsToSelector:@selector(performDragOperation:)] || [_draggingDestination performDragOperation:CPDragServerDraggingInfo]) &&
|
||||
[_draggingDestination respondsToSelector:@selector(concludeDragOperation:)])
|
||||
[_draggingDestination concludeDragOperation:CPDragServerDraggingInfo];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initiates a drag session.
|
||||
@param aView the view being dragged
|
||||
@@ -235,50 +285,70 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
@param anEvent
|
||||
@param aPasteboard the pasteboard that contains the drag data
|
||||
@param aSourceObject the object where the drag started
|
||||
@param slideBack if <code>YES</code>, <code>aView</code> slides back to
|
||||
@param slideBack if \c YES, \c aView slides back to
|
||||
its origin on a failed drop
|
||||
*/
|
||||
- (void)dragView:(CPView)aView fromWindow:(CPWindow)aWindow at:(CGPoint)viewLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
|
||||
- (void)dragView:(CPView)aView fromWindow:(CPWindow)aWindow at:(CGPoint)viewLocation offset:(CGSize)mouseOffset event:(CPEvent)mouseDownEvent 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;
|
||||
_isDragging = YES;
|
||||
|
||||
[_dragWindow setFrameSize:CGSizeMakeCopy([[CPDOMWindowBridge sharedDOMWindowBridge] frame].size)];
|
||||
[_dragWindow orderFront:self];
|
||||
_draggedView = aView;
|
||||
_draggingPasteboard = aPasteboard || [CPPasteboard pasteboardWithName:CPDragPboard];
|
||||
_draggingSource = aSourceObject;
|
||||
_draggingDestination = nil;
|
||||
|
||||
[aView setFrameOrigin:viewLocation];
|
||||
[[_dragWindow contentView] addSubview:aView];
|
||||
// The offset is based on the distance from where we want the view to be initially from where the mouse is initially
|
||||
// Hence the use of mouseDownEvent's location and view's location in global coordinates.
|
||||
var mouseDownWindow = [mouseDownEvent window],
|
||||
mouseDownEventLocation = [mouseDownEvent locationInWindow];
|
||||
|
||||
if (CPDragServerIsDraggingImage)
|
||||
if (mouseDownEventLocation)
|
||||
{
|
||||
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;
|
||||
if (mouseDownWindow)
|
||||
mouseDownEventLocation = [mouseDownWindow convertBaseToGlobal:mouseDownEventLocation];
|
||||
|
||||
_draggingOffset = _CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y);
|
||||
}
|
||||
else
|
||||
_draggingOffset = _CGSizeMakerZero();
|
||||
|
||||
if ([CPPlatform isBrowser])
|
||||
[_draggedWindow setPlatformWindow:[aWindow platformWindow]];
|
||||
|
||||
[aView setFrameOrigin:_CGPointMakeZero()];
|
||||
|
||||
var mouseLocation = [CPEvent mouseLocation];
|
||||
|
||||
// Place it where the mouse pointer is.
|
||||
[_draggedWindow setFrameOrigin:_CGPointMake(mouseLocation.x - _draggingOffset.width, mouseLocation.y - _draggingOffset.height)];
|
||||
[_draggedWindow setFrameSize:[aView frame].size];
|
||||
|
||||
[[_draggedWindow contentView] addSubview:aView];
|
||||
|
||||
_implementedDraggingSourceMethods = 0;
|
||||
|
||||
if (_draggedView === _imageView)
|
||||
{
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedImage:movedTo:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_movedTo_;
|
||||
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedImage:endAt:operation:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_endAt_operation_;
|
||||
}
|
||||
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:)];
|
||||
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedView:movedTo:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedView_movedTo_;
|
||||
|
||||
CPDragServerShouldSendDraggedImageMovedTo = NO;
|
||||
CPDragServerShouldSendDraggedImageEndedAtOperation = NO;
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedView:endedAt:operation:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedView_endedAt_operation_;
|
||||
}
|
||||
|
||||
CPDragServerStartDragging(anEvent);
|
||||
if (![CPPlatform supportsDragAndDrop])
|
||||
{
|
||||
[self draggingStartedInPlatformWindow:[aWindow platformWindow] globalLocation:mouseLocation];
|
||||
[self trackDragging:mouseDownEvent];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -290,22 +360,47 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
@param anEvent
|
||||
@param aPasteboard the pasteboard where the drag data is located
|
||||
@param aSourceObject the object where the drag started
|
||||
@param slideBack if <code>YES</code>, <code>aView</code> slides back to
|
||||
@param slideBack if \c YES, \c aView slides back to
|
||||
its origin on a failed drop
|
||||
*/
|
||||
- (void)dragImage:(CPImage)anImage fromWindow:(CPWindow)aWindow at:(CGPoint)imageLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
|
||||
{
|
||||
CPDragServerIsDraggingImage = YES;
|
||||
|
||||
_isDraggingImage = YES;
|
||||
|
||||
var imageSize = [anImage size];
|
||||
|
||||
if (!_imageView)
|
||||
_imageView = [[CPImageView alloc] initWithFrame:CPRectMakeZero()];
|
||||
|
||||
_imageView = [[CPImageView alloc] initWithFrame:_CGRectMake(0.0, 0.0, imageSize.width, imageSize.height)];
|
||||
|
||||
[_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];
|
||||
}
|
||||
|
||||
- (void)trackDragging:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type],
|
||||
platformWindow = [_draggedWindow platformWindow],
|
||||
platformWindowLocation = [[anEvent window] convertBaseToPlatformWindow:[anEvent locationInWindow]];
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
[self performDragOperationInPlatformWindow:platformWindow];
|
||||
[self draggingEndedInPlatformWindow:platformWindow globalLocation:platformWindowLocation];
|
||||
|
||||
// Stop tracking events.
|
||||
return;
|
||||
}
|
||||
|
||||
[self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation];
|
||||
[self draggingUpdatedInPlatformWindow:platformWindow location:platformWindowLocation];
|
||||
|
||||
// If we're not a mouse up, then we're going to want to grab the next event.
|
||||
[CPApp setTarget:self selector:@selector(trackDragging:)
|
||||
forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask
|
||||
untilDate:nil inMode:0 dequeue:NO];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPWindow (CPDraggingAdditions)
|
||||
@@ -318,23 +413,23 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
return nil;
|
||||
|
||||
// We don't need to do this because the only place this gets called
|
||||
// -_dragHitTest: in CPDOMWindowBridge does this already. Perhaps to
|
||||
// -_dragHitTest: in CPPlatformWindow does this already. Perhaps to
|
||||
// be safe?
|
||||
// if (![self containsPoint:aPoint])
|
||||
// return nil;
|
||||
|
||||
var adjustedPoint = _CGPointMake(aPoint.x - _CGRectGetMinX(_frame), aPoint.y - _CGRectGetMinY(_frame)),
|
||||
var adjustedPoint = [self convertPlatformWindowToBase:aPoint],
|
||||
hitView = [_windowView hitTest:adjustedPoint];
|
||||
|
||||
while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]])
|
||||
hitView = [hitView superview];
|
||||
|
||||
|
||||
if (hitView)
|
||||
return hitView;
|
||||
|
||||
|
||||
if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]])
|
||||
return self;
|
||||
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -221,16 +221,16 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
/*!
|
||||
Creates a new keyboard event.
|
||||
@param anEventType the event type. Must be one of CPKeyDown, CPKeyUp or CPFlagsChanged
|
||||
@param aPoint the location of the cursor in the window specified by <code>aWindowNumber</code>
|
||||
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||
@param aTimestamp the time the event occurred
|
||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||
@param aGraphicsContext the graphics context where the event occurred
|
||||
@param characters the characters associated with the event
|
||||
@param unmodCharacters the string of keys pressed without the presence of any modifiers other than Shift
|
||||
@param repeatKey <code>YES</code> if this is caused by the system repeat as opposed to the user pressing the key again
|
||||
@param repeatKey \c YES if this is caused by the system repeat as opposed to the user pressing the key again
|
||||
@param code a number associated with the keyboard key of this event
|
||||
@throws CPInternalInconsistencyException if <code>anEventType</code> is not a CPKeyDown,
|
||||
@throws CPInternalInconsistencyException if \c anEventType is not a CPKeyDown,
|
||||
CPKeyUp or CPFlagsChanged
|
||||
@return the keyboard event
|
||||
*/
|
||||
@@ -246,7 +246,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
/*!
|
||||
Creates a new mouse event
|
||||
@param anEventType the event type
|
||||
@param aPoint the location of the cursor in the window specified by <code>aWindowNumber</code>
|
||||
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||
@param aTimestamp the time the event occurred
|
||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||
@@ -268,7 +268,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
/*!
|
||||
Creates a new custom event
|
||||
@param anEventType the event type. Must be one of CPAppKitDefined, CPSystemDefined, CPApplicationDefined or CPPeriodic
|
||||
@param aLocation the location of the cursor in the window specified by <code>aWindowNumber</code>
|
||||
@param aLocation the location of the cursor in the window specified by \c aWindowNumber
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||
@param aTimestamp the time the event occurred
|
||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||
@@ -358,15 +358,15 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
|
||||
/*!
|
||||
Returns the location of the mouse (for mouse events).
|
||||
If this is not a mouse event, it returns <code>nil</code>.
|
||||
If <code>window</code> returns <code>nil</code>, then
|
||||
If this is not a mouse event, it returns \c nil.
|
||||
If \c window returns \c nil, then
|
||||
the mouse coordinates will be based on the screen coordinates.
|
||||
Otherwise, the coordinates are relative to the window's coordinates.
|
||||
@return the location of the mouse, or <code>nil</code> for non-mouse events.
|
||||
@return the location of the mouse, or \c nil for non-mouse events.
|
||||
*/
|
||||
- (CGPoint)locationInWindow
|
||||
{
|
||||
return _location;
|
||||
return _CGPointMakeCopy(_location);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -448,7 +448,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the keyboard event was caused by the key being held down.
|
||||
Returns \c YES if the keyboard event was caused by the key being held down.
|
||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||
*/
|
||||
- (BOOL)isARepeat
|
||||
@@ -465,6 +465,18 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _keyCode;
|
||||
}
|
||||
|
||||
+ (CGPoint)mouseLocation
|
||||
{
|
||||
// FIXME: this is incorrect, we shouldn't depend on the current event.
|
||||
var event = [CPApp currentEvent],
|
||||
eventWindow = [event window];
|
||||
|
||||
if (eventWindow)
|
||||
return [eventWindow convertBaseToGlobal:[event locationInWindow]];
|
||||
|
||||
return [event locationInWindow];
|
||||
}
|
||||
|
||||
- (float)pressure
|
||||
{
|
||||
return _pressure;
|
||||
@@ -504,7 +516,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
}
|
||||
|
||||
/*!
|
||||
Generates periodic events every <code>aPeriod</code> seconds.
|
||||
Generates periodic events every \c aPeriod seconds.
|
||||
@param aDelay the number of seconds before the first event
|
||||
@param aPeriod the length of time in seconds between successive events
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a new Flash movie with the swf at <code>aFileName</code>.
|
||||
Creates a new Flash movie with the swf at \c aFileName.
|
||||
@param aFilename the swf to load
|
||||
@return the initialized CPFlashMovie
|
||||
*/
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPDOMWindowBridge.j"
|
||||
@import "CPFlashMovie.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
@@ -96,17 +96,17 @@
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -62,11 +62,11 @@ function CPPointMake(x, y)
|
||||
}
|
||||
|
||||
/*!
|
||||
Makes a CGRect with an origin and size equal to <code>aRect</code> less the <code>dX/dY</code> insets specified.
|
||||
Makes a CGRect with an origin and size equal to \c aRect less the \c dX/dY insets specified.
|
||||
@param dX the size of the inset in the x-axis
|
||||
@param dY the size of the inset in the y-axis
|
||||
@group CGRect
|
||||
@return CGRect a rectangle like <code>aRect</code> with an inset
|
||||
@return CGRect a rectangle like \c aRect with an inset
|
||||
*/
|
||||
function CPRectInset(aRect, dX, dY)
|
||||
{
|
||||
@@ -132,7 +132,7 @@ function CPRectMake(x, y, width, height)
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a new rectangle with its origin offset by <code>dX</code> and <code>dY</code>.
|
||||
Creates a new rectangle with its origin offset by \c dX and \c dY.
|
||||
@group CGRect
|
||||
@param aRect the rectangle to copy the origin and size from
|
||||
@param dX the amount added to the x-size of the new rectangle
|
||||
@@ -171,7 +171,7 @@ function CPRectStandardize(aRect)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the smallest rectangle that can contain the two argument <code>CGRect</code>s.
|
||||
Returns the smallest rectangle that can contain the two argument \c CGRects.
|
||||
@group CGRect
|
||||
@param lhsRect the first CGRect to use for the union calculation
|
||||
@param rhsRect the second CGRect to use for the union calculation
|
||||
@@ -211,12 +211,12 @@ function CPSizeMake(width, height)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the CGRect, <code>aRect</code>, contains
|
||||
the CGPoint, <code>aPoint</code>.
|
||||
Returns \c YES if the CGRect, \c aRect, contains
|
||||
the CGPoint, \c aPoint.
|
||||
@param aRect the rectangle to test with
|
||||
@param aPoint the point to test with
|
||||
@group CGRect
|
||||
@return BOOL <code>YES</code> if the rectangle contains the point, <code>NO</code> otherwise.
|
||||
@return BOOL \c YES if the rectangle contains the point, \c NO otherwise.
|
||||
*/
|
||||
function CPRectContainsPoint(aRect, aPoint)
|
||||
{
|
||||
@@ -227,12 +227,12 @@ function CPRectContainsPoint(aRect, aPoint)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a <code>BOOL</code> indicating whether CGRect <code>possibleOuter</code>
|
||||
contains CGRect <code>possibleInner</code>.
|
||||
Returns a \c BOOL indicating whether CGRect \c possibleOuter
|
||||
contains CGRect \c possibleInner.
|
||||
@group CGRect
|
||||
@param possibleOuter the CGRect to test if <code>possibleInner</code> is inside of
|
||||
@param possibleInner the CGRect to test if it fits inside <code>possibleOuter</code>.
|
||||
@return BOOL <code>YES</code> if <code>possibleInner</code> fits inside <code>possibleOuter</code>.
|
||||
@param possibleOuter the CGRect to test if \c possibleInner is inside of
|
||||
@param possibleInner the CGRect to test if it fits inside \c possibleOuter.
|
||||
@return BOOL \c YES if \c possibleInner fits inside \c possibleOuter.
|
||||
*/
|
||||
function CPRectContainsRect(lhsRect, rhsRect)
|
||||
{
|
||||
@@ -241,11 +241,11 @@ function CPRectContainsRect(lhsRect, rhsRect)
|
||||
|
||||
/*!
|
||||
Tests whether the two CGPoints are equal to each other by comparing their
|
||||
<code>x</code> and <code>y</code> members.
|
||||
\c x and \c y members.
|
||||
@group @CGPoint
|
||||
@param lhsPoint the first CGPoint to check
|
||||
@param rhsPoint the second CGPoint to check
|
||||
@return BOOL <code>YES</code> if the two points have the same x's, and the same y's.
|
||||
@return BOOL \c YES if the two points have the same x's, and the same y's.
|
||||
*/
|
||||
function CPPointEqualToPoint(lhsPoint, rhsPoint)
|
||||
{
|
||||
@@ -257,7 +257,7 @@ function CPPointEqualToPoint(lhsPoint, rhsPoint)
|
||||
@group CGRect
|
||||
@param lhsRect the first CGRect to compare
|
||||
@param rhsRect the second CGRect to compare
|
||||
@return BOOL <code>YES</code> if the two rectangles have the same origin and size. <code>NO</code>, otherwise.
|
||||
@return BOOL \c YES if the two rectangles have the same origin and size. \c NO, otherwise.
|
||||
*/
|
||||
function CPRectEqualToRect(lhsRect, rhsRect)
|
||||
{
|
||||
@@ -346,11 +346,11 @@ function CPRectGetWidth(aRect)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the two rectangles intersect
|
||||
Returns \c YES if the two rectangles intersect
|
||||
@group CGRect
|
||||
@param lhsRect the first CGRect
|
||||
@param rhsRect the second CGRect
|
||||
@return BOOL <code>YES</code> if the two rectangles have any common spaces, and <code>NO</code>, otherwise.
|
||||
@return BOOL \c YES if the two rectangles have any common spaces, and \c NO, otherwise.
|
||||
*/
|
||||
function CPRectIntersectsRect(lhsRect, rhsRect)
|
||||
{
|
||||
@@ -358,11 +358,11 @@ function CPRectIntersectsRect(lhsRect, rhsRect)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the CGRect has no area.
|
||||
Returns \c YES if the CGRect has no area.
|
||||
The test is performed by checking if the width and height are both zero.
|
||||
@group CGRect
|
||||
@param aRect the CGRect to test
|
||||
@return BOOL <code>YES</code> if the CGRect has no area, and <code>NO</code>, otherwise.
|
||||
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
||||
*/
|
||||
function CPRectIsEmpty(aRect)
|
||||
{
|
||||
@@ -370,10 +370,10 @@ function CPRectIsEmpty(aRect)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the CGRect has no area.
|
||||
Returns \c YES if the CGRect has no area.
|
||||
The test is performed by checking if the width and height are both zero.
|
||||
@group CGRect
|
||||
@return BOOL <code>YES</code> if the CGRect has no area, and <code>NO</code>, otherwise.
|
||||
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
||||
*/
|
||||
function CPRectIsNull(aRect)
|
||||
{
|
||||
@@ -381,11 +381,11 @@ function CPRectIsNull(aRect)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the two CGSizes are identical.
|
||||
Returns \c YES if the two CGSizes are identical.
|
||||
@group CGSize
|
||||
@param lhsSize the first CGSize to compare
|
||||
@param rhsSize the second CGSize to compare
|
||||
@return BOOL <code>YES</code> if the two sizes are identical. <code>NO</code>, otherwise.
|
||||
@return BOOL \c YES if the two sizes are identical. \c NO, otherwise.
|
||||
*/
|
||||
function CPSizeEqualToSize(lhsSize, rhsSize)
|
||||
{
|
||||
@@ -454,7 +454,7 @@ function CPSizeFromString(aString)
|
||||
/*!
|
||||
Returns a CGRect created from a string.
|
||||
@group CGRect
|
||||
@param aString a string in the form generated by <code>CPStringFromRect</code>
|
||||
@param aString a string in the form generated by \c CPStringFromRect
|
||||
@return CGRect the rectangle created from the string
|
||||
*/
|
||||
function CPRectFromString(aString)
|
||||
@@ -477,7 +477,7 @@ function CPPointFromEvent(anEvent)
|
||||
/*!
|
||||
Returns a zero sized CGSize.
|
||||
@group CGSize
|
||||
@return CGSize a size object with zeros for <code>width</code> and <code>height</code>
|
||||
@return CGSize a size object with zeros for \c width and \c height
|
||||
*/
|
||||
function CPSizeMakeZero()
|
||||
{
|
||||
@@ -485,7 +485,7 @@ function CPSizeMakeZero()
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a rectangle at origin <code>(0,0)</code> and size of <code>(0,0)</code>.
|
||||
Returns a rectangle at origin \c (0,0) and size of \c (0,0).
|
||||
@group CGRect
|
||||
@return CGRect a zeroed out CGRect
|
||||
*/
|
||||
@@ -495,9 +495,9 @@ function CPRectMakeZero()
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a point located at <code>(0, 0)</code>.
|
||||
Returns a point located at \c (0, 0).
|
||||
@group CGPoint
|
||||
@return CGPoint a point located at <code>(0, 0)</code>
|
||||
@return CGPoint a point located at \c (0, 0)
|
||||
*/
|
||||
function CPPointMakeZero()
|
||||
{
|
||||
|
||||
@@ -92,7 +92,7 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
|
||||
/*!
|
||||
Initializes the image, by associating it with a filename. The image
|
||||
denoted in <code>aFilename</code> is not actually loaded. It will
|
||||
denoted in \c aFilename is not actually loaded. It will
|
||||
be loaded once needed.
|
||||
@param aFilename the file containing the image
|
||||
@param aSize the image's size
|
||||
@@ -187,9 +187,9 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the image data has already been loaded.
|
||||
Returns the load status, which will be CPImageLoadStatusCompleted if the image data has already been loaded.
|
||||
*/
|
||||
- (BOOL)loadStatus
|
||||
- (unsigned)loadStatus
|
||||
{
|
||||
return _loadStatus;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,9 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
|
||||
BOOL _hasShadow;
|
||||
CPView _shadowView;
|
||||
|
||||
|
||||
BOOL _isEditable;
|
||||
|
||||
CGRect _imageRect;
|
||||
}
|
||||
|
||||
@@ -85,7 +87,13 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
_DOMImageElement.style.position = "absolute";
|
||||
_DOMImageElement.style.left = "0px";
|
||||
_DOMImageElement.style.top = "0px";
|
||||
|
||||
|
||||
if ([CPPlatform supportsDragAndDrop])
|
||||
{
|
||||
_DOMImageElement.setAttribute("draggable", "true");
|
||||
_DOMImageElement.style["-khtml-user-drag"] = "element";
|
||||
}
|
||||
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement);
|
||||
|
||||
_DOMImageElement.style.visibility = "hidden";
|
||||
@@ -159,8 +167,8 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the image view draws with
|
||||
a drop shadow. The default is <code>NO</code>.
|
||||
Returns \c YES if the image view draws with
|
||||
a drop shadow. The default is \c NO.
|
||||
*/
|
||||
- (BOOL)hasShadow
|
||||
{
|
||||
@@ -339,11 +347,47 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
[[self nextResponder] mouseDown:anEvent];
|
||||
}
|
||||
|
||||
- (void)setEditable:(BOOL)shouldBeEditable
|
||||
{
|
||||
if (_isEditable === shouldBeEditable)
|
||||
return;
|
||||
|
||||
_isEditable = shouldBeEditable;
|
||||
|
||||
if (_isEditable)
|
||||
[self registerForDraggedTypes:[CPImagesPboardType]];
|
||||
|
||||
else
|
||||
{
|
||||
var draggedTypes = [self registeredDraggedTypes];
|
||||
|
||||
[self unregisterDraggedTypes];
|
||||
|
||||
[draggedTypes removeObjectIdenticalTo:CPImagesPboardType];
|
||||
|
||||
[self registerForDraggedTypes:draggedTypes];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
- (void)performDragOperation:(CPDraggingInfo)aSender
|
||||
{
|
||||
var images = [CPKeyedUnarchiver unarchiveObjectWithData:[[aSender draggingPasteboard] dataForType:CPImagesPboardType]];
|
||||
|
||||
if ([images count])
|
||||
[self setImage:images[0]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
|
||||
CPImageViewHasShadowKey = @"CPImageViewHasShadowKey";
|
||||
CPImageViewHasShadowKey = @"CPImageViewHasShadowKey",
|
||||
CPImageViewIsEditableKey = @"CPImageViewIsEditableKey";
|
||||
|
||||
@implementation CPImageView (CPCoding)
|
||||
|
||||
@@ -360,6 +404,11 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
_DOMImageElement.style.left = "0px";
|
||||
_DOMImageElement.style.top = "0px";
|
||||
_DOMImageElement.style.visibility = "hidden";
|
||||
if ([CPPlatform supportsDragAndDrop])
|
||||
{
|
||||
_DOMImageElement.setAttribute("draggable", "true");
|
||||
_DOMImageElement.style["-khtml-user-drag"] = "element";
|
||||
}
|
||||
#endif
|
||||
|
||||
self = [super initWithCoder:aCoder];
|
||||
@@ -372,6 +421,9 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
|
||||
[self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
|
||||
|
||||
if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO)
|
||||
[self setEditable:YES];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
@@ -402,6 +454,9 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
_subviews = actualSubviews;
|
||||
|
||||
[aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey];
|
||||
|
||||
if (_isEditable)
|
||||
[aCoder encodeBool:_isEditable forKey:CPImageViewIsEditableKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -60,6 +60,7 @@ var _CPMenuBarVisible = NO,
|
||||
CPMenu _supermenu;
|
||||
|
||||
CPString _title;
|
||||
CPString _name;
|
||||
|
||||
CPMutableArray _items;
|
||||
CPMenu _attachedMenu;
|
||||
@@ -91,7 +92,10 @@ var _CPMenuBarVisible = NO,
|
||||
return;
|
||||
|
||||
_CPMenuBarVisible = menuBarShouldBeVisible;
|
||||
|
||||
|
||||
if ([CPPlatform supportsNativeMainMenu])
|
||||
return;
|
||||
|
||||
if (menuBarShouldBeVisible)
|
||||
{
|
||||
if (!_CPMenuBarSharedWindow)
|
||||
@@ -119,7 +123,7 @@ var _CPMenuBarVisible = NO,
|
||||
|
||||
// FIXME: There must be a better way to do this.
|
||||
#if PLATFORM(DOM)
|
||||
[[CPDOMWindowBridge sharedDOMWindowBridge] _bridgeResizeEvent:nil];
|
||||
[[CPPlatformWindow primaryPlatformWindow] resizeEvent:nil];
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -267,13 +271,13 @@ var _CPMenuBarVisible = NO,
|
||||
- (void)insertItem:(CPMenuItem)aMenuItem atIndex:(unsigned)anIndex
|
||||
{
|
||||
var menu = [aMenuItem menu];
|
||||
|
||||
|
||||
if (menu)
|
||||
if (menu != self)
|
||||
if (menu !== self)
|
||||
[CPException raise:CPInternalInconsistencyException reason:@"Attempted to insert item into menu that was already in another menu."];
|
||||
else
|
||||
return;
|
||||
|
||||
|
||||
[aMenuItem setMenu:self];
|
||||
[_items insertObject:aMenuItem atIndex:anIndex];
|
||||
|
||||
@@ -369,9 +373,9 @@ var _CPMenuBarVisible = NO,
|
||||
/*!
|
||||
Returns the menu item with the specified tag
|
||||
@param the tag of the desired menu item
|
||||
@return the menu item or <code>nil</code> if a match was not found
|
||||
@return the menu item or \c nil if a match was not found
|
||||
*/
|
||||
- (CPMenuItem)menuWithTag:(int)aTag
|
||||
- (CPMenuItem)itemWithTag:(int)aTag
|
||||
{
|
||||
var index = [self indexOfItemWithTag:aTag];
|
||||
|
||||
@@ -384,9 +388,9 @@ var _CPMenuBarVisible = NO,
|
||||
/*!
|
||||
Returns the menu item with the specified title.
|
||||
@param aTitle the title of the menu item
|
||||
@return the menu item or <code>nil</code> if a match was not found
|
||||
@return the menu item or \c nil if a match was not found
|
||||
*/
|
||||
- (CPMenuItem)menuWithTitle:(CPString)aTitle
|
||||
- (CPMenuItem)itemWithTitle:(CPString)aTitle
|
||||
{
|
||||
var index = [self indexOfItemWithTitle:aTitle];
|
||||
|
||||
@@ -551,7 +555,7 @@ var _CPMenuBarVisible = NO,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the attaced menu, or <code>nil</code> if there isn't one.
|
||||
Returns the attaced menu, or \c nil if there isn't one.
|
||||
*/
|
||||
- (CPMenu)attachedMenu
|
||||
{
|
||||
@@ -559,7 +563,7 @@ var _CPMenuBarVisible = NO,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the menu is attached to another menu.
|
||||
Returns \c YES if the menu is attached to another menu.
|
||||
*/
|
||||
- (BOOL)isAttached
|
||||
{
|
||||
@@ -575,7 +579,7 @@ var _CPMenuBarVisible = NO,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the super menu or <code>nil</code> if there is none.
|
||||
Returns the super menu or \c nil if there is none.
|
||||
*/
|
||||
- (CPMenu)supermenu
|
||||
{
|
||||
@@ -592,8 +596,8 @@ var _CPMenuBarVisible = NO,
|
||||
}
|
||||
|
||||
/*!
|
||||
If there are two instances of this menu visible, return <code>NO</code>.
|
||||
Otherwise, return <code>YES</code> if we are a detached menu and visible.
|
||||
If there are two instances of this menu visible, return \c NO.
|
||||
Otherwise, return \c YES if we are a detached menu and visible.
|
||||
*/
|
||||
- (BOOL)isTornOff
|
||||
{
|
||||
@@ -603,7 +607,7 @@ var _CPMenuBarVisible = NO,
|
||||
// Enabling and Disabling Menu Items
|
||||
/*!
|
||||
Sets whether the menu automatically enables menu items.
|
||||
@param aFlag <code>YES</code> sets the menu to automatically enable items.
|
||||
@param aFlag \c YES sets the menu to automatically enable items.
|
||||
*/
|
||||
- (void)setAutoenablesItems:(BOOL)aFlag
|
||||
{
|
||||
@@ -611,7 +615,7 @@ var _CPMenuBarVisible = NO,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the menu auto enables items.
|
||||
Returns \c YES if the menu auto enables items.
|
||||
*/
|
||||
- (BOOL)autoenablesItems
|
||||
{
|
||||
@@ -672,7 +676,7 @@ var _CPMenuBarVisible = NO,
|
||||
[menuWindow setDelegate:self];
|
||||
[menuWindow setBackgroundStyle:isForMenuBar ? _CPMenuWindowMenuBarBackgroundStyle : _CPMenuWindowPopUpBackgroundStyle];
|
||||
|
||||
[menuWindow setFrameOrigin:[[anEvent window] convertBaseToBridge:[anEvent locationInWindow]]];
|
||||
[menuWindow setFrameOrigin:[[anEvent window] convertBaseToGlobal:[anEvent locationInWindow]]];
|
||||
|
||||
[menuWindow orderFront:self];
|
||||
[menuWindow beginTrackingWithEvent:anEvent sessionDelegate:self didEndSelector:@selector(_menuWindowDidFinishTracking:highlightedItem:)];
|
||||
@@ -686,17 +690,12 @@ var _CPMenuBarVisible = NO,
|
||||
|
||||
if([aMenuItem isEnabled])
|
||||
[CPApp sendAction:[aMenuItem action] to:[aMenuItem target] from:aMenuItem];
|
||||
|
||||
var delegate = [menu delegate];
|
||||
|
||||
if ([delegate respondsToSelector:@selector(menuDidClose:)])
|
||||
[delegate menuDidClose:menu];
|
||||
}
|
||||
|
||||
// Managing Display of State Column
|
||||
/*!
|
||||
Sets whether to show the state column
|
||||
@param shouldShowStateColumn <code>YES</code> shows the state column
|
||||
@param shouldShowStateColumn \c YES shows the state column
|
||||
*/
|
||||
- (void)setShowsStateColumn:(BOOL)shouldShowStateColumn
|
||||
{
|
||||
@@ -704,7 +703,7 @@ var _CPMenuBarVisible = NO,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the menu shows the state column
|
||||
Returns \c YES if the menu shows the state column
|
||||
*/
|
||||
- (BOOL)showsStateColumn
|
||||
{
|
||||
@@ -714,7 +713,7 @@ var _CPMenuBarVisible = NO,
|
||||
// Handling Highlighting
|
||||
/*!
|
||||
Returns the currently highlighted menu item.
|
||||
@return the highlighted menu item or <code>nil</code> if no item is currently highlighted
|
||||
@return the highlighted menu item or \c nil if no item is currently highlighted
|
||||
*/
|
||||
- (CPMenuItem)highlightedItem
|
||||
{
|
||||
@@ -750,9 +749,9 @@ var _CPMenuBarVisible = NO,
|
||||
|
||||
/*!
|
||||
Initiates the action of the menu item that
|
||||
has a keyboard shortcut equivalent to <code>anEvent</code>
|
||||
has a keyboard shortcut equivalent to \c anEvent
|
||||
@param anEvent the keyboard event
|
||||
@return <code>YES</code> if it was handled.
|
||||
@return \c YES if it was handled.
|
||||
*/
|
||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||
{
|
||||
@@ -826,10 +825,36 @@ var _CPMenuBarVisible = NO,
|
||||
[[_items[_highlightedIndex] _menuItemView] highlight:YES];
|
||||
}
|
||||
|
||||
- (void)_setMenuName:(CPString)aName
|
||||
{
|
||||
if (_name === aName)
|
||||
return;
|
||||
|
||||
_name = aName;
|
||||
|
||||
if (_name === @"CPMainMenu")
|
||||
[CPApp setMainMenu:self];
|
||||
}
|
||||
|
||||
- (CPString)_menuName
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
if (_name === @"_CPMainMenu")
|
||||
{
|
||||
[self _setMenuName:@"CPMainMenu"];
|
||||
[CPMenu setMenuBarVisible:YES];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPMenuTitleKey = @"CPMenuTitleKey",
|
||||
CPMenuNameKey = @"CPMenuNameKey",
|
||||
CPMenuItemsKey = @"CPMenuItemsKey",
|
||||
CPMenuShowsStateColumnKey = @"CPMenuShowsStateColumnKey";
|
||||
|
||||
@@ -849,6 +874,8 @@ var CPMenuTitleKey = @"CPMenuTitleKey",
|
||||
_title = [aCoder decodeObjectForKey:CPMenuTitleKey];
|
||||
_items = [aCoder decodeObjectForKey:CPMenuItemsKey];
|
||||
|
||||
[self _setMenuName:[aCoder decodeObjectForKey:CPMenuNameKey]];
|
||||
|
||||
_showsStateColumn = ![aCoder containsValueForKey:CPMenuShowsStateColumnKey] || [aCoder decodeBoolForKey:CPMenuShowsStateColumnKey];
|
||||
}
|
||||
|
||||
@@ -862,6 +889,10 @@ var CPMenuTitleKey = @"CPMenuTitleKey",
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_title forKey:CPMenuTitleKey];
|
||||
|
||||
if (_name)
|
||||
[aCoder encodeObject:_name forKey:CPMenuNameKey];
|
||||
|
||||
[aCoder encodeObject:_items forKey:CPMenuItemsKey];
|
||||
|
||||
if (!_showsStateColumn)
|
||||
@@ -909,7 +940,7 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
|
||||
CPTimeInterval _startTime;
|
||||
int _scrollingState;
|
||||
CGPoint _lastScreenLocation;
|
||||
CGPoint _lastGlobalLocation;
|
||||
|
||||
BOOL _isShowingTopScrollIndicator;
|
||||
BOOL _isShowingBottomScrollIndicator;
|
||||
@@ -926,10 +957,10 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
menuWindow = _CPMenuWindowPool.pop();
|
||||
else
|
||||
menuWindow = [[_CPMenuWindow alloc] init];
|
||||
|
||||
|
||||
[menuWindow setFont:aFont];
|
||||
[menuWindow setMenu:aMenu];
|
||||
|
||||
|
||||
return menuWindow;
|
||||
}
|
||||
|
||||
@@ -1081,35 +1112,42 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
|
||||
- (void)constrainToScreen
|
||||
{
|
||||
// FIXME: There are integral window issues with platform windows.
|
||||
// FIXME: This gets called far too often.
|
||||
_unconstrainedFrame = CGRectMakeCopy([self frame]);
|
||||
|
||||
var screenBounds = CGRectInset([[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds], 5.0, 5.0),
|
||||
constrainedFrame = CGRectIntersection(_unconstrainedFrame, screenBounds),
|
||||
menuViewOrigin = [self convertBaseToBridge:CGPointMake(LEFT_MARGIN, TOP_MARGIN)];
|
||||
|
||||
var isBrowser = [CPPlatform isBrowser],
|
||||
visibleFrame = CGRectInset(isBrowser ? [[self platformWindow] contentBounds] : [[self screen] visibleFrame], 5.0, 5.0),
|
||||
constrainedFrame = CGRectIntersection(_unconstrainedFrame, visibleFrame);
|
||||
|
||||
// We don't want to simply intersect the visible frame and the unconstrained frame.
|
||||
// We should be allowing as much of the width to fit as possible (pushing back and forward).
|
||||
constrainedFrame.origin.x = CGRectGetMinX(_unconstrainedFrame);
|
||||
constrainedFrame.size.width = CGRectGetWidth(_unconstrainedFrame);
|
||||
|
||||
if (CGRectGetWidth(constrainedFrame) > CGRectGetWidth(screenBounds))
|
||||
constrainedFrame.size.width = CGRectGetWidth(screenBounds);
|
||||
|
||||
if (CGRectGetMaxX(constrainedFrame) > CGRectGetMaxX(screenBounds))
|
||||
constrainedFrame.origin.x -= CGRectGetMaxX(constrainedFrame) - CGRectGetMaxX(screenBounds);
|
||||
|
||||
if (CGRectGetMinX(constrainedFrame) < CGRectGetMinX(screenBounds))
|
||||
constrainedFrame.origin.x = CGRectGetMinX(screenBounds);
|
||||
|
||||
|
||||
if (CGRectGetWidth(constrainedFrame) > CGRectGetWidth(visibleFrame))
|
||||
constrainedFrame.size.width = CGRectGetWidth(visibleFrame);
|
||||
|
||||
if (CGRectGetMaxX(constrainedFrame) > CGRectGetMaxX(visibleFrame))
|
||||
constrainedFrame.origin.x -= CGRectGetMaxX(constrainedFrame) - CGRectGetMaxX(visibleFrame);
|
||||
|
||||
if (CGRectGetMinX(constrainedFrame) < CGRectGetMinX(visibleFrame))
|
||||
constrainedFrame.origin.x = CGRectGetMinX(visibleFrame);
|
||||
|
||||
// This needs to happen before changing the frame.
|
||||
var menuViewOrigin = [self convertBaseToGlobal:CGPointMake(LEFT_MARGIN, TOP_MARGIN)];
|
||||
|
||||
[super setFrame:constrainedFrame];
|
||||
|
||||
var topMargin = TOP_MARGIN,
|
||||
|
||||
var moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + TOP_MARGIN,
|
||||
moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - BOTTOM_MARGIN,
|
||||
|
||||
topMargin = TOP_MARGIN,
|
||||
bottomMargin = BOTTOM_MARGIN,
|
||||
|
||||
contentView = [self contentView],
|
||||
bounds = [contentView bounds];
|
||||
|
||||
var moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + TOP_MARGIN,
|
||||
moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - BOTTOM_MARGIN;
|
||||
|
||||
|
||||
if (moreAbove)
|
||||
{
|
||||
topMargin += SCROLL_INDICATOR_HEIGHT;
|
||||
@@ -1118,9 +1156,9 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
|
||||
[_moreAboveView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(frame)) / 2.0, (TOP_MARGIN + SCROLL_INDICATOR_HEIGHT - CGRectGetHeight(frame)) / 2.0)];
|
||||
}
|
||||
|
||||
|
||||
[_moreAboveView setHidden:!moreAbove];
|
||||
|
||||
|
||||
if (moreBelow)
|
||||
{
|
||||
bottomMargin += SCROLL_INDICATOR_HEIGHT;
|
||||
@@ -1129,13 +1167,13 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
}
|
||||
|
||||
[_moreBelowView setHidden:!moreBelow];
|
||||
|
||||
|
||||
var clipFrame = CGRectMake(LEFT_MARGIN, topMargin, CGRectGetWidth(constrainedFrame) - LEFT_MARGIN - RIGHT_MARGIN, CGRectGetHeight(constrainedFrame) - topMargin - bottomMargin)
|
||||
|
||||
|
||||
[_menuClipView setFrame:clipFrame];
|
||||
[_menuView setFrameSize:CGSizeMake(CGRectGetWidth(clipFrame), CGRectGetHeight([_menuView frame]))];
|
||||
|
||||
[_menuView scrollPoint:CGPointMake(0.0, [self convertBaseToBridge:clipFrame.origin].y - menuViewOrigin.y)];
|
||||
|
||||
[_menuView scrollPoint:CGPointMake(0.0, [self convertBaseToGlobal:clipFrame.origin].y - menuViewOrigin.y)];
|
||||
}
|
||||
|
||||
- (void)cancelTracking
|
||||
@@ -1159,11 +1197,11 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
{
|
||||
var type = [anEvent type],
|
||||
theWindow = [anEvent window],
|
||||
screenLocation = theWindow ? [theWindow convertBaseToBridge:[anEvent locationInWindow]] : [anEvent locationInWindow];
|
||||
|
||||
if (type == CPPeriodic)
|
||||
globalLocation = theWindow ? [theWindow convertBaseToGlobal:[anEvent locationInWindow]] : [anEvent locationInWindow];
|
||||
|
||||
if (type === CPPeriodic)
|
||||
{
|
||||
var constrainedBounds = CGRectInset([[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds], 5.0, 5.0);
|
||||
var constrainedBounds = CGRectInset([CPPlatform isBrowser] ? [[self platformWindow] contentBounds] : [[self screen] visibleFrame], 5.0, 5.0);
|
||||
|
||||
if (_scrollingState == _CPMenuWindowScrollingStateUp)
|
||||
{
|
||||
@@ -1177,13 +1215,13 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
[self constrainToScreen];
|
||||
|
||||
screenLocation = _lastScreenLocation;
|
||||
globalLocation = _lastGlobalLocation;
|
||||
}
|
||||
|
||||
_lastScreenLocation = screenLocation;
|
||||
_lastGlobalLocation = globalLocation;
|
||||
|
||||
var menu = [_menuView menu],
|
||||
menuLocation = [self convertBridgeToBase:screenLocation],
|
||||
menuLocation = [self convertGlobalToBase:globalLocation],
|
||||
activeItemIndex = [_menuView itemIndexAtPoint:[_menuView convertPoint:menuLocation fromView:nil]],
|
||||
mouseOverMenuView = [[menu itemAtIndex:activeItemIndex] view];
|
||||
|
||||
@@ -1214,7 +1252,7 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
_lastMouseOverMenuView = nil;
|
||||
}
|
||||
|
||||
[menu _highlightItemAtIndex:[_menuView itemIndexAtPoint:[_menuView convertPoint:[self convertBridgeToBase:screenLocation] fromView:nil]]];
|
||||
[menu _highlightItemAtIndex:[_menuView itemIndexAtPoint:[_menuView convertPoint:[self convertGlobalToBase:globalLocation] fromView:nil]]];
|
||||
|
||||
if (type == CPMouseMoved || type == CPLeftMouseDragged || type == CPLeftMouseDown)
|
||||
{
|
||||
@@ -1224,11 +1262,11 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
_scrollingState = _CPMenuWindowScrollingStateNone;
|
||||
|
||||
// If we're at or above of the top scroll indicator...
|
||||
if (screenLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT)
|
||||
if (globalLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT)
|
||||
_scrollingState = _CPMenuWindowScrollingStateUp;
|
||||
|
||||
// If we're at or below the bottom scroll indicator...
|
||||
else if (screenLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT)
|
||||
else if (globalLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT)
|
||||
_scrollingState = _CPMenuWindowScrollingStateDown;
|
||||
|
||||
if (_scrollingState != oldScrollingState)
|
||||
@@ -1259,23 +1297,23 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
|
||||
[menu _highlightItemAtIndex:CPNotFound];
|
||||
|
||||
// Clear these now so its faster next time around.
|
||||
[_menuView setMenu:nil];
|
||||
|
||||
[self orderOut:self];
|
||||
|
||||
if (_sessionDelegate && _didEndSelector)
|
||||
objj_msgSend(_sessionDelegate, _didEndSelector, self, highlightedItem);
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPMenuDidEndTrackingNotification
|
||||
object:menu];
|
||||
|
||||
|
||||
var delegate = [menu delegate];
|
||||
|
||||
if ([delegate respondsToSelector:@selector(menuDidClose:)])
|
||||
[delegate menuDidClose:menu];
|
||||
|
||||
|
||||
if (_sessionDelegate && _didEndSelector)
|
||||
objj_msgSend(_sessionDelegate, _didEndSelector, self, highlightedItem);
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPMenuDidEndTrackingNotification
|
||||
object:menu];
|
||||
|
||||
// Clear these now so its faster next time around.
|
||||
[_menuView setMenu:nil];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1452,10 +1490,12 @@ var _CPMenuBarWindowBackgroundColor = nil,
|
||||
|
||||
- (id)init
|
||||
{
|
||||
var bridgeWidth = CGRectGetWidth([[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds]);
|
||||
|
||||
self = [super initWithContentRect:CGRectMake(0.0, 0.0, bridgeWidth, MENUBAR_HEIGHT) styleMask:CPBorderlessWindowMask];
|
||||
|
||||
var contentRect = [CPPlatform isBrowser] ? [[CPPlatformWindow primaryPlatformWindow] contentBounds] : [[self screen] visibleFrame];
|
||||
|
||||
contentRect.size.height = MENUBAR_HEIGHT;
|
||||
|
||||
self = [super initWithContentRect:contentRect styleMask:CPBorderlessWindowMask];
|
||||
|
||||
if (self)
|
||||
{
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/39-dont-allow-windows-to-go-above-menubar
|
||||
@@ -1735,7 +1775,7 @@ var _CPMenuBarWindowBackgroundColor = nil,
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (CPView)menuItemAtPoint:(CGPoint)aPoint
|
||||
- (CPMenuItem)menuItemAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var items = [_menu itemArray],
|
||||
count = items.length;
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
// Enabling a Menu Item
|
||||
/*!
|
||||
Sets whether the menu item is enabled or not
|
||||
@param isEnabled <code>YES</code> enables the item. <code>NO</code> disables it.
|
||||
@param isEnabled \c YES enables the item. \c NO disables it.
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)isEnabled
|
||||
{
|
||||
@@ -128,7 +128,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the item is enabled.
|
||||
Returns \c YES if the item is enabled.
|
||||
*/
|
||||
- (BOOL)isEnabled
|
||||
{
|
||||
@@ -138,7 +138,7 @@
|
||||
// Managing Hidden Status
|
||||
/*!
|
||||
Sets whether the item should be hidden. A hidden item can not be triggered by keyboard shortcuts.
|
||||
@param isHidden <code>YES</code> hides the item. <code>NO</code> reveals it.
|
||||
@param isHidden \c YES hides the item. \c NO reveals it.
|
||||
*/
|
||||
- (void)setHidden:(BOOL)isHidden
|
||||
{
|
||||
@@ -151,7 +151,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the item is hidden.
|
||||
Returns \c YES if the item is hidden.
|
||||
*/
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
@@ -159,7 +159,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the item is hidden or if one of it's supermenus is hidden.
|
||||
Returns \c YES if the item is hidden or if one of it's supermenus is hidden.
|
||||
*/
|
||||
- (BOOL)isHiddenOrHasHiddenAncestor
|
||||
{
|
||||
@@ -461,7 +461,7 @@ CPOffState
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the submenu of the item. <code>nil</code> if there is no submenu.
|
||||
Returns the submenu of the item. \c nil if there is no submenu.
|
||||
*/
|
||||
- (CPMenu)submenu
|
||||
{
|
||||
@@ -469,7 +469,7 @@ CPOffState
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the menu item has a submenu.
|
||||
Returns \c YES if the menu item has a submenu.
|
||||
*/
|
||||
- (BOOL)hasSubmenu
|
||||
{
|
||||
@@ -491,7 +491,7 @@ CPOffState
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the menu item is a separator.
|
||||
Returns \c YES if the menu item is a separator.
|
||||
*/
|
||||
- (BOOL)isSeparatorItem
|
||||
{
|
||||
@@ -613,7 +613,7 @@ CPControlKeyMask
|
||||
|
||||
/*!
|
||||
Sets whether this item is an alternate for the previous menu item.
|
||||
@param isAlternate <code>YES</code> denotes that this menu item is an alternate
|
||||
@param isAlternate \c YES denotes that this menu item is an alternate
|
||||
*/
|
||||
- (void)setAlternate:(BOOL)isAlternate
|
||||
{
|
||||
@@ -621,7 +621,7 @@ CPControlKeyMask
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the menu item is an alternate for the previous item.
|
||||
Returns \c YES if the menu item is an alternate for the previous item.
|
||||
*/
|
||||
- (BOOL)isAlternate
|
||||
{
|
||||
@@ -717,7 +717,7 @@ CPControlKeyMask
|
||||
// Getting Highlighted Status
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the menu item is highlighted.
|
||||
Returns \c YES if the menu item is highlighted.
|
||||
*/
|
||||
- (BOOL)isHighlighted
|
||||
{
|
||||
@@ -1188,7 +1188,7 @@ var _CPMenuItemSelectionColor = nil,
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
return [_menuItem isEnabled] ? (_textColor ? _textColor : [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0]) : [CPColor darkGrayColor];
|
||||
return [_menuItem isEnabled] ? (_textColor ? _textColor : [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0]) : [CPColor lightGrayColor];
|
||||
}
|
||||
|
||||
- (void)setTextShadowColor:(CPColor)aColor
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2008, 280 North, Inc.
|
||||
* Copyright 2009, 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
|
||||
@@ -20,93 +20,834 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
This class is a subclass of CPTableView which provides the user with a way to display
|
||||
tree structured data in an outline format. It is particularly useful for displaying hierarchical data
|
||||
such as a class inheritance tree or any other set of relationships.
|
||||
*/
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
CPOutlineViewColumnDidMoveNotification = @"CPOutlineViewColumnDidMoveNotification";
|
||||
CPOutlineViewColumnDidResizeNotification = @"CPOutlineViewColumnDidResizeNotification";
|
||||
CPOutlineViewItemDidCollapseNotification = @"CPOutlineViewItemDidCollapseNotification";
|
||||
CPOutlineViewItemDidExpandNotification = @"CPOutlineViewItemDidExpandNotification";
|
||||
CPOutlineViewItemWillCollapseNotification = @"CPOutlineViewItemWillCollapseNotification";
|
||||
CPOutlineViewItemWillExpandNotification = @"CPOutlineViewItemWillExpandNotification";
|
||||
CPOutlineViewSelectionDidChangeNotification = @"CPOutlineViewSelectionDidChangeNotification";
|
||||
CPOutlineViewSelectionIsChangingNotification = @"CPOutlineViewSelectionIsChangingNotification";
|
||||
|
||||
var CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_ = 1 << 1,
|
||||
CPOutlineViewDataSource_outlineView_shouldDeferDisplayingChildrenOfItem_ = 1 << 2,
|
||||
|
||||
CPOutlineViewDataSource_outlineView_acceptDrop_item_childIndex_ = 1 << 3,
|
||||
CPOutlineViewDataSource_outlineView_validateDrop_proposedItem_proposedChildIndex_ = 1 << 4,
|
||||
CPOutlineViewDataSource_outlineView_validateDrop_proposedRow_proposedDropOperation_ = 1 << 5,
|
||||
CPOutlineViewDataSource_outlineView_namesOfPromisedFilesDroppedAtDestination_forDraggedItems_ = 1 << 6,
|
||||
|
||||
CPOutlineViewDataSource_outlineView_itemForPersistentObject_ = 1 << 7,
|
||||
CPOutlineViewDataSource_outlineView_persistentObjectForItem_ = 1 << 8,
|
||||
|
||||
CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_ = 1 << 9,
|
||||
|
||||
CPOutlineViewDataSource_outlineView_sortDescriptorsDidChange_ = 1 << 10;
|
||||
|
||||
@implementation CPOutlineView : CPTableView
|
||||
{
|
||||
id _outlineDataSource;
|
||||
CPArray _itemsByRow;
|
||||
id _outlineViewDataSource;
|
||||
id _outlineViewDelegate;
|
||||
CPTableColumn _outlineTableColumn;
|
||||
|
||||
float _indentationPerLevel;
|
||||
BOOL _indentationMarkerFollowsDataView;
|
||||
|
||||
CPInteger _implementedOutlineViewDataSourceMethods;
|
||||
|
||||
Object _rootItemInfo;
|
||||
CPMutableArray _itemsForRows;
|
||||
Object _itemInfosForItems;
|
||||
|
||||
CPControl _disclosureControlPrototype;
|
||||
CPArray _disclosureControlsForRows;
|
||||
CPData _disclosureControlData;
|
||||
CPArray _disclosureControlQueue;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[super setDataSource:self];
|
||||
_itemsByRow = [[CPArray alloc] init];
|
||||
// The root item has weight "0", thus represents the weight solely of its descendants.
|
||||
_rootItemInfo = { isExpanded:YES, isExpandable:NO, level:-1, row:-1, children:[], weight:0 };
|
||||
|
||||
_itemsForRows = [];
|
||||
_itemInfosForItems = { };
|
||||
_disclosureControlsForRows = [];
|
||||
|
||||
[self setIndentationPerLevel:16.0];
|
||||
[self setIndentationMarkerFollowsDataView:YES];
|
||||
|
||||
[super setDataSource:[[_CPOutlineViewTableViewDataSource alloc] initWithOutlineView:self]];
|
||||
|
||||
[self setDisclosureControlPrototype:[[CPDisclosureButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 10.0)]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
Sets the outline's data source. The data source must implement the following methods:
|
||||
<pre>
|
||||
- (id)outlineView:(CPOutlineView)outlineView child:(int)index ofItem:(id)item
|
||||
- (BOOL)outlineView:(CPOutlineView)outlineView isItemExpandable:(id)item
|
||||
- (int)outlineView:(CPOutlineView)outlineView numberOfChildrenOfItem:(id)item
|
||||
- (id)outlineView:(CPOutlineView)outlineView objectValueForTableColumn:(CPTableColumn)tableColumn byItem:(id)item
|
||||
</pre>
|
||||
@param aDataSource the outline's data source
|
||||
@throws CPInternalInconsistencyException if the data source does not implement all the required methods
|
||||
*/
|
||||
- (void)setDataSource:(id)aDataSource
|
||||
{
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:child:ofItem)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:child:ofItem'"];
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:isItemExpandable)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:isItemExpandable'"];
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:numberOfChildrenOfItem)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:numberOfChildrenOfItem'"];
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:objectValueForTableColumn:byItem)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:objectValueForTableColumn:byItem'"];
|
||||
if (_outlineViewDataSource === aDataSource)
|
||||
return;
|
||||
|
||||
_outlineDataSource = aDataSource;
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:child:ofItem:)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:child:ofItem:'"];
|
||||
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:isItemExpandable:)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:isItemExpandable:'"];
|
||||
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:numberOfChildrenOfItem:)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:numberOfChildrenOfItem:'"];
|
||||
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:objectValueForTableColumn:byItem:)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:objectValueForTableColumn:byItem:'"];
|
||||
|
||||
_outlineViewDataSource = aDataSource;
|
||||
_implementedOutlineViewDataSourceMethods = 0;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:setObjectValue:forTableColumn:byItem:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:shouldDeferDisplayingChildrenOfItem:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_shouldDeferDisplayingChildrenOfItem_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:acceptDrop:item:childIndex:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_acceptDrop_item_childIndex_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:validateDrop:proposedItem:proposedChildIndex:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_validateDrop_proposedItem_proposedChildIndex_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:validateDrop:proposedRow:proposedDropOperation:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_validateDrop_proposedRow_proposedDropOperation_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:namesOfPromisedFilesDroppedAtDestination:forDraggedItems:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_namesOfPromisedFilesDroppedAtDestination_forDraggedItems_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:itemForPersistentObject:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_itemForPersistentObject_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:persistentObjectForItem:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_persistentObjectForItem_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:writeItems:toPasteboard:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:sortDescriptorsDidChange:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_sortDescriptorsDidChange_;
|
||||
|
||||
[self reloadData];
|
||||
}
|
||||
/* @ignore */
|
||||
|
||||
- (id)dataSource
|
||||
{
|
||||
return _outlineViewDataSource;
|
||||
}
|
||||
|
||||
- (BOOL)isExpandable:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return YES;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return NO;
|
||||
|
||||
return itemInfo.isExpandable;
|
||||
}
|
||||
|
||||
- (void)isItemExpanded:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return YES;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return NO;
|
||||
|
||||
return itemInfo.isExpanded;
|
||||
}
|
||||
|
||||
- (void)expandItem:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return;
|
||||
|
||||
if (itemInfo.isExpanded)
|
||||
return;
|
||||
|
||||
itemInfo.isExpanded = YES;
|
||||
|
||||
[self reloadItem:anItem reloadChildren:YES];
|
||||
}
|
||||
|
||||
- (void)collapseItem:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return;
|
||||
|
||||
if (!itemInfo.isExpanded)
|
||||
return;
|
||||
|
||||
itemInfo.isExpanded = NO;
|
||||
|
||||
[self reloadItem:anItem reloadChildren:YES];
|
||||
}
|
||||
|
||||
- (void)reloadItem:(id)anItem
|
||||
{
|
||||
[self reloadItem:anItem reloadChildren:NO];
|
||||
}
|
||||
|
||||
- (void)reloadItem:(id)anItem reloadChildren:(BOOL)shouldReloadChildren
|
||||
{
|
||||
if (!!shouldReloadChildren || !anItem)
|
||||
_loadItemInfoForItem(self, anItem);
|
||||
else
|
||||
_reloadItem(self, anItem);
|
||||
|
||||
[super reloadData];
|
||||
}
|
||||
|
||||
- (id)itemAtRow:(CPInteger)aRow
|
||||
{
|
||||
return _itemsForRows[aRow] || nil;
|
||||
}
|
||||
|
||||
- (CPInteger)rowForItem:(id)aItem
|
||||
{
|
||||
if (!anItem)
|
||||
return _rootItemInfo.row;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return CPNotFound;
|
||||
|
||||
return itemInfo.row;
|
||||
}
|
||||
|
||||
- (void)setOutlineTableColumn:(CPTableColumn)aTableColumn
|
||||
{
|
||||
if (_outlineTableColumn === aTableColumn)
|
||||
return;
|
||||
|
||||
_outlineTableColumn = aTableColumn;
|
||||
|
||||
// FIXME: efficiency.
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (CPTableColumn)outlineTableColumn
|
||||
{
|
||||
return _outlineTableColumn;
|
||||
}
|
||||
|
||||
- (CPInteger)levelForItem:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return _rootItemInfo.level;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return CPNotFound;
|
||||
|
||||
return itemInfo.level;
|
||||
}
|
||||
|
||||
- (CPInteger)levelForRow:(CPInteger)aRow
|
||||
{
|
||||
return [self levelForItem:[self itemAtRow:aRow]];
|
||||
}
|
||||
|
||||
- (void)setIndentationPerLevel:(float)anIndentationWidth
|
||||
{
|
||||
if (_indentationPerLevel === anIndentationWidth)
|
||||
return;
|
||||
|
||||
_indentationPerLevel = anIndentationWidth;
|
||||
|
||||
// FIXME: efficiency!!!!
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (float)indentationPerLevel
|
||||
{
|
||||
return _indentationPerLevel;
|
||||
}
|
||||
|
||||
- (void)setIndentationMarkerFollowsDataView:(BOOL)indentationMarkerShouldFollowDataView
|
||||
{
|
||||
if (_indentationMarkerFollowsDataView === indentationMarkerShouldFollowDataView)
|
||||
return;
|
||||
|
||||
_indentationMarkerFollowsDataView = indentationMarkerShouldFollowDataView;
|
||||
|
||||
// !!!!
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (BOOL)indentationMarkerFollowsDataView
|
||||
{
|
||||
return _indentationMarkerFollowsDataView;
|
||||
}
|
||||
|
||||
- (id)parentForItem:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return nil;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return nil;
|
||||
|
||||
return itemInfo.parent;
|
||||
}
|
||||
|
||||
- (CGRect)frameOfOutlineDataViewAtColumn:(CPInteger)aColumn row:(CPInteger)aRow
|
||||
{
|
||||
var frame = [super frameOfDataViewAtColumn:aColumn row:aRow],
|
||||
indentationWidth = ([self levelForRow:aRow] + 1) * [self indentationPerLevel];
|
||||
|
||||
frame.origin.x += indentationWidth;
|
||||
frame.size.width -= indentationWidth;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
{
|
||||
if (_outlineViewDelegate === aDelegate)
|
||||
return;
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
if (_outlineViewDelegate)
|
||||
{
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewColumnDidMove:)])
|
||||
[defaultCenter
|
||||
removeObserver:_outlineViewDelegate
|
||||
name:CPOutlineViewColumnDidMoveNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewColumnDidResize:)])
|
||||
[defaultCenter
|
||||
removeObserver:_outlineViewDelegate
|
||||
name:CPOutlineViewColumnDidResizeNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewSelectionDidChange:)])
|
||||
[defaultCenter
|
||||
removeObserver:_outlineViewDelegate
|
||||
name:CPOutlineViewSelectionDidChangeNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewSelectionIsChanging:)])
|
||||
[defaultCenter
|
||||
removeObserver:_outlineViewDelegate
|
||||
name:CPOutlineViewSelectionIsChangingNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
_outlineViewDelegate = aDelegate;/*
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(selectionShouldChangeInTableView:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_selectionShouldChangeInTableView_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:dataViewForTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_dataViewForTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:didClickTableColumn:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_didClickTableColumn_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:didDragTableColumn:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_didDragTableColumn_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:heightOfRow:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_heightOfRow_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:isGroupRow:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_isGroupRow_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:mouseDownInHeaderOfTableColumn:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_mouseDownInHeaderOfTableColumn_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:nextTypeSelectMatchFromRow:toRow:forString:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_nextTypeSelectMatchFromRow_toRow_forString_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:selectionIndexesForProposedSelection:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldEditTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldEditTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldSelectRow:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldSelectRow_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldSelectTableColumn:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldSelectTableColumn_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldShowViewExpansionForTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldShowViewExpansionForTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldTrackView:forTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldTrackView_forTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldTypeSelectForEvent:withCurrentSearchString:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldTypeSelectForEvent_withCurrentSearchString_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:toolTipForView:rect:tableColumn:row:mouseLocation:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_toolTipForView_rect_tableColumn_row_mouseLocation_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:typeSelectStringForTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_typeSelectStringForTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:willDisplayView:forTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_;
|
||||
*/
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewColumnDidMove:)])
|
||||
[defaultCenter
|
||||
addObserver:_outlineViewDelegate
|
||||
selector:@selector(outlineViewColumnDidMove:)
|
||||
name:CPOutlineViewColumnDidMoveNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewColumnDidResize:)])
|
||||
[defaultCenter
|
||||
addObserver:_outlineViewDelegate
|
||||
selector:@selector(outlineViewColumnDidMove:)
|
||||
name:CPOutlineViewColumnDidResizeNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewSelectionDidChange:)])
|
||||
[defaultCenter
|
||||
addObserver:_outlineViewDelegate
|
||||
selector:@selector(outlineViewSelectionDidChange:)
|
||||
name:CPOutlineViewSelectionDidChangeNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewSelectionIsChanging:)])
|
||||
[defaultCenter
|
||||
addObserver:_outlineViewDelegate
|
||||
selector:@selector(outlineViewSelectionIsChanging:)
|
||||
name:CPOutlineViewSelectionIsChangingNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (id)delegate
|
||||
{
|
||||
return _outlineViewDelegate;
|
||||
}
|
||||
|
||||
- (void)setDisclosureControlPrototype:(CPControl)aControl
|
||||
{
|
||||
_disclosureControlPrototype = aControl;
|
||||
_disclosureControlData = nil;
|
||||
_disclosureControlQueue = [];
|
||||
|
||||
// fIXME: reall?
|
||||
[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]];
|
||||
[self reloadItem:nil reloadChildren:YES];
|
||||
}
|
||||
|
||||
- (CGRect)frameOfDataViewAtColumn:(CPInteger)aColumn row:(CPInteger)aRow
|
||||
{
|
||||
var tableColumn = [self tableColumns][aColumn];
|
||||
|
||||
if (tableColumn === _outlineTableColumn)
|
||||
return [self frameOfOutlineDataViewAtColumn:aColumn row:aRow];
|
||||
|
||||
return [super frameOfDataViewAtColumn:aColumn row:aRow];
|
||||
}
|
||||
|
||||
- (void)_loadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
|
||||
{
|
||||
[super _loadDataViewsInRows:rows columns:columns];
|
||||
|
||||
var outlineColumn = [[self tableColumns] indexOfObjectIdenticalTo:[self outlineTableColumn]];
|
||||
|
||||
if (![columns containsIndex:outlineColumn])
|
||||
return;
|
||||
|
||||
var rowArray = [];
|
||||
|
||||
[rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
|
||||
|
||||
var rowIndex = 0,
|
||||
rowsCount = rowArray.length;
|
||||
|
||||
for (; rowIndex < rowsCount; ++rowIndex)
|
||||
{
|
||||
var row = rowArray[rowIndex],
|
||||
item = _itemsForRows[row],
|
||||
isExpandable = [self isExpandable:item];
|
||||
|
||||
if (!isExpandable)
|
||||
continue;
|
||||
|
||||
var control = [self _dequeueDisclosureControl],
|
||||
frame = [control frame],
|
||||
dataViewFrame = [self frameOfDataViewAtColumn:outlineColumn row:row];
|
||||
|
||||
frame.origin.x = _indentationMarkerFollowsDataView ? _CGRectGetMinX(dataViewFrame) - _CGRectGetWidth(frame) : 0.0;
|
||||
frame.origin.y = _CGRectGetMinY(dataViewFrame);
|
||||
frame.size.height = _CGRectGetHeight(dataViewFrame);
|
||||
// FIXME: center instead?
|
||||
//frame.origin.y = _CGRectGetMidY(dataViewFrame) - _CGRectGetHeight(frame) / 2.0;
|
||||
|
||||
_disclosureControlsForRows[row] = control;
|
||||
|
||||
[control setState:[self isItemExpanded:item] ? CPOnState : CPOffState];
|
||||
[control setFrame:frame];
|
||||
|
||||
[self addSubview:control];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_unloadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
|
||||
{
|
||||
[super _unloadDataViewsInRows:rows columns:columns];
|
||||
|
||||
var outlineColumn = [[self tableColumns] indexOfObjectIdenticalTo:[self outlineTableColumn]];
|
||||
|
||||
if (![columns containsIndex:outlineColumn])
|
||||
return;
|
||||
|
||||
var rowArray = [];
|
||||
|
||||
[rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
|
||||
|
||||
var rowIndex = 0,
|
||||
rowsCount = rowArray.length;
|
||||
|
||||
for (; rowIndex < rowsCount; ++rowIndex)
|
||||
{
|
||||
var row = rowArray[rowIndex],
|
||||
control = _disclosureControlsForRows[row];
|
||||
|
||||
if (!control)
|
||||
continue;
|
||||
|
||||
[control removeFromSuperview];
|
||||
|
||||
[self _enqueueDisclosureControl:control];
|
||||
|
||||
_disclosureControlsForRows[row] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_toggleFromDisclosureControl:(CPControl)aControl
|
||||
{
|
||||
var controlFrame = [aControl frame],
|
||||
item = [self itemAtRow:[self rowAtPoint:_CGPointMake(_CGRectGetMinX(controlFrame), _CGRectGetMidY(controlFrame))]];
|
||||
|
||||
if ([self isItemExpanded:item])
|
||||
[self collapseItem:item];
|
||||
|
||||
else
|
||||
[self expandItem:item];
|
||||
}
|
||||
|
||||
- (void)_enqueueDisclosureControl:(CPControl)aControl
|
||||
{
|
||||
_disclosureControlQueue.push(aControl);
|
||||
}
|
||||
|
||||
- (CPControl)_dequeueDisclosureControl
|
||||
{
|
||||
if (_disclosureControlQueue.length)
|
||||
return _disclosureControlQueue.pop();
|
||||
|
||||
if (!_disclosureControlData)
|
||||
if (!_disclosureControlPrototype)
|
||||
return nil;
|
||||
else
|
||||
_disclosureControlData = [CPKeyedArchiver archivedDataWithRootObject:_disclosureControlPrototype];
|
||||
|
||||
var disclosureControl = [CPKeyedUnarchiver unarchiveObjectWithData:_disclosureControlData];
|
||||
|
||||
[disclosureControl setTarget:self];
|
||||
[disclosureControl setAction:@selector(_toggleFromDisclosureControl:)];
|
||||
|
||||
return disclosureControl;
|
||||
}
|
||||
|
||||
- (void)_noteSelectionIsChanging
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPOutlineViewSelectionIsChangingNotification
|
||||
object:self
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
- (void)_noteSelectionDidChange
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPOutlineViewSelectionDidChangeNotification
|
||||
object:self
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/* @ignore */
|
||||
@implementation CPOutlineView (CPTableDataSource)
|
||||
|
||||
/*
|
||||
FIXME
|
||||
*/
|
||||
/* @ignore */
|
||||
- (void)numberOfRowsInTableView:(CPTableView)aTableView
|
||||
var _reloadItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anItem)
|
||||
{
|
||||
return _numberOfVisibleItems;
|
||||
if (!anItem)
|
||||
return;
|
||||
|
||||
// Get the existing info if it exists.
|
||||
var itemInfosForItems = anOutlineView._itemInfosForItems,
|
||||
dataSource = anOutlineView._outlineViewDataSource,
|
||||
itemUID = [anItem UID],
|
||||
itemInfo = itemInfosForItems[itemUID];
|
||||
|
||||
// If we're not in the tree, then just bail.
|
||||
if (!itemInfo)
|
||||
return [];
|
||||
|
||||
// See if the item itself can be swapped out.
|
||||
var parent = itemInfo.parent,
|
||||
parentItemInfo = parent ? itemInfosForItems[[parent UID]] : anOutlineView._rootItemInfo,
|
||||
parentChildren = parentItemInfo.children,
|
||||
index = [parentChildren indexOfObjectIdenticalTo:anItem],
|
||||
newItem = [dataSource outlineView:anOutlineView child:index ofItem:parent];
|
||||
|
||||
if (anItem !== newItem)
|
||||
{
|
||||
itemInfosForItems[[anItem UID]] = nil;
|
||||
itemInfosForItems[[newItem UID]] = itemInfo;
|
||||
|
||||
parentChildren[index] = newItem;
|
||||
anOutlineView._itemsForRows[itemInfo.row] = newItem;
|
||||
}
|
||||
|
||||
itemInfo.isExpandable = [dataSource outlineView:anOutlineView isItemExpandable:newItem];
|
||||
itemInfo.isExpanded = itemInfo.isExpandable && itemInfo.isExpanded;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)aTableColumn row:(int)aRowIndex
|
||||
var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anItem, /*BOOL*/ isIntermediate)
|
||||
{
|
||||
return [_outlineDataSource outlineView:self objectValueForTableColumn:aTableColumn byItem:_itemsByRow[aRowIndex]];
|
||||
var itemInfosForItems = anOutlineView._itemInfosForItems,
|
||||
dataSource = anOutlineView._outlineViewDataSource;
|
||||
|
||||
if (!anItem)
|
||||
var itemInfo = anOutlineView._rootItemInfo;
|
||||
|
||||
else
|
||||
{
|
||||
// Get the existing info if it exists.
|
||||
var itemUID = [anItem UID],
|
||||
itemInfo = itemInfosForItems[itemUID];
|
||||
|
||||
// If we're not in the tree, then just bail.
|
||||
if (!itemInfo)
|
||||
return [];
|
||||
|
||||
itemInfo.isExpandable = [dataSource outlineView:anOutlineView isItemExpandable:anItem];
|
||||
|
||||
// If we were previously expanded, but now no longer expandable, "de-expand".
|
||||
// NOTE: we are *not* collapsing, thus no notification is posted.
|
||||
if (!itemInfo.isExpandable && itemInfo.isExpanded)
|
||||
{
|
||||
itemInfo.isExpanded = NO;
|
||||
itemInfo.children = [];
|
||||
}
|
||||
}
|
||||
|
||||
// The root item does not count as a descendant.
|
||||
var weight = itemInfo.weight,
|
||||
descendants = anItem ? [anItem] : [];
|
||||
|
||||
if (itemInfo.isExpanded && (!(anOutlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_shouldDeferDisplayingChildrenOfItem_) ||
|
||||
![dataSource outlineView:anOutlineView shouldDeferDisplayingChildrenOfItem:anItem]))
|
||||
{
|
||||
var index = 0,
|
||||
count = [dataSource outlineView:anOutlineView numberOfChildrenOfItem:anItem],
|
||||
level = itemInfo.level + 1;
|
||||
|
||||
itemInfo.children = [];
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var childItem = [dataSource outlineView:anOutlineView child:index ofItem:anItem],
|
||||
childItemInfo = itemInfosForItems[[childItem UID]];
|
||||
|
||||
if (!childItemInfo)
|
||||
{
|
||||
childItemInfo = { isExpanded:NO, isExpandable:NO, children:[], weight:1 };
|
||||
itemInfosForItems[[childItem UID]] = childItemInfo;
|
||||
}
|
||||
|
||||
itemInfo.children[index] = childItem;
|
||||
|
||||
var childDescendants = _loadItemInfoForItem(anOutlineView, childItem, YES);
|
||||
|
||||
childItemInfo.parent = anItem;
|
||||
childItemInfo.level = level;
|
||||
descendants = descendants.concat(childDescendants);
|
||||
}
|
||||
}
|
||||
|
||||
itemInfo.weight = descendants.length;
|
||||
|
||||
if (!isIntermediate)
|
||||
{
|
||||
// row = -1 is the root item, so just go to row 0 since it is ignored.
|
||||
var index = MAX(itemInfo.row, 0),
|
||||
itemsForRows = anOutlineView._itemsForRows;
|
||||
|
||||
descendants.unshift(index, weight);
|
||||
|
||||
itemsForRows.splice.apply(itemsForRows, descendants);
|
||||
|
||||
var count = itemsForRows.length;
|
||||
|
||||
for (; index < count; ++index)
|
||||
itemInfosForItems[[itemsForRows[index] UID]].row = index;
|
||||
|
||||
var deltaWeight = itemInfo.weight - weight;
|
||||
|
||||
if (deltaWeight !== 0)
|
||||
{
|
||||
var parent = itemInfo.parent;
|
||||
|
||||
while (parent)
|
||||
{
|
||||
var parentItemInfo = itemInfosForItems[[parent UID]];
|
||||
|
||||
parentItemInfo.weight += deltaWeight;
|
||||
parent = parentItemInfo.parent;
|
||||
}
|
||||
|
||||
if (anItem)
|
||||
anOutlineView._rootItemInfo.weight += deltaWeight;
|
||||
}
|
||||
}
|
||||
|
||||
return descendants;
|
||||
}
|
||||
|
||||
@end
|
||||
@implementation _CPOutlineViewTableViewDataSource : CPObject
|
||||
{
|
||||
CPObject _outlineView;
|
||||
}
|
||||
|
||||
- (id)initWithOutlineView:(CPOutlineView)anOutlineView
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_outlineView = anOutlineView;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPInteger)numberOfRowsInTableView:(CPTableView)anOutlineView
|
||||
{
|
||||
return _outlineView._itemsForRows.length;
|
||||
}
|
||||
|
||||
- (id)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRow
|
||||
{
|
||||
return [_outlineView._outlineViewDataSource outlineView:_outlineView objectValueForTableColumn:aTableColumn byItem:_outlineView._itemsForRows[aRow]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPOutlineViewTableViewDelegate : CPObject
|
||||
{
|
||||
CPOutlineView _outlineView;
|
||||
}
|
||||
|
||||
- (id)initWithOutlineView:(CPOutlineView)anOutlineView
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_outlineView = anOutlineView;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPDisclosureButton : CPButton
|
||||
{
|
||||
float _angle;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
[self setBordered:NO];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setState:(CPState)aState
|
||||
{
|
||||
[super setState:aState];
|
||||
|
||||
if ([self state] === CPOnState)
|
||||
_angle = 0.0;
|
||||
|
||||
else
|
||||
_angle = -PI_2;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextTranslateCTM(context, _CGRectGetWidth(bounds) / 2.0, _CGRectGetHeight(bounds) / 2.0);
|
||||
CGContextRotateCTM(context, _angle);
|
||||
CGContextTranslateCTM(context, -_CGRectGetWidth(bounds) / 2.0, -_CGRectGetHeight(bounds) / 2.0);
|
||||
|
||||
// Center, but crisp.
|
||||
CGContextTranslateCTM(context, FLOOR((_CGRectGetWidth(bounds) - 9.0) / 2.0), FLOOR((_CGRectGetHeight(bounds) - 8.0) / 2.0));
|
||||
|
||||
CGContextMoveToPoint(context, 0.0, 0.0);
|
||||
CGContextAddLineToPoint(context, 9.0, 0.0);
|
||||
CGContextAddLineToPoint(context, 4.5, 8.0);
|
||||
CGContextAddLineToPoint(context, 0.0, 0.0);
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetFillColor(context, ([self themeState] & CPThemeState("highlighted")) ? [CPColor blackColor] : [CPColor grayColor]);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -53,7 +53,7 @@ CPCancelButton = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the receiver is a floating panel (like a palette).
|
||||
Returns \c YES if the receiver is a floating panel (like a palette).
|
||||
*/
|
||||
- (BOOL)isFloatingPanel
|
||||
{
|
||||
@@ -61,8 +61,8 @@ CPCancelButton = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the receiver to be a floating panel. <code>YES</code>
|
||||
makes the window a floating panel. <code>NO</code> makes it a normal window.
|
||||
Sets the receiver to be a floating panel. \c YES
|
||||
makes the window a floating panel. \c NO makes it a normal window.
|
||||
@param isFloatingPanel specifies whether to make it floating
|
||||
*/
|
||||
- (void)setFloatingPanel:(BOOL)isFloatingPanel
|
||||
@@ -71,8 +71,8 @@ CPCancelButton = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the window only becomes key
|
||||
if needed. <code>NO</code> means it behaves just like other windows.
|
||||
Returns \c YES if the window only becomes key
|
||||
if needed. \c NO means it behaves just like other windows.
|
||||
*/
|
||||
- (BOOL)becomesKeyOnlyIfNeeded
|
||||
{
|
||||
@@ -81,7 +81,7 @@ CPCancelButton = 0;
|
||||
|
||||
/*!
|
||||
Sets whether the the window becomes key only if needed.
|
||||
@param shouldBecomeKeyOnlyIfNeeded <code>YES</code> makes the window become key only if needed
|
||||
@param shouldBecomeKeyOnlyIfNeeded \c YES makes the window become key only if needed
|
||||
*/
|
||||
- (void)setBecomesKeyOnlyIfNeeded:(BOOL)shouldBecomeKeyOnlyIfNeeded
|
||||
{
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPPropertyListSerialization.j>
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
|
||||
CPGeneralPboard = @"CPGeneralPboard";
|
||||
CPFontPboard = @"CPFontPboard";
|
||||
@@ -38,8 +40,12 @@ CPFontPboardType = @"CPFontPboardType";
|
||||
CPHTMLPboardType = @"CPHTMLPboardType";
|
||||
CPStringPboardType = @"CPStringPboardType";
|
||||
CPURLPboardType = @"CPURLPboardType";
|
||||
CPImagesPboardType = @"CPImagesPboardType";
|
||||
CPVideosPboardType = @"CPVideosPboardType";
|
||||
|
||||
// Deprecated
|
||||
CPImagePboardType = @"CPImagePboardType";
|
||||
CPVideoPboardType = @"CPVideoPboardType";
|
||||
|
||||
|
||||
var CPPasteboards = nil;
|
||||
|
||||
@@ -169,7 +175,7 @@ var CPPasteboards = nil;
|
||||
Sets the pasteboard data for the specified type
|
||||
@param aData the data
|
||||
@param aType the data type being set
|
||||
@return <code>YES</code> if the data was successfully written to the pasteboard
|
||||
@return \c YES if the data was successfully written to the pasteboard
|
||||
*/
|
||||
- (BOOL)setData:(CPData)aData forType:(CPString)aType
|
||||
{
|
||||
@@ -182,7 +188,7 @@ var CPPasteboards = nil;
|
||||
Writes the specified property list as data for the specified type
|
||||
@param aPropertyList the property list to write
|
||||
@param aType the data type
|
||||
@return <code>YES</code> if the property list was successfully written to the pasteboard
|
||||
@return \c YES if the property list was successfully written to the pasteboard
|
||||
*/
|
||||
- (BOOL)setPropertyList:(id)aPropertyList forType:(CPString)aType
|
||||
{
|
||||
@@ -193,7 +199,7 @@ var CPPasteboards = nil;
|
||||
Sets the specified string as data for the specified type
|
||||
@param aString the string to write
|
||||
@param aType the data type
|
||||
@return <code>YES</code> if the string was successfully written to the pasteboard
|
||||
@return \c YES if the string was successfully written to the pasteboard
|
||||
*/
|
||||
- (void)setString:(CPString)aString forType:(CPString)aType
|
||||
{
|
||||
@@ -205,11 +211,11 @@ var CPPasteboards = nil;
|
||||
Checks the pasteboard's types for a match with the types listen in the specified array. The array should
|
||||
be ordered by the requestor's most preferred data type first.
|
||||
@param anArray an array of requested types ordered by preference
|
||||
@return the highest match with the pasteboard's supported types or <code>nil</code> if no match was found
|
||||
@return the highest match with the pasteboard's supported types or \c nil if no match was found
|
||||
*/
|
||||
- (CPString)availableTypeFromArray:(CPArray)anArray
|
||||
{
|
||||
return [_types firstObjectCommonWithArray:anArray];
|
||||
return [[self types] firstObjectCommonWithArray:anArray];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -232,7 +238,7 @@ var CPPasteboards = nil;
|
||||
/*!
|
||||
Returns the pasteboard data for the specified data type
|
||||
@param aType the requested data type
|
||||
@return the requested data or <code>nil</code> if the data doesn't exist
|
||||
@return the requested data or \c nil if the data doesn't exist
|
||||
*/
|
||||
- (CPData)dataForType:(CPString)aType
|
||||
{
|
||||
@@ -258,7 +264,7 @@ var CPPasteboards = nil;
|
||||
/*!
|
||||
Returns the property list for the specified data type
|
||||
@param aType the requested data type
|
||||
@return the property list or <code>nil</code> if the list was not found
|
||||
@return the property list or \c nil if the list was not found
|
||||
*/
|
||||
- (id)propertyListForType:(CPString)aType
|
||||
{
|
||||
@@ -273,7 +279,7 @@ var CPPasteboards = nil;
|
||||
/*!
|
||||
Returns the string for the specified data type
|
||||
@param aType the requested data type
|
||||
@return the string or <code>nil</code> if the string was not found
|
||||
@return the string or \c nil if the string was not found
|
||||
*/
|
||||
- (CPString)stringForType:(CPString)aType
|
||||
{
|
||||
@@ -300,3 +306,70 @@ var CPPasteboards = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
var DOMDataTransferPasteboard = nil;
|
||||
|
||||
@implementation _CPDOMDataTransferPasteboard : CPPasteboard
|
||||
{
|
||||
DataTransfer _dataTransfer;
|
||||
}
|
||||
|
||||
+ (_CPDOMDataTransferPasteboard)DOMDataTransferPasteboard
|
||||
{
|
||||
if (!DOMDataTransferPasteboard)
|
||||
DOMDataTransferPasteboard = [[_CPDOMDataTransferPasteboard alloc] init];
|
||||
|
||||
return DOMDataTransferPasteboard;
|
||||
}
|
||||
|
||||
- (void)_setDataTransfer:(DataTransfer)aDataTransfer
|
||||
{
|
||||
_dataTransfer = aDataTransfer;
|
||||
}
|
||||
|
||||
- (void)_setPasteboard:(CPPasteboard)aPasteboard
|
||||
{
|
||||
_dataTransfer.clearData();
|
||||
|
||||
var types = [aPasteboard types],
|
||||
count = types.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var type = types[count];
|
||||
|
||||
if (type === CPStringPboardType)
|
||||
_dataTransfer.setData(type, [aPasteboard stringForType:type]);
|
||||
else
|
||||
_dataTransfer.setData(type, [[aPasteboard dataForType:type] string]);
|
||||
}
|
||||
}
|
||||
|
||||
- (CPArray)types
|
||||
{
|
||||
return Array.prototype.slice.apply(_dataTransfer.types);
|
||||
}
|
||||
|
||||
- (CPData)dataForType:(CPString)aType
|
||||
{
|
||||
var dataString = _dataTransfer.getData(aType);
|
||||
|
||||
if (aType === CPStringPboardType)
|
||||
return [CPData dataFromPropertyList:dataString format:kCFPropertyList280NorthFormat_v1_0 errorDescription:0];
|
||||
|
||||
return [CPData dataWithString:dataString];
|
||||
}
|
||||
|
||||
- (id)propertyListForType:(CPString)aType
|
||||
{
|
||||
if (aType === CPStringPboardType)
|
||||
return _dataTransfer.getData(aType);
|
||||
|
||||
return [CPPropertyListSerialization propertyListFromData:[self dataForType:aType] format:CPPropertyListUnknownFormat errorDescription:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@@ -54,7 +54,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
/*!
|
||||
Initializes the pop-up button to the specified size.
|
||||
@param aFrame the size for the button
|
||||
@param shouldPullDown <code>YES</code> makes this a pull-down menu, <code>NO</code> makes it a pop-up menu.
|
||||
@param shouldPullDown \c YES makes this a pull-down menu, \c NO makes it a pop-up menu.
|
||||
@return the initialized pop-up button
|
||||
*/
|
||||
- (id)initWithFrame:(CGRect)aFrame pullsDown:(BOOL)shouldPullDown
|
||||
@@ -87,8 +87,8 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
/*!
|
||||
Specifies whether the object is a pull-down or a pop-up menu.
|
||||
@param shouldPullDown <code>YES</code> makes the pop-up button
|
||||
a pull-down menu. <code>NO</code> makes it a pop-up menu.
|
||||
@param shouldPullDown \c YES makes the pop-up button
|
||||
a pull-down menu. \c NO makes it a pop-up menu.
|
||||
*/
|
||||
- (void)setPullsDown:(BOOL)shouldPullDown
|
||||
{
|
||||
@@ -111,7 +111,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the button is a pull-down menu. <code>NO</code> if the button is a pop-up menu.
|
||||
Returns \c YES if the button is a pull-down menu. \c NO if the button is a pop-up menu.
|
||||
*/
|
||||
- (BOOL)pullsDown
|
||||
{
|
||||
@@ -200,18 +200,18 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
// Getting the User's Selection
|
||||
/*!
|
||||
Returns the selected item or <code>nil</code> if no item is selected.
|
||||
Returns the selected item or \c nil if no item is selected.
|
||||
*/
|
||||
- (CPMenuItem)selectedItem
|
||||
{
|
||||
if (_selectedIndex < 0)
|
||||
if (_selectedIndex < 0 || _selectedIndex > [self numberOfItems] - 1)
|
||||
return nil;
|
||||
|
||||
return [_menu itemAtIndex:_selectedIndex];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the title of the selected item or <code>nil</code> if no item is selected.
|
||||
Returns the title of the selected item or \c nil if no item is selected.
|
||||
*/
|
||||
- (CPString)titleOfSelectedItem
|
||||
{
|
||||
@@ -372,7 +372,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the item at the specified index or <code>nil</code> if the item does not exist.
|
||||
Returns the item at the specified index or \c nil if the item does not exist.
|
||||
@param anIndex the index of the item to obtain
|
||||
*/
|
||||
- (CPMenuItem)itemAtIndex:(unsigned)anIndex
|
||||
@@ -381,7 +381,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the title of the item at the specified index or <code>nil</code> if no item exists.
|
||||
Returns the title of the item at the specified index or \c nil if no item exists.
|
||||
@param anIndex the index of the item
|
||||
*/
|
||||
- (CPString)itemTitleAtIndex:(unsigned)anIndex
|
||||
@@ -628,13 +628,15 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
var numberOfItems = [self numberOfItems];
|
||||
|
||||
if (numberOfItems <= _selectedIndex)
|
||||
if (numberOfItems <= _selectedIndex && numberOfItems > 0)
|
||||
[self selectItemAtIndex:numberOfItems - 1];
|
||||
else
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled])
|
||||
if (![self isEnabled] || ![self numberOfItems])
|
||||
return;
|
||||
|
||||
[self highlight:YES];
|
||||
@@ -648,7 +650,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
// Pull Down Menus show up directly below their buttons.
|
||||
if ([self pullsDown])
|
||||
var menuOrigin = [theWindow convertBaseToBridge:[self convertPoint:CGPointMake(0.0, CGRectGetMaxY([self bounds])) toView:nil]];
|
||||
var menuOrigin = [theWindow convertBaseToGlobal:[self convertPoint:CGPointMake(0.0, CGRectGetMaxY([self bounds])) toView:nil]];
|
||||
|
||||
// Pop Up Menus attempt to show up "on top" of the selected item.
|
||||
else
|
||||
@@ -659,21 +661,21 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
// 2. Move LEFT by whatever indentation we have (offsetWidths, aka, window margin, item margin, etc).
|
||||
// 3. MOVE UP by the difference in sizes of the content and menu item, this will only work if the content is vertically centered.
|
||||
var contentRect = [self convertRect:[self contentRectForBounds:[self bounds]] toView:nil],
|
||||
menuOrigin = [theWindow convertBaseToBridge:contentRect.origin],
|
||||
menuOrigin = [theWindow convertBaseToGlobal:contentRect.origin],
|
||||
menuItemRect = [menuWindow rectForItemAtIndex:_selectedIndex];
|
||||
|
||||
menuOrigin.x -= CGRectGetMinX(menuItemRect) + [menuWindow overlapOffsetWidth] + [[[menu itemAtIndex:_selectedIndex] _menuItemView] overlapOffsetWidth];
|
||||
menuOrigin.y -= CGRectGetMinY(menuItemRect) + (CGRectGetHeight(menuItemRect) - CGRectGetHeight(contentRect)) / 2.0;
|
||||
}
|
||||
|
||||
|
||||
[menuWindow setFrameOrigin:menuOrigin];
|
||||
|
||||
|
||||
var menuMaxX = CGRectGetMaxX([menuWindow frame]),
|
||||
buttonMaxX = [theWindow convertBaseToBridge:CGPointMake(CGRectGetMaxX([self convertRect:[self bounds] toView:nil]), 0.0)].x;
|
||||
|
||||
buttonMaxX = [theWindow convertBaseToGlobal:CGPointMake(CGRectGetMaxX([self convertRect:[self bounds] toView:nil]), 0.0)].x;
|
||||
|
||||
if (menuMaxX < buttonMaxX)
|
||||
[menuWindow setMinWidth:CGRectGetWidth([menuWindow frame]) + buttonMaxX - menuMaxX - ([self pullsDown] ? 0.0 : VISIBLE_MARGIN)];
|
||||
|
||||
|
||||
[menuWindow orderFront:self];
|
||||
[menuWindow beginTrackingWithEvent:anEvent sessionDelegate:self didEndSelector:@selector(menuWindowDidFinishTracking:highlightedItem:)];
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Always returns <code>NO</code>. Cappuccino does not have multiple threads.
|
||||
Always returns \c NO. Cappuccino does not have multiple threads.
|
||||
*/
|
||||
- (BOOL)usesThreadedAnimation
|
||||
{
|
||||
@@ -329,7 +329,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
|
||||
/*!
|
||||
Specifies whether this progress indicator should be indeterminate or display progress based on it's max and min.
|
||||
@param isDeterminate <code>YES</code> makes the indicator indeterminate
|
||||
@param isDeterminate \c YES makes the indicator indeterminate
|
||||
*/
|
||||
- (void)setIndeterminate:(BOOL)isIndeterminate
|
||||
{
|
||||
@@ -342,7 +342,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the progress bar is indeterminate.
|
||||
Returns \c YES if the progress bar is indeterminate.
|
||||
*/
|
||||
- (BOOL)isIndeterminate
|
||||
{
|
||||
@@ -377,9 +377,9 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the indicator should be displayed when it isn't animating. By default this is <code>YES</code> if the style
|
||||
is CPProgressIndicatorBarStyle, and <code>NO</code> if it's CPProgressIndicatorSpinningStyle.
|
||||
@param isDisplayedWhenStopped <code>YES</code> means the indicator will be displayed when it's not animating.
|
||||
Sets whether the indicator should be displayed when it isn't animating. By default this is \c YES if the style
|
||||
is CPProgressIndicatorBarStyle, and \c NO if it's CPProgressIndicatorSpinningStyle.
|
||||
@param isDisplayedWhenStopped \c YES means the indicator will be displayed when it's not animating.
|
||||
*/
|
||||
- (void)setDisplayedWhenStopped:(BOOL)isDisplayedWhenStopped
|
||||
{
|
||||
@@ -394,7 +394,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the progress bar is displayed when not animating.
|
||||
Returns \c YES if the progress bar is displayed when not animating.
|
||||
*/
|
||||
- (BOOL)isDisplayedWhenStopped
|
||||
{
|
||||
|
||||
@@ -46,7 +46,7 @@ CPDownArrowKeyCode = 40;
|
||||
|
||||
// Changing the first responder
|
||||
/*!
|
||||
Returns <code>YES</code> if the receiver is able to become the first responder. <code>NO</code> otherwise.
|
||||
Returns \c YES if the receiver is able to become the first responder. \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
@@ -55,8 +55,8 @@ CPDownArrowKeyCode = 40;
|
||||
|
||||
/*!
|
||||
Notifies the receiver that it will become the first responder. The receiver can reject first
|
||||
responder if it returns <code>NO</code>. The default implementation always returns <code>YES</code>.
|
||||
@return <code>YES</code> if the receiver accepts first responder status.
|
||||
responder if it returns \c NO. The default implementation always returns \c YES.
|
||||
@return \c YES if the receiver accepts first responder status.
|
||||
*/
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
@@ -65,7 +65,7 @@ CPDownArrowKeyCode = 40;
|
||||
|
||||
/*!
|
||||
Notifies the receiver that it has been asked to give up first responder status.
|
||||
@return <code>YES</code> if the receiver is willing to give up first responder status.
|
||||
@return \c YES if the receiver is willing to give up first responder status.
|
||||
*/
|
||||
- (BOOL)resignFirstResponder
|
||||
{
|
||||
@@ -96,36 +96,38 @@ CPDownArrowKeyCode = 40;
|
||||
*/
|
||||
- (void)interpretKeyEvents:(CPArray)events
|
||||
{
|
||||
var event,
|
||||
index = 0;
|
||||
|
||||
while(event = events[index++])
|
||||
var index = 0,
|
||||
count = [events count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var event = events[index];
|
||||
|
||||
switch([event keyCode])
|
||||
{
|
||||
case CPLeftArrowKeyCode: [self moveBackward:self];
|
||||
case CPLeftArrowKeyCode: [self doCommandBySelector:@selector(moveLeft:)];
|
||||
break;
|
||||
case CPRightArrowKeyCode: [self moveForward:self];
|
||||
case CPRightArrowKeyCode: [self doCommandBySelector:@selector(moveRight:)];
|
||||
break;
|
||||
case CPUpArrowKeyCode: [self moveUp:self];
|
||||
case CPUpArrowKeyCode: [self doCommandBySelector:@selector(moveUp:)];
|
||||
break;
|
||||
case CPDownArrowKeyCode: [self moveDown:self];
|
||||
case CPDownArrowKeyCode: [self doCommandBySelector:@selector(moveDown:)];
|
||||
break;
|
||||
case CPDeleteKeyCode: [self deleteBackward:self];
|
||||
case CPDeleteKeyCode: [self doCommandBySelector:@selector(deleteBackward:)];
|
||||
break;
|
||||
case CPReturnKeyCode:
|
||||
case 3: [self insertLineBreak:self];
|
||||
case 3: [self doCommandBySelector:@selector(insertLineBreak:)];
|
||||
break;
|
||||
|
||||
case CPEscapeKeyCode: [self cancel:self];
|
||||
case CPEscapeKeyCode: [self doCommandBySelector:@selector(cancel:)];
|
||||
break;
|
||||
|
||||
case CPTabKeyCode: var shift = [event modifierFlags] & CPShiftKeyMask;
|
||||
|
||||
if (!shift)
|
||||
[self insertTab:self];
|
||||
[self doCommandBySelector:@selector(insertTab:)];
|
||||
else
|
||||
[self insertBackTab:self];
|
||||
[self doCommandBySelector:@selector(insertBackTab:)];
|
||||
|
||||
break;
|
||||
|
||||
@@ -214,9 +216,9 @@ CPDownArrowKeyCode = 40;
|
||||
|
||||
/*
|
||||
FIXME This description is bad.
|
||||
Based on <code>anEvent</code>, the receiver should simulate the event.
|
||||
Based on \c anEvent, the receiver should simulate the event.
|
||||
@param anEvent the event to simulate
|
||||
@return <code>YES</code> if the event receiver simulated the event
|
||||
@return \c YES if the event receiver simulated the event
|
||||
*/
|
||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||
{
|
||||
@@ -224,14 +226,6 @@ CPDownArrowKeyCode = 40;
|
||||
}
|
||||
|
||||
// Action Methods
|
||||
/*!
|
||||
Deletes one character backward, or the selection if anything is selected.
|
||||
@param aSender the object requesting this
|
||||
*/
|
||||
- (void)deleteBackward:(id)aSender
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Insert a line break at the caret position or selection.
|
||||
@param aSender the object requesting this
|
||||
@@ -273,12 +267,12 @@ CPDownArrowKeyCode = 40;
|
||||
// Dispatch methods
|
||||
/*!
|
||||
The receiver will attempt to perform the command,
|
||||
if it responds to it. If not, the <code>nextResponder</code> will be called to do it.
|
||||
if it responds to it. If not, the \c -nextResponder will be called to do it.
|
||||
@param aSelector the command to attempt
|
||||
*/
|
||||
- (void)doCommandBySelector:(SEL)aSelector
|
||||
{
|
||||
if([self respondsToSelector:aSelector])
|
||||
if ([self respondsToSelector:aSelector])
|
||||
[self performSelector:aSelector];
|
||||
else
|
||||
[_nextResponder doCommandBySelector:aSelector];
|
||||
@@ -288,7 +282,7 @@ CPDownArrowKeyCode = 40;
|
||||
The receiver will attempt to perform the command, or pass it on to the next responder if it doesn't respond to it.
|
||||
@param aSelector the command to perform
|
||||
@param anObject the argument to the method
|
||||
@return <code>YES</code> if the receiver was able to perform the command, or a responder down the chain was
|
||||
@return \c YES if the receiver was able to perform the command, or a responder down the chain was
|
||||
able to perform the command.
|
||||
*/
|
||||
- (BOOL)tryToPerform:(SEL)aSelector with:(id)anObject
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
#import "CoreGraphics/CGGeometry.h"
|
||||
|
||||
@implementation CPScreen : CPObject
|
||||
{
|
||||
}
|
||||
|
||||
- (CGRect)visibleFrame
|
||||
{
|
||||
return _CGRectMake(window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -38,7 +38,9 @@
|
||||
@implementation CPScrollView : CPView
|
||||
{
|
||||
CPClipView _contentView;
|
||||
|
||||
CPClipView _headerClipView;
|
||||
CPView _cornerView;
|
||||
|
||||
BOOL _hasVerticalScroller;
|
||||
BOOL _hasHorizontalScroller;
|
||||
BOOL _autohidesScrollers;
|
||||
@@ -57,23 +59,27 @@
|
||||
- (id)initWithFrame:(CGRect)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];
|
||||
|
||||
|
||||
_headerClipView = [[CPClipView alloc] init];
|
||||
|
||||
[self addSubview:_headerClipView];
|
||||
|
||||
[self setHasVerticalScroller:YES];
|
||||
[self setHasHorizontalScroller:YES];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -100,24 +106,24 @@
|
||||
*/
|
||||
- (void)setContentView:(CPClipView)aContentView
|
||||
{
|
||||
if (!aContentView)
|
||||
if (_contentView !== aContentView || !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];
|
||||
|
||||
// This will size the content view appropriately, so no need to size it in this method.
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -134,8 +140,11 @@
|
||||
*/
|
||||
- (void)setDocumentView:(CPView)aView
|
||||
{
|
||||
[_contentView setDocumentView:aView];
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
[_contentView setDocumentView:aView];
|
||||
|
||||
// FIXME: This should be observed.
|
||||
[self _updateCornerAndHeaderView];
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -166,72 +175,98 @@
|
||||
// [_verticalScroller setEnabled:NO];
|
||||
// [_horizontalScroller setEnabled:NO];
|
||||
}
|
||||
|
||||
|
||||
[_contentView setFrame:[self bounds]];
|
||||
|
||||
[_headerClipView setFrame:_CGRectMakeZero()];
|
||||
|
||||
--_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 = _CGRectGetWidth([_verticalScroller frame]);
|
||||
horizontalScrollerHeight = _CGRectGetHeight([_horizontalScroller frame]);
|
||||
var documentFrame = [documentView frame], // the size of the whole document
|
||||
contentFrame = [self bounds], // assume it takes up the entire size of the scrollview (no scrollers)
|
||||
headerClipViewFrame = [self _headerClipViewFrame],
|
||||
headerClipViewHeight = _CGRectGetHeight(headerClipViewFrame);
|
||||
|
||||
if (_autohidesScrollers)
|
||||
{
|
||||
// Check to see if either affected the other!
|
||||
if (shouldShowVerticalScroller)
|
||||
shouldShowHorizontalScroller = (!_autohidesScrollers || difference.width > -verticalScrollerWidth) && _hasHorizontalScroller;
|
||||
contentFrame.origin.y += headerClipViewHeight;
|
||||
contentFrame.size.height -= headerClipViewHeight;
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
shouldShowVerticalScroller = (!_autohidesScrollers || difference.height > -horizontalScrollerHeight) && _hasVerticalScroller;
|
||||
}
|
||||
|
||||
[_verticalScroller setHidden:!shouldShowVerticalScroller];
|
||||
[_verticalScroller setEnabled:difference.height > 0.0];
|
||||
|
||||
[_horizontalScroller setHidden:!shouldShowHorizontalScroller];
|
||||
[_horizontalScroller setEnabled:difference.width > 0.0];
|
||||
var difference = _CGSizeMake(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(contentFrame), _CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentFrame)),
|
||||
verticalScrollerWidth = _CGRectGetWidth([_verticalScroller frame]),
|
||||
horizontalScrollerHeight = _CGRectGetHeight([_horizontalScroller frame]),
|
||||
hasVerticalScroll = difference.height > 0.0,
|
||||
hasHorizontalScroll = difference.width > 0.0,
|
||||
shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll),
|
||||
shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
|
||||
|
||||
// Now we have to account for the shown scrollers affecting the deltas.
|
||||
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)];
|
||||
hasHorizontalScroll = difference.width > 0.0;
|
||||
shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
|
||||
}
|
||||
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)];
|
||||
hasVerticalScroll = difference.height > 0.0;
|
||||
shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll);
|
||||
}
|
||||
|
||||
// We now definitively know which scrollers are shown or not, as well as whether they are showing scroll values.
|
||||
[_verticalScroller setHidden:!shouldShowVerticalScroller];
|
||||
[_verticalScroller setEnabled:hasVerticalScroll];
|
||||
|
||||
[_horizontalScroller setHidden:!shouldShowHorizontalScroller];
|
||||
[_horizontalScroller setEnabled:hasHorizontalScroll];
|
||||
|
||||
// We can thus appropriately account for them changing the content size.
|
||||
if (shouldShowVerticalScroller)
|
||||
contentFrame.size.width -= verticalScrollerWidth;
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
contentFrame.size.height -= horizontalScrollerHeight;
|
||||
|
||||
var scrollPoint = [_contentView bounds].origin,
|
||||
wasShowingVerticalScroller = ![_verticalScroller isHidden],
|
||||
wasShowingHorizontalScroller = ![_horizontalScroller isHidden];
|
||||
|
||||
if (shouldShowVerticalScroller)
|
||||
{
|
||||
var verticalScrollerY = MAX(_CGRectGetHeight([self _cornerViewFrame]), headerClipViewHeight),
|
||||
verticalScrollerHeight = _CGRectGetHeight([self bounds]) - verticalScrollerY;
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
verticalScrollerHeight -= horizontalScrollerHeight;
|
||||
|
||||
[_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height];
|
||||
[_verticalScroller setKnobProportion:_CGRectGetHeight(contentFrame) / _CGRectGetHeight(documentFrame)];
|
||||
[_verticalScroller setFrame:_CGRectMake(_CGRectGetMaxX(contentFrame), verticalScrollerY, verticalScrollerWidth, verticalScrollerHeight)];
|
||||
}
|
||||
else if (wasShowingVerticalScroller)
|
||||
{
|
||||
[_verticalScroller setFloatValue:0.0];
|
||||
[_verticalScroller setKnobProportion:1.0];
|
||||
}
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
{
|
||||
[_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width];
|
||||
[_horizontalScroller setKnobProportion:_CGRectGetWidth(contentFrame) / _CGRectGetWidth(documentFrame)];
|
||||
[_horizontalScroller setFrame:_CGRectMake(0.0, _CGRectGetMaxY(contentFrame), _CGRectGetWidth(contentFrame), horizontalScrollerHeight)];
|
||||
}
|
||||
else if (wasShowingHorizontalScroller)
|
||||
[_horizontalScroller setFloatValue:0.0 knobProportion:1.0];
|
||||
|
||||
[_contentView setFrame:contentViewFrame];
|
||||
|
||||
{
|
||||
[_horizontalScroller setFloatValue:0.0];
|
||||
[_horizontalScroller setKnobProportion:1.0];
|
||||
}
|
||||
|
||||
[_contentView setFrame:contentFrame];
|
||||
[_headerClipView setFrame:headerClipViewFrame];
|
||||
[_cornerView setFrame:[self _cornerViewFrame]];
|
||||
|
||||
--_recursionCount;
|
||||
}
|
||||
|
||||
@@ -269,7 +304,7 @@
|
||||
|
||||
/*!
|
||||
Specifies whether the scroll view can have a horizontal scroller.
|
||||
@param hasHorizontalScroller <code>YES</code> lets the scroll view
|
||||
@param hasHorizontalScroller \c YES lets the scroll view
|
||||
allocate a horizontal scroller if necessary.
|
||||
*/
|
||||
- (void)setHasHorizontalScroller:(BOOL)shouldHaveHorizontalScroller
|
||||
@@ -280,7 +315,7 @@
|
||||
_hasHorizontalScroller = shouldHaveHorizontalScroller;
|
||||
|
||||
if (_hasHorizontalScroller && !_horizontalScroller)
|
||||
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, CPRectGetWidth([self bounds]), [CPScroller scrollerWidth])]];
|
||||
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, _CGRectGetWidth([self bounds]), [CPScroller scrollerWidth])]];
|
||||
|
||||
else if (!_hasHorizontalScroller && _horizontalScroller)
|
||||
{
|
||||
@@ -291,7 +326,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the scroll view can have a horizontal scroller.
|
||||
Returns \c YES if the scroll view can have a horizontal scroller.
|
||||
*/
|
||||
- (BOOL)hasHorizontalScroller
|
||||
{
|
||||
@@ -332,7 +367,7 @@
|
||||
/*!
|
||||
Specifies whether the scroll view has can have
|
||||
a vertical scroller. It allocates it if necessary.
|
||||
@param hasVerticalScroller <code>YES</code> allows
|
||||
@param hasVerticalScroller \c YES allows
|
||||
the scroll view to display a vertical scroller
|
||||
*/
|
||||
- (void)setHasVerticalScroller:(BOOL)shouldHaveVerticalScroller
|
||||
@@ -343,7 +378,7 @@
|
||||
_hasVerticalScroller = shouldHaveVerticalScroller;
|
||||
|
||||
if (_hasVerticalScroller && !_verticalScroller)
|
||||
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:CPRectMake(0.0, 0.0, [CPScroller scrollerWidth], CPRectGetHeight([self bounds]))]];
|
||||
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:_CGRectMake(0.0, 0.0, [CPScroller scrollerWidth], _CGRectGetHeight([self bounds]))]];
|
||||
|
||||
else if (!_hasVerticalScroller && _verticalScroller)
|
||||
{
|
||||
@@ -354,7 +389,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the scroll view can have a vertical scroller.
|
||||
Returns \c YES if the scroll view can have a vertical scroller.
|
||||
*/
|
||||
- (BOOL)hasVerticalScroller
|
||||
{
|
||||
@@ -363,7 +398,7 @@
|
||||
|
||||
/*!
|
||||
Sets whether the scroll view hides its scoll bars when not needed.
|
||||
@param autohidesScrollers <code>YES</code> causes the scroll bars
|
||||
@param autohidesScrollers \c YES causes the scroll bars
|
||||
to be hidden when not needed.
|
||||
*/
|
||||
- (void)setAutohidesScrollers:(BOOL)autohidesScrollers
|
||||
@@ -377,20 +412,76 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the scroll view hides its scroll
|
||||
Returns \c YES if the scroll view hides its scroll
|
||||
bars when not necessary.
|
||||
*/
|
||||
- (BOOL)autohidesScrollers
|
||||
{
|
||||
return _autohidesScrollers;
|
||||
}
|
||||
/*
|
||||
- (void)setFrameSize:(CPRect)aSize
|
||||
|
||||
- (void)_updateCornerAndHeaderView
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
var documentView = [self documentView],
|
||||
currentHeaderView = [self _headerView],
|
||||
documentHeaderView = [documentView respondsToSelector:@selector(headerView)] ? [documentView headerView] : nil;
|
||||
|
||||
if (currentHeaderView !== documentHeaderView)
|
||||
{
|
||||
[currentHeaderView removeFromSuperview];
|
||||
[_headerClipView setDocumentView:documentHeaderView];
|
||||
}
|
||||
|
||||
var documentCornerView = [documentView respondsToSelector:@selector(cornerView)] ? [documentView cornerView] : nil;
|
||||
|
||||
if (_cornerView !== documentCornerView)
|
||||
{
|
||||
[_cornerView removeFromSuperview];
|
||||
|
||||
_cornerView = documentCornerView;
|
||||
|
||||
if (_cornerView)
|
||||
[self addSubview:_cornerView];
|
||||
}
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}*/
|
||||
}
|
||||
|
||||
- (CPView)_headerView
|
||||
{
|
||||
var headerClipViewSubviews = [_headerClipView subviews];
|
||||
|
||||
return [headerClipViewSubviews count] ? headerClipViewSubviews[0] : nil;
|
||||
}
|
||||
|
||||
- (CGRect)_cornerViewFrame
|
||||
{
|
||||
if (!_cornerView)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
var bounds = [self bounds],
|
||||
frame = [_cornerView frame];
|
||||
|
||||
frame.origin.x = _CGRectGetMaxX(bounds) - _CGRectGetWidth(frame);
|
||||
frame.origin.y = 0;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (CGRect)_headerClipViewFrame
|
||||
{
|
||||
var headerView = [self _headerView];
|
||||
|
||||
if (!headerView)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
var frame = [self bounds];
|
||||
|
||||
frame.size.height = _CGRectGetHeight([headerView frame]);
|
||||
frame.size.width -= _CGRectGetWidth([self _cornerViewFrame]);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_verticalScrollerDidScroll:(CPScroller)aScroller
|
||||
@@ -448,6 +539,7 @@
|
||||
}
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -580,20 +672,19 @@
|
||||
*/
|
||||
- (void)scrollWheel:(CPEvent)anEvent
|
||||
{
|
||||
var value = [_verticalScroller floatValue],
|
||||
documentFrame = [[self documentView] frame],
|
||||
var documentFrame = [[self documentView] frame],
|
||||
contentBounds = [_contentView bounds];
|
||||
|
||||
contentBounds.origin.x += [anEvent deltaX] * _horizontalLineScroll;
|
||||
contentBounds.origin.y += [anEvent deltaY] * _verticalLineScroll;
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
|
||||
}
|
||||
|
||||
- (void)keyDown:(CPEvent)anEvent
|
||||
{
|
||||
var keyCode = [anEvent keyCode],
|
||||
value = [_verticalScroller floatValue],
|
||||
documentFrame = [[self documentView] frame],
|
||||
contentBounds = [_contentView bounds];
|
||||
|
||||
@@ -627,6 +718,7 @@
|
||||
}
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin, 0)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -64,9 +64,9 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
|
||||
BOOL _isVertical @accessors(readonly, getter=isVertical);
|
||||
float _knobProportion;
|
||||
|
||||
|
||||
CPScrollerPart _hitPart;
|
||||
|
||||
|
||||
CPScrollerPart _trackingPart;
|
||||
float _trackingFloatValue;
|
||||
CGPoint _trackingStartPoint;
|
||||
@@ -104,11 +104,12 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
_controlSize = CPRegularControlSize;
|
||||
_partRects = [];
|
||||
|
||||
[self setFloatValue:0.0 knobProportion:1.0];
|
||||
[self setFloatValue:0.0];
|
||||
[self setKnobProportion:1.0];
|
||||
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
[self _recalculateIsVertical];
|
||||
[self _calculateIsVertical];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -120,7 +121,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
*/
|
||||
+ (float)scrollerWidth
|
||||
{
|
||||
return 17.0;//[self scrollerWidthForControlSize:CPRegularControlSize];
|
||||
return 15.0;//[self scrollerWidthForControlSize:CPRegularControlSize];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -129,7 +130,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
*/
|
||||
+ (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
|
||||
{
|
||||
return 17.0;//_CPScrollerWidths[aControlSize];
|
||||
return 15.0;//_CPScrollerWidths[aControlSize];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -155,28 +156,17 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
return _controlSize;
|
||||
}
|
||||
|
||||
// Setting the Knob Position
|
||||
/*!
|
||||
Sets the scroller's knob position (ranges from 0.0 to 1.0).
|
||||
@param aValue the knob position (ranges from 0.0 to 1.0)
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue
|
||||
- (void)setObjectValue:(id)aValue
|
||||
{
|
||||
[super setFloatValue:MIN(1.0, MAX(0.0, aValue))];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the position and proportion of the knob.
|
||||
@param aValue the knob position (ranges from 0.0 to 1.0)
|
||||
@param aProportion the knob's proportion (ranges from 0.0 to 1.0)
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
|
||||
- (void)setKnobProportion:(float)aProportion
|
||||
{
|
||||
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
||||
|
||||
[self setFloatValue:aValue];
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -208,7 +198,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the part of the scroller that would be hit by <code>aPoint</code>.
|
||||
Returns the part of the scroller that would be hit by \c aPoint.
|
||||
@param aPoint the simulated point hit
|
||||
@return the part of the scroller that intersects the point
|
||||
*/
|
||||
@@ -533,15 +523,15 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
|
||||
}
|
||||
|
||||
- (void)_recalculateIsVertical
|
||||
- (void)_calculateIsVertical
|
||||
{
|
||||
// Recalculate isVertical.
|
||||
var bounds = [self bounds],
|
||||
width = _CGRectGetWidth(bounds),
|
||||
height = _CGRectGetHeight(bounds);
|
||||
|
||||
|
||||
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
||||
|
||||
|
||||
if (_isVertical === 1)
|
||||
[self setThemeState:CPThemeStateVertical];
|
||||
else if (_isVertical === 0)
|
||||
@@ -552,8 +542,6 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
[self _recalculateIsVertical];
|
||||
|
||||
[self checkSpaceForParts];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
@@ -590,18 +578,16 @@ var CPScrollerControlSizeKey = "CPScrollerControlSize",
|
||||
_controlSize = CPRegularControlSize;
|
||||
if ([aCoder containsValueForKey:CPScrollerControlSizeKey])
|
||||
_controlSize = [aCoder decodeIntForKey:CPScrollerControlSizeKey];
|
||||
|
||||
|
||||
_knobProportion = 1.0;
|
||||
if ([aCoder containsValueForKey:CPScrollerKnobProportionKey])
|
||||
_knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
|
||||
|
||||
|
||||
_partRects = [];
|
||||
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
[self _recalculateIsVertical];
|
||||
// [self checkSpaceForParts];
|
||||
// [self setNeedsLayout];
|
||||
[self _calculateIsVertical];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -616,3 +602,18 @@ var CPScrollerControlSizeKey = "CPScrollerControlSize",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPScroller (Deprecated)
|
||||
|
||||
/*!
|
||||
Sets the position and proportion of the knob.
|
||||
@param aValue the knob position (ranges from 0.0 to 1.0)
|
||||
@param aProportion the knob's proportion (ranges from 0.0 to 1.0)
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
|
||||
{
|
||||
[self setFloatValue:aValue];
|
||||
[self setKnobProportion:aProportion];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,704 @@
|
||||
/*
|
||||
* CPSearchField.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by cacaodev.
|
||||
* Copyright 2009.
|
||||
*
|
||||
* 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 "CPTextField.j"
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
/*!
|
||||
@global
|
||||
@group Menu tags
|
||||
*/
|
||||
CPSearchFieldRecentsTitleMenuItemTag = 1000;
|
||||
/*!
|
||||
@global
|
||||
@group Menu tags
|
||||
*/
|
||||
CPSearchFieldRecentsMenuItemTag = 1001;
|
||||
/*!
|
||||
@global
|
||||
@group Menu tags
|
||||
*/
|
||||
CPSearchFieldClearRecentsMenuItemTag = 1002;
|
||||
/*!
|
||||
@global
|
||||
@group Menu tags
|
||||
*/
|
||||
CPSearchFieldNoRecentsMenuItemTag = 1003;
|
||||
|
||||
var CPSearchFieldSearchImage = nil,
|
||||
CPSearchFieldFindImage = nil,
|
||||
CPSearchFieldCancelImage = nil,
|
||||
CPSearchFieldCancelPressedImage = nil;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPSearchField
|
||||
The CPSearchField class defines the programmatic interface for text fields that are optimized for text-based searches. A CPSearchField object directly inherits from the CPTextField class. The search field implemented by these classes presents a standard user interface for searches, including a search button, a cancel button, and a pop-up icon menu for listing recent search strings and custom search categories.
|
||||
|
||||
When the user types and then pauses, the text field's action message is sent to its target. You can query the text field's string value for the current text to search for. Do not rely on the sender of the action to be an CPMenu object because the menu may change. If you need to change the menu, modify the search menu template and call the setSearchMenuTemplate: method to update.
|
||||
*/
|
||||
@implementation CPSearchField : CPTextField
|
||||
{
|
||||
CPButton _searchButton;
|
||||
CPButton _cancelButton;
|
||||
CPMenu _searchMenuTemplate;
|
||||
CPMenu _searchMenu;
|
||||
|
||||
CPString _recentsAutosaveName;
|
||||
CPArray _recentSearches;
|
||||
|
||||
int _maximumRecents;
|
||||
BOOL _sendsWholeSearchString;
|
||||
BOOL _sendsSearchStringImmediately;
|
||||
CPTimer _partialStringTimer;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [CPSearchField class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
CPSearchFieldSearchImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldSearch.png"]];
|
||||
CPSearchFieldFindImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldFind.png"]];
|
||||
CPSearchFieldCancelImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancel.png"]];
|
||||
CPSearchFieldCancelPressedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancelPressed.png"]];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
_recentSearches = [CPArray array];
|
||||
_maximumRecents = 10;
|
||||
_sendsWholeSearchString = NO;
|
||||
_sendsSearchStringImmediately = NO;
|
||||
|
||||
[self setBezeled:YES];
|
||||
[self setBezelStyle:CPTextFieldRoundedBezel];
|
||||
[self setBordered:YES];
|
||||
[self setEditable:YES];
|
||||
[self setDelegate:self];
|
||||
|
||||
_cancelButton = [[CPButton alloc] initWithFrame:CPMakeRect(frame.size.width - 27,(frame.size.height-22)/2,22,22)];
|
||||
[self resetCancelButton];
|
||||
|
||||
|
||||
[_cancelButton setHidden:YES];
|
||||
[self addSubview:_cancelButton];
|
||||
|
||||
_searchButton = [[CPButton alloc] initWithFrame:CPMakeRect(5,(frame.size.height-25)/2,25,25)];
|
||||
[_searchButton setBezelStyle:CPRegularSquareBezelStyle];
|
||||
[_searchButton setBordered:NO];
|
||||
[_searchButton setImageScaling:CPScaleToFit];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_cancelButton._DOMElement.style.cursor = "default";
|
||||
_searchButton._DOMElement.style.cursor = "default";
|
||||
#endif
|
||||
|
||||
[self setSearchMenuTemplate:[self _searchMenuTemplate]];
|
||||
[self addSubview:_searchButton];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
var copy = [super copy];
|
||||
|
||||
[copy setCancelButton:[_cancelButton copy]];
|
||||
[copy setSearchButton:[_searchButton copy]];
|
||||
[copy setrecentsAutosaveName:[_recentsAutosaveName copy]];
|
||||
[copy setSendsWholeSearchString:[_sendsWholeSearchString copy]];
|
||||
[copy setSendsSearchStringImmediately:[_sendsSearchStringImmediately copy]];
|
||||
[copy setMaximumRecents:_maximumRecents];
|
||||
[copy setSearchMenutemplate:[_searchMenuTemplate copy]];
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Managing Buttons
|
||||
/*!
|
||||
Sets the button used to display the search-button image
|
||||
@param button The search button.
|
||||
*/
|
||||
- (void)setSearchButton:(CPButton)button
|
||||
{
|
||||
_searchButton = button;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the button used to display the search-button image.
|
||||
@return The search button.
|
||||
*/
|
||||
- (CPButton)searchButton
|
||||
{
|
||||
return _searchButton;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resets the search button to its default attributes.
|
||||
This method resets the target, action, regular image, and pressed image. By default, when users click the search button or press the Return key, the action defined for the receiver is sent to its designated target. This method gives you a way to customize the search button for specific situations and then reset the button defaults without having to undo changes individually.
|
||||
*/
|
||||
- (void)resetSearchButton
|
||||
{
|
||||
var searchButtonImage,
|
||||
action,
|
||||
target,
|
||||
button = [self searchButton];
|
||||
|
||||
if (_searchMenuTemplate == nil)
|
||||
{
|
||||
searchButtonImage = CPSearchFieldSearchImage;
|
||||
action = [self action];
|
||||
target = [self target];
|
||||
}
|
||||
else
|
||||
{
|
||||
searchButtonImage = CPSearchFieldFindImage;
|
||||
action = @selector(_showMenu:);
|
||||
target = self;
|
||||
}
|
||||
|
||||
[button setImage:searchButtonImage];
|
||||
[button setTarget:target];
|
||||
[button setAction:action];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the button object used to display the cancel-button image.
|
||||
@param button The cancel button.
|
||||
*/
|
||||
- (void)setCancelButton:(CPButton)button
|
||||
{
|
||||
_cancelButton = button;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the button object used to display the cancel-button image.
|
||||
@return The cancel button.
|
||||
*/
|
||||
- (CPButton)cancelButton
|
||||
{
|
||||
return _cancelButton;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resets the cancel button to its default attributes.
|
||||
This method resets the target, action, regular image, and pressed image. By default, when users click the cancel button, the delete: action message is sent up the responder chain. This method gives you a way to customize the cancel button for specific situations and then reset the button defaults without having to undo changes individually.
|
||||
*/
|
||||
- (void)resetCancelButton
|
||||
{
|
||||
var button = [self cancelButton];
|
||||
[button setBezelStyle:CPRegularSquareBezelStyle];
|
||||
[button setBordered:NO];
|
||||
[button setImageScaling:CPScaleToFit];
|
||||
[button setImage:CPSearchFieldCancelImage];
|
||||
[button setAlternateImage:CPSearchFieldCancelPressedImage];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(_searchFieldCancel:)];
|
||||
}
|
||||
|
||||
// Custom Layout
|
||||
/*!
|
||||
Modifies the bounding rectangle for the search-text field.
|
||||
@param rect The current bounding rectangle for the search text field.
|
||||
@return The updated bounding rectangle to use for the search text field. The default value is the value passed into the rect parameter.
|
||||
Subclasses can override this method to return a new bounding rectangle for the text-field object. You might use this method to provide a custom layout for the search field control.
|
||||
*/
|
||||
- (CPRect)searchTextRectForBounds:(CPRect)rect
|
||||
{
|
||||
var leftOffset = 0, width = rect.size.width;
|
||||
|
||||
if (_searchButton)
|
||||
{
|
||||
var searchRect = [_searchButton frame];
|
||||
leftOffset = searchRect.origin.x + searchRect.size.width;
|
||||
}
|
||||
|
||||
if (_cancelButton)
|
||||
{
|
||||
var cancelRect = [_cancelButton frame];
|
||||
width = cancelRect.origin.x - leftOffset;
|
||||
}
|
||||
|
||||
return CPMakeRect(leftOffset,rect.origin.y,width,rect.size.height);
|
||||
}
|
||||
|
||||
/*!
|
||||
Modifies the bounding rectangle for the search button.
|
||||
@param rect The current bounding rectangle for the search button.
|
||||
Subclasses can override this method to return a new bounding rectangle for the search button. You might use this method to provide a custom layout for the search field control.
|
||||
*/
|
||||
- (CPRect)searchButtonRectForBounds:(CPRect)rect // fix
|
||||
{
|
||||
return [_searchButton frame];
|
||||
}
|
||||
|
||||
/*!
|
||||
Modifies the bounding rectangle for the cancel button.
|
||||
@param rect The updated bounding rectangle to use for the cancel button. The default value is the value passed into the rect parameter.
|
||||
Subclasses can override this method to return a new bounding rectangle for the cancel button. You might use this method to provide a custom layout for the search field control.
|
||||
*/
|
||||
- (CPRect)cancelButtonRectForBounds:(CPRect)rect
|
||||
{
|
||||
return [_cancelButton frame];
|
||||
}
|
||||
|
||||
// Managing Menu Templates
|
||||
/*!
|
||||
Returns the menu template object used to dynamically construct the search pop-up icon menu.
|
||||
@return The current menu template.
|
||||
*/
|
||||
- (CPMenu)searchMenuTemplate
|
||||
{
|
||||
return _searchMenuTemplate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the menu template object used to dynamically construct the receiver's pop-up icon menu.
|
||||
@param menu The menu template to use.
|
||||
The receiver looks for the tag constants described in ÒMenu tagsÓ to determine how to populate the menu with items related to recent searches. See ÒConfiguring a Search MenuÓ for a sample of how you might set up the search menu template.
|
||||
*/
|
||||
- (void)setSearchMenuTemplate:(CPMenu)menu
|
||||
{
|
||||
_searchMenuTemplate = menu;
|
||||
|
||||
[self resetSearchButton];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
// Managing Search Modes
|
||||
/*!
|
||||
Returns a Boolean value indicating whether the receiver sends the search action message when the user clicks the search button (or presses return) or after each keystroke.
|
||||
@return \c YES if the action message is sent all at once when the user clicks the search button or presses return; otherwise, NO if the search string is sent after each keystroke. The default value is NO.
|
||||
*/
|
||||
- (BOOL)sendsWholeSearchString
|
||||
{
|
||||
return _sendsWholeSearchString;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the receiver sends the search action message when the user clicks the search button (or presses return) or after each keystroke.
|
||||
@param flag \c YES to send the action message all at once when the user clicks the search button or presses return; otherwise, NO to send the search string after each keystroke.
|
||||
*/
|
||||
- (void)setSendsWholeSearchString:(BOOL)flag
|
||||
{
|
||||
_sendsWholeSearchString = flag;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a Boolean value indicating whether the receiver sends its action immediately upon being notified of changes to the search field text or after a brief pause.
|
||||
@return \c YES if the text field sends its action immediately upon notification of any changes to the search field; otherwise, NO.
|
||||
*/
|
||||
- (BOOL)sendsSearchStringImmediately
|
||||
{
|
||||
return _sendsSearchStringImmediately;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the text field sends its action message to the target immediately upon notification of any changes to the search field text or after a brief pause.
|
||||
@param flag \c YES to send the text field's action immediately upon notification of any changes to the search field; otherwise, NO if you want the text field to pause briefly before sending its action message. Pausing gives the user the opportunity to type more text into the search field before initiating the search.
|
||||
*/
|
||||
- (void)setSendsSearchStringImmediately:(BOOL)flag
|
||||
{
|
||||
_sendsSearchStringImmediately = flag;
|
||||
}
|
||||
|
||||
// Managing Recent Search Strings
|
||||
/*!
|
||||
Returns the maximum number of recent search strings to display in the custom search menu.
|
||||
@return The maximum number of search strings that can appear in the menu. This value is between 0 and 254.
|
||||
*/
|
||||
- (int)maximumRecents
|
||||
{
|
||||
return _maximumRecents;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the maximum number of search strings that can appear in the search menu.
|
||||
@param maxRecents The maximum number of search strings that can appear in the menu. This value can be between 0 and 254. Specifying a value less than 0 sets the value to the default, which is 10. Specifying a value greater than 254 sets the maximum to 254.
|
||||
*/
|
||||
- (void)setMaximumRecents:(int)max
|
||||
{
|
||||
if (max > 254)
|
||||
max = 254;
|
||||
else if (max < 0)
|
||||
max = 10;
|
||||
|
||||
_maximumRecents = max;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the list of recent search strings for the control.
|
||||
@return An array of \c CPString objects, each of which contains a search string either displayed in the search menu or from a recent autosave archive. If there have been no recent searches and no prior searches saved under an autosave name, this array may be empty.
|
||||
*/
|
||||
- (CPArray)recentSearches
|
||||
{
|
||||
return _recentSearches;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the list of recent search strings to list in the pop-up icon menu of the receiver.
|
||||
@param searches An array of CPString objects containing the search strings.
|
||||
You might use this method to set the recent list of searches from an archived copy.
|
||||
*/
|
||||
- (void)setRecentSearches:(CPArray)searches
|
||||
{
|
||||
var max = MIN([self maximumRecents],[searches count]);
|
||||
var searches = [searches subarrayWithRange:CPMakeRange(0,max)];
|
||||
_recentSearches = searches;
|
||||
|
||||
[self _autosaveRecentSearchList];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the key under which the prior list of recent search strings has been archived.
|
||||
@return The autosave name, which is used as a key in the standard user defaults to save the recent searches. The default value is nil, which causes searches not to be autosaved.
|
||||
*/
|
||||
- (CPString)recentsAutosaveName
|
||||
{
|
||||
return _recentsAutosaveName;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the autosave name under which the receiver automatically archives the list of recent search strings.
|
||||
@param name The autosave name, which is used as a key in the standard user defaults to save the recent searches. If you specify nil or an empty string for this parameter, no autosave name is set and searches are not autosaved.
|
||||
*/
|
||||
- (void)setRecentsAutosaveName:(CPString)name
|
||||
{
|
||||
_recentsAutosaveName = name;
|
||||
|
||||
if(name != nil)
|
||||
[self _registerForAutosaveNotification];
|
||||
else
|
||||
[self _deregisterForAutosaveNotification];
|
||||
}
|
||||
|
||||
// Private methods and subclassing
|
||||
|
||||
- (CPRect)contentRectForBounds:(CPRect)bounds
|
||||
{
|
||||
var superbounds = [super contentRectForBounds:bounds];
|
||||
return [self searchTextRectForBounds:superbounds];
|
||||
}
|
||||
|
||||
+ (double)_keyboardDelayForPartialSearchString:(CPString)string
|
||||
{
|
||||
return (6 - MIN([string length],4))/10;
|
||||
}
|
||||
|
||||
- (CPMenu)menu
|
||||
{
|
||||
return _searchMenu;
|
||||
}
|
||||
|
||||
- (BOOL)isOpaque
|
||||
{
|
||||
return [super isOpaque] && [_cancelButton isOpaque] && [_searchButton isOpaque];
|
||||
}
|
||||
|
||||
- (void)_updateCancelButtonVisibility
|
||||
{
|
||||
[_cancelButton setHidden:([[self stringValue] length] == 0)];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(CPNotification)aNotification
|
||||
{
|
||||
if(!_sendsWholeSearchString)
|
||||
{
|
||||
if(_sendsSearchStringImmediately)
|
||||
[self _sendPartialString];
|
||||
else
|
||||
{
|
||||
[_partialStringTimer invalidate];
|
||||
var timeInterval = [CPSearchField _keyboardDelayForPartialSearchString:[self stringValue]];
|
||||
|
||||
_partialStringTimer = [CPTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(_sendPartialString) userInfo:nil repeats:NO];
|
||||
}
|
||||
}
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
||||
{
|
||||
[super sendAction:anAction to:anObject];
|
||||
|
||||
[_partialStringTimer invalidate];
|
||||
|
||||
var current_value = [self objectValue];
|
||||
if(current_value != nil && current_value != "" && ![_recentSearches containsObject:current_value])
|
||||
{
|
||||
[self _addStringToRecentSearches:current_value];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_addStringToRecentSearches:(CPString)string
|
||||
{
|
||||
var newSearches = [CPMutableArray arrayWithArray:_recentSearches];
|
||||
[newSearches addObject:string];
|
||||
[self setRecentSearches:newSearches];
|
||||
}
|
||||
|
||||
- (BOOL)trackMouse:(CPEvent)event
|
||||
{
|
||||
var rect;
|
||||
var point;
|
||||
var location = [event locationInWindow];
|
||||
|
||||
point = [self convertPoint:location fromView:nil];
|
||||
|
||||
rect = [self searchButtonRectForBounds:[self frame]];
|
||||
|
||||
if (CPRectContainsPoint(rect,point))
|
||||
{
|
||||
return [[self searchButton] trackMouse:event];
|
||||
}
|
||||
|
||||
rect = [self cancelButtonRectForBounds:[self frame]];
|
||||
if (CPRectContainsPoint(rect,point))
|
||||
{
|
||||
return [[self cancelButton] trackMouse:event];
|
||||
}
|
||||
|
||||
return [super trackMouse:event];
|
||||
}
|
||||
|
||||
- (CPMenu)_searchMenuTemplate
|
||||
{
|
||||
var template, item;
|
||||
|
||||
template = [[CPMenu alloc] init];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent searches" action:NULL keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldRecentsTitleMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent search item" action:@selector(_searchFieldSearch:) keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Clear recent searches" action:@selector(_searchFieldClearRecents:) keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"No recent searches" action:NULL keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
- (void)_updateSearchMenu
|
||||
{
|
||||
if(_searchMenuTemplate == nil)
|
||||
return;
|
||||
|
||||
var i, menu = [[CPMenu alloc] init];
|
||||
var countOfRecents = [_recentSearches count];
|
||||
|
||||
for (i = 0; i < [_searchMenuTemplate numberOfItems]; i++)
|
||||
{
|
||||
var item = [_searchMenuTemplate itemAtIndex:i];
|
||||
var tag = [item tag];
|
||||
|
||||
if(tag == CPSearchFieldClearRecentsMenuItemTag && countOfRecents != 0)
|
||||
{
|
||||
var separator = [CPMenuItem separatorItem];
|
||||
[menu addItem:separator];
|
||||
}
|
||||
|
||||
if (!(tag == CPSearchFieldRecentsTitleMenuItemTag && countOfRecents == 0) &&
|
||||
!(tag == CPSearchFieldClearRecentsMenuItemTag && countOfRecents == 0) &&
|
||||
!(tag == CPSearchFieldNoRecentsMenuItemTag && countOfRecents != 0) &&
|
||||
!(tag == CPSearchFieldRecentsMenuItemTag))
|
||||
{
|
||||
var templateItem = [[CPMenuItem alloc] initWithTitle:[item title] action:[item action] keyEquivalent:[item keyEquivalent]];
|
||||
[templateItem setTarget:[item target]];
|
||||
[templateItem setEnabled:[item isEnabled]];
|
||||
[templateItem setTag:[item tag]];
|
||||
[menu addItem:templateItem];
|
||||
}
|
||||
else if (tag == CPSearchFieldRecentsMenuItemTag)
|
||||
{
|
||||
var j;
|
||||
for (j = 0; j < countOfRecents; j++)
|
||||
{
|
||||
var rencentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:j] action:[item action] keyEquivalent:[item keyEquivalent]];
|
||||
[rencentItem setTarget:[item target]];
|
||||
[menu addItem:rencentItem];
|
||||
}
|
||||
}
|
||||
}
|
||||
_searchMenu = menu;
|
||||
}
|
||||
|
||||
|
||||
- (void)_showMenu:(id)sender
|
||||
{
|
||||
if(_searchMenu == nil || ![self isEnabled])
|
||||
return;
|
||||
|
||||
[super selectText:nil];
|
||||
|
||||
var origin = CPMakePoint([self frame].origin.x, [self frame].origin.y + [self frame].size.height);
|
||||
var anEvent = [CPEvent keyEventWithType:CPRightMouseDown location:origin modifierFlags:0 timestamp:[CPDate date] windowNumber:1 context:[[CPGraphicsContext currentContext] graphicsPort] characters:"" charactersIgnoringModifiers:"" isARepeat:NO keyCode:0];
|
||||
|
||||
[CPMenu popUpContextMenu:_searchMenu withEvent:anEvent forView:sender];
|
||||
}
|
||||
|
||||
- (void)_sendPartialString
|
||||
{
|
||||
[[self target] performSelector:[self action] withObject:self];
|
||||
}
|
||||
|
||||
- (void)_searchFieldCancel:(id)sender
|
||||
{
|
||||
[self setObjectValue:nil];
|
||||
[self _sendPartialString];
|
||||
[self _updateCancelButtonVisibility];
|
||||
[sender setHidden:YES];
|
||||
}
|
||||
|
||||
- (void)_searchFieldSearch:(id)sender
|
||||
{
|
||||
[self setObjectValue:[sender title]];
|
||||
[self _sendPartialString];
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_searchFieldClearRecents:(id)sender
|
||||
{
|
||||
[self setRecentSearches:[CPArray array]];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
- (void)_registerForAutosaveNotification
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateAutosavedRecents:) name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
||||
}
|
||||
|
||||
- (void)_deregisterForAutosaveNotification
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
||||
}
|
||||
|
||||
- (void)_updateAutosavedRecents:(id)notification
|
||||
{
|
||||
var name = [notification object];
|
||||
var list = [self recentSearches];
|
||||
|
||||
[[CPUserDefaults standardUserDefaults] setObject:list forKey:name];
|
||||
|
||||
}
|
||||
|
||||
- (void)_autosaveRecentSearchList
|
||||
{
|
||||
if(_recentsAutosaveName != nil)
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:@"CPAutosavedRecentsChangedNotification" object:_recentsAutosaveName];
|
||||
}
|
||||
|
||||
- (void)_loadRecentSearchList
|
||||
{
|
||||
var list,
|
||||
name = [self recentsAutosaveName];
|
||||
|
||||
list = [[CPUserDefaults standardUserDefaults] objectForKey:name];
|
||||
_recentSearches = list;
|
||||
}
|
||||
|
||||
/*
|
||||
- (BOOL)trackMouse:(CPEvent)theEvent inRect:(CPRect)cellFrame ofView:(CPView)aTextView untilMouseUp:(BOOL)flag
|
||||
{
|
||||
}
|
||||
|
||||
- (BOOL)_trimRecentSearchList
|
||||
{
|
||||
}
|
||||
|
||||
- (void)_trackButton:(CPButton)button forEvent:(CPEvent)event inRect:(CPRect)rect ofView:(id)view
|
||||
{
|
||||
}
|
||||
|
||||
- (id)_selectOrEdit:(CPRect)rect inView:(id)view target:(id)target editor:(id)editor event:(id)event start:(int)start end:(int)end
|
||||
{
|
||||
}
|
||||
|
||||
- (void)resetCursorRect:(CPRect)rect inView:(id)view
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
var CPSearchButtonKey = @"CPSearchButtonKey",
|
||||
CPCancelButtonKey = @"CPCancelButtonKey",
|
||||
CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
|
||||
CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
|
||||
CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
|
||||
CPMaximumRecentsKey = @"CPMaximumRecentsKey",
|
||||
CPSearchMenuTemplateKey = @"CPSearchMenuTemplateKey";
|
||||
|
||||
@implementation CPSearchField (CPCoding)
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)coder
|
||||
{
|
||||
[super encodeWithCoder:coder];
|
||||
|
||||
[coder encodeObject:_searchButton forKey:CPSearchButtonKey];
|
||||
[coder encodeObject:_cancelButton forKey:CPCancelButtonKey];
|
||||
[coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
|
||||
[coder encodeBool:_sendsWholeSearchString forKey:CPSendsWholeSearchStringKey];
|
||||
[coder encodeBool:_sendsSearchStringImmediately forKey:CPSendsSearchStringImmediatelyKey];
|
||||
[coder encodeInt:_maximumRecents forKey:CPMaximumRecentsKey];
|
||||
[coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)coder
|
||||
{
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
_searchButton = [coder decodeObjectForKey:CPSearchButtonKey];
|
||||
_cancelButton = [coder decodeObjectForKey:CPCancelButtonKey];
|
||||
_recentsAutosaveName = [coder decodeObjectForKey:CPRecentsAutosaveNameKey];
|
||||
_sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
|
||||
_sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
|
||||
_maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
|
||||
[self setSearchMenuTemplate:[coder decodeObjectForKey:CPSearchMenuTemplateKey]];
|
||||
[self resetCancelButton];
|
||||
[self setDelegate:self];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -119,13 +119,13 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
else if (aCount < _segments.length)
|
||||
{
|
||||
for (var index = aCount; index < _segments.length; ++index)
|
||||
_segments[index] = nil;
|
||||
_segments.length = aCount;
|
||||
_themeStates.length = aCount;
|
||||
}
|
||||
|
||||
if (_selectedSegment < _segments.length)
|
||||
|
||||
if (_selectedSegment >= _segments.length)
|
||||
_selectedSegment = -1;
|
||||
|
||||
|
||||
[self tileWithChangedSegment:0];
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
/*!
|
||||
Selects a segment.
|
||||
@param aSegment the segment to select
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setSelectedSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -225,7 +225,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
Sets the width of the specified segment.
|
||||
@param aWidth the new width for the segment
|
||||
@param aSegment the segment to set the width for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -237,7 +237,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
/*!
|
||||
Returns the width for the specified segment.
|
||||
@param aSegment the segment to get the width for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (float)widthForSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -248,7 +248,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
Sets the image for the specified segment.
|
||||
@param anImage the image for the segment
|
||||
@param aSegment the segment to set the image on
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -262,7 +262,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
/*!
|
||||
Returns the image for the specified segment
|
||||
@param aSegment the segment to obtain the image for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (CPImage)imageForSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -273,7 +273,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
Sets the label for the specified segment
|
||||
@param aLabel the label for the segment
|
||||
@param aSegment the segment to label
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -287,7 +287,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
/*!
|
||||
Returns the label for the specified segment
|
||||
@param the segment to obtain the label for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (CPString)labelForSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -298,7 +298,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
Sets the menu for the specified segment
|
||||
@param aMenu the menu to set
|
||||
@param aSegment the segment to set the menu on
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -308,7 +308,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
/*!
|
||||
Returns the menu for the specified segment.
|
||||
@param aSegment the segment to obtain the menu for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (CPMenu)menuForSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -318,9 +318,9 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
/*!
|
||||
Sets the selection for the specified segment. If only one segment
|
||||
can be selected at a time, any other segment will be deselected.
|
||||
@param isSelected <code>YES</code> selects the segment. <code>NO</code> deselects it.
|
||||
@param isSelected \c YES selects the segment. \c NO deselects it.
|
||||
@param aSegment the segment to set the selection for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -358,9 +358,9 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the specified segment is selected.
|
||||
Returns \c YES if the specified segment is selected.
|
||||
@param aSegment the segment to check for selection
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (BOOL)isSelectedForSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -369,9 +369,9 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
/*!
|
||||
Enables/diables the specified segment.
|
||||
@param isEnabled <code>YES</code> enables the segment
|
||||
@param isEnabled \c YES enables the segment
|
||||
@param aSegment the segment to enable/disble
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -382,9 +382,9 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the specified segment is enabled.
|
||||
Returns \c YES if the specified segment is enabled.
|
||||
@param aSegment the segment to check
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (BOOL)isEnabledForSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -414,7 +414,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
/*!
|
||||
Draws the specified segment bezel
|
||||
@param aSegment the segment to draw the bezel for
|
||||
@param shouldHighlight <code>YES</code> highlights the bezel
|
||||
@param shouldHighlight \c YES highlights the bezel
|
||||
*/
|
||||
- (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
@@ -439,6 +439,16 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
return [self _leftOffsetForSegment:segment - 1] + [self widthForSegment:segment - 1] + thickness;
|
||||
}
|
||||
|
||||
- (unsigned)_indexOfLastSegment
|
||||
{
|
||||
var lastSegmentIndex = [_segments count] - 1;
|
||||
|
||||
if (lastSegmentIndex < 0)
|
||||
lastSegmentIndex = 0;
|
||||
|
||||
return lastSegmentIndex;
|
||||
}
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
var height = [self currentValueForThemeAttribute:@"default-height"],
|
||||
@@ -452,7 +462,12 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
else if (aName === "right-segment-bezel")
|
||||
{
|
||||
return CGRectMake(CGRectGetMaxX(bounds) - contentInset.right - bezelInset.right, bezelInset.top, contentInset.right, height);
|
||||
var lastSegmentLeftOffset = [self _leftOffsetForSegment:[self _indexOfLastSegment]];
|
||||
|
||||
return CPRectMake(lastSegmentLeftOffset + [self widthForSegment:[self _indexOfLastSegment]] - contentInset.right,
|
||||
bezelInset.top,
|
||||
contentInset.right,
|
||||
height);
|
||||
}
|
||||
else if (aName.substring(0, "segment-bezel".length) == "segment-bezel")
|
||||
{
|
||||
@@ -499,6 +514,9 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (_segments.length <= 0)
|
||||
return;
|
||||
|
||||
var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
|
||||
inState:_themeStates[0]];
|
||||
|
||||
@@ -582,7 +600,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
/*!
|
||||
Draws the specified segment
|
||||
@param aSegment the segment to draw
|
||||
@param shouldHighlight <code>YES</code> highlights the bezel
|
||||
@param shouldHighlight \c YES highlights the bezel
|
||||
*/
|
||||
- (void)drawSegment:(int)aSegment highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
|
||||
@@ -83,6 +83,10 @@ CPCircularSlider = 1;
|
||||
|
||||
if (doubleValue < _minValue)
|
||||
[self setDoubleValue:_minValue];
|
||||
|
||||
// The relative position may have (did) change.
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (float)minValue
|
||||
@@ -101,6 +105,10 @@ CPCircularSlider = 1;
|
||||
|
||||
if (doubleValue > _maxValue)
|
||||
[self setDoubleValue:_maxValue];
|
||||
|
||||
// The relative position may have (did) change.
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (float)maxValue
|
||||
|
||||
@@ -210,7 +210,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
_DOMDividerElements[_drawingDivider].style.cursor = "move";
|
||||
_DOMDividerElements[_drawingDivider].style.position = "absolute";
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
|
||||
|
||||
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
|
||||
|
||||
if (_isPaneSplitter)
|
||||
@@ -224,7 +224,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect));
|
||||
CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
|
||||
#endif
|
||||
@@ -303,7 +303,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
- (void)trackDivider:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type];
|
||||
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
{
|
||||
if (_currentDivider != CPNotFound)
|
||||
@@ -359,6 +359,9 @@ var CPSplitViewHorizontalImage = nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentDivider === CPNotFound)
|
||||
return;
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
|
||||
@@ -423,7 +426,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
frameA = [viewA frame],
|
||||
viewB = _subviews[dividerIndex + 1],
|
||||
frameB = [viewB frame];
|
||||
|
||||
|
||||
var realPosition = MAX(MIN(position, actualMax), actualMin);
|
||||
|
||||
if (position < proposedMin + (actualMin - proposedMin) / 2)
|
||||
@@ -488,8 +491,9 @@ var CPSplitViewHorizontalImage = nil,
|
||||
totalSizableSpace = 0;
|
||||
|
||||
var nonSizableSpace = totalSizableSpace ? bounds.size[_sizeComponent] - totalSizableSpace : 0,
|
||||
ratio = (bounds.size[_sizeComponent] - totalDividers*dividerThickness - nonSizableSpace) / (oldSize[_sizeComponent]- totalDividers*dividerThickness - nonSizableSpace),
|
||||
remainingFlexibleSpace = bounds.size[_sizeComponent] - oldSize[_sizeComponent];
|
||||
remainingFlexibleSpace = bounds.size[_sizeComponent] - oldSize[_sizeComponent],
|
||||
oldDimension = (oldSize[_sizeComponent]- totalDividers*dividerThickness - nonSizableSpace),
|
||||
ratio = oldDimension <= 0 ? 0 : (bounds.size[_sizeComponent] - totalDividers*dividerThickness - nonSizableSpace) / oldDimension;
|
||||
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
@@ -510,7 +514,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
viewFrame.size[_sizeComponent] = [view frame].size[_sizeComponent];
|
||||
else
|
||||
alert("SHOULD NEVER GET HERE");
|
||||
|
||||
|
||||
bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness;
|
||||
|
||||
[view setFrame:viewFrame];
|
||||
@@ -559,7 +563,7 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
||||
@implementation CPSplitView (CPCoding)
|
||||
|
||||
/*
|
||||
Initializes the split view by unarchiving data from <code>aCoder</code>.
|
||||
Initializes the split view by unarchiving data from \c aCoder.
|
||||
@param aCoder the coder containing the archived CPSplitView.
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
|
||||
@@ -22,13 +22,9 @@
|
||||
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
#include "Platform/Platform.h"
|
||||
@import "CPPlatformString.j"
|
||||
|
||||
|
||||
var CPStringReferenceElement = nil,
|
||||
CPStringDefaultFont = nil;
|
||||
|
||||
@implementation CPString (CPStringDrawing)
|
||||
|
||||
/*!
|
||||
@@ -46,75 +42,7 @@ var CPStringReferenceElement = nil,
|
||||
|
||||
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
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);
|
||||
}
|
||||
|
||||
if (!aFont)
|
||||
{
|
||||
if (!CPStringDefaultFont)
|
||||
CPStringDefaultFont = [CPFont systemFontOfSize:12.0];
|
||||
|
||||
aFont = CPStringDefaultFont;
|
||||
}
|
||||
|
||||
var style = CPStringReferenceElement.style;
|
||||
|
||||
if (aWidth === NULL)
|
||||
{
|
||||
style.width = "";
|
||||
style.whiteSpace = "pre";
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
style.width = ROUND(aWidth) + "px";
|
||||
|
||||
if (document.attachEvent)
|
||||
style.wordWrap = "break-word";
|
||||
|
||||
else
|
||||
{
|
||||
style.whiteSpace = "-o-pre-wrap";
|
||||
style.whiteSpace = "-pre-wrap";
|
||||
style.whiteSpace = "-moz-pre-wrap";
|
||||
style.whiteSpace = "pre-wrap";
|
||||
}
|
||||
}
|
||||
|
||||
style.font = [aFont cssString];
|
||||
|
||||
if (CPFeatureIsCompatible(CPJavascriptInnerTextFeature))
|
||||
CPStringReferenceElement.innerText = self;
|
||||
|
||||
else if (CPFeatureIsCompatible(CPJavascriptTextContentFeature))
|
||||
CPStringReferenceElement.textContent = self;
|
||||
|
||||
return _CGSizeMake(CPStringReferenceElement.clientWidth, CPStringReferenceElement.clientHeight);
|
||||
#endif
|
||||
return _CGSizeMakeZero();
|
||||
}
|
||||
|
||||
+ (void)_resetSize
|
||||
{
|
||||
CPStringReferenceElement = nil;
|
||||
return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2008, 280 North, Inc.
|
||||
* Copyright 2009, 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
|
||||
@@ -20,12 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPSortDescriptor.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import "CPTableHeaderView.j"
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
|
||||
/*
|
||||
@global
|
||||
@@ -43,389 +44,300 @@ CPTableColumnAutoresizingMask = 1;
|
||||
*/
|
||||
CPTableColumnUserResizingMask = 2;
|
||||
|
||||
#define PurgableInfoMake(aView, aRow) { view:(aView), row:(aRow) }
|
||||
#define PurgableInfoView(anInfo) ((anInfo).view)
|
||||
#define PurgableInfoRow(anInfo) ((anInfo).row)
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTableColumn
|
||||
|
||||
An CPTableColumn object mainly keeps information about the width of the column, its minimum and maximum width; whether the column can be edited or resized; and the cells used to draw the column header and the data in the column. You can change all these attributes of the column by calling the appropriate methods. Please note that the table column does not hold nor has access to the data to be displayed in the column; this data is maintained in the table view's data source.</p>
|
||||
|
||||
<p>Each CPTableColumn object is identified by a CPString, called the column identifier. The reason is that, after a column has been added to a table view, the user might move the columns around, so there is a need to identify the columns regardless of its position in the table.
|
||||
|
||||
@ignore
|
||||
*/
|
||||
@implementation CPTableColumn : CPObject
|
||||
{
|
||||
CPString _identifier;
|
||||
CPView _headerView;
|
||||
|
||||
CPTableView _tableView;
|
||||
|
||||
float _width;
|
||||
float _minWidth;
|
||||
float _maxWidth;
|
||||
|
||||
unsigned _resizingMask;
|
||||
CPTableView _tableView;
|
||||
CPView _headerView;
|
||||
CPView _dataView;
|
||||
Object _dataViewData;
|
||||
|
||||
CPView _dataView; // default data view for this column
|
||||
float _width;
|
||||
float _minWidth;
|
||||
float _maxWidth;
|
||||
|
||||
Object _dataViewData; // cache of data view archives (key=data view hash, value=data view archive)
|
||||
Object _dataViewForView; // mapping from view instances back to their data view prototype (key=view instance hash, value=data view)
|
||||
Object _purgableInfosForDataView; // (key=data view hash, value=)
|
||||
id _identifier;
|
||||
BOOL _isEditable;
|
||||
CPSortDescriptor _sortDescriptorPrototype;
|
||||
BOOL _isHidden;
|
||||
CPString _headerToolTip;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the table column with the specified identifier.
|
||||
@param anIdentifier the identifier
|
||||
@return the initialized table column
|
||||
*/
|
||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||
- (id)initWithIdentifier:(id)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = anIdentifier;
|
||||
|
||||
_width = 40.0;
|
||||
_minWidth = 8.0;
|
||||
_maxWidth = 1000.0;
|
||||
|
||||
var dataView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[dataView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateHighlighted];
|
||||
|
||||
[self setDataView:dataView];
|
||||
|
||||
_headerView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[_headerView setBackgroundColor:[CPColor greenColor]];
|
||||
_dataViewData = { };
|
||||
|
||||
_width = 100.0;
|
||||
_minWidth = 10.0;
|
||||
_maxWidth = 1000000.0;
|
||||
|
||||
[self setIdentifier:anIdentifier];
|
||||
[self setHeaderView:[CPTextField new]];
|
||||
[self setDataView:[CPTextField new]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_dataViewData = {};
|
||||
_dataViewForView = {};
|
||||
_purgableInfosForDataView = {};
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the table column's identifier
|
||||
@param anIdentifier the new identifier
|
||||
*/
|
||||
- (void)setIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
_identifier = anIdentifier;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the table column's identifier
|
||||
*/
|
||||
- (CPString)identifier
|
||||
{
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
// Setting the CPTableView
|
||||
/*!
|
||||
Sets the table's view. This is called automatically by Cappuccino.
|
||||
@param aTableView the new table view
|
||||
*/
|
||||
- (void)setTableView:(CPTableView)aTableView
|
||||
{
|
||||
_tableView = aTableView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's table view.
|
||||
*/
|
||||
- (CPTableView)tableView
|
||||
{
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
// Controlling size
|
||||
/*!
|
||||
Sets the column's width.
|
||||
@param aWidth the new column width
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth
|
||||
{
|
||||
_width = aWidth;
|
||||
aWidth = +aWidth;
|
||||
|
||||
if (_width === aWidth)
|
||||
return;
|
||||
|
||||
var newWidth = MIN(MAX(aWidth, [self minWidth]), [self maxWidth]);
|
||||
|
||||
if (_width === newWidth)
|
||||
return;
|
||||
|
||||
var oldWidth = _width;
|
||||
|
||||
_width = newWidth;
|
||||
|
||||
var tableView = [self tableView];
|
||||
|
||||
if (tableView)
|
||||
{
|
||||
var index = [[tableView tableColumns] indexOfObjectIdenticalTo:self];
|
||||
|
||||
// FIXME: THIS IS HORRIBLE. Don't just reload everything when a table column changes, just relayout the changed widths.
|
||||
tableView._reloadAllRows = YES;
|
||||
tableView._dirtyTableColumnRangeIndex = tableView._dirtyTableColumnRangeIndex < 0 ? index : MIN(index, tableView._dirtyTableColumnRangeIndex);
|
||||
|
||||
[tableView tile];
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPTableViewColumnDidResizeNotification
|
||||
object:tableView
|
||||
userInfo:[CPDictionary dictionaryWithObjects:[self, oldWidth] forKeys:[@"CPTableColumn", "CPOldWidth"]]];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's width
|
||||
*/
|
||||
- (float)width
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets column's minimum width.
|
||||
@param aWidth the new minimum column width
|
||||
*/
|
||||
- (void)setMinWidth:(float)aWidth
|
||||
- (void)setMinWidth:(float)aMinWidth
|
||||
{
|
||||
if (_width < (_minWidth = aWidth))
|
||||
[self setWidth:_minWidth];
|
||||
aMinWidth = +aMinWidth;
|
||||
|
||||
if (_minWidth === aMinWidth)
|
||||
return;
|
||||
|
||||
_minWidth = aMinWidth;
|
||||
|
||||
var width = [self width],
|
||||
newWidth = MAX(width, [self minWidth]);
|
||||
|
||||
if (width !== newWidth)
|
||||
[self setWidth:newWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
The column's minimum width
|
||||
*/
|
||||
- (float)minWidth
|
||||
{
|
||||
return _minWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the column's maximum width.
|
||||
@param aWidth the new maximum width
|
||||
*/
|
||||
- (void)setMaxWidth:(float)aWidth
|
||||
- (void)setMaxWidth:(float)aMaxWidth
|
||||
{
|
||||
if (_width > (_maxmimumWidth = aWidth))
|
||||
[self setWidth:_maxWidth];
|
||||
aMaxWidth = +aMaxWidth;
|
||||
|
||||
if (_maxWidth === aMaxWidth)
|
||||
return;
|
||||
|
||||
_maxWidth = aMaxWidth;
|
||||
|
||||
var width = [self width],
|
||||
newWidth = MAX(width, [self maxWidth]);
|
||||
|
||||
if (width !== newWidth)
|
||||
[self setWidth:newWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the resizing mask. The mask is one of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
@param aMask the new resizing mask
|
||||
*/
|
||||
- (void)setResizingMask:(unsigned)aMask
|
||||
- (float)maxWidth
|
||||
{
|
||||
_resizingMask = aMask;
|
||||
return _maxWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's resizing mask. One of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
*/
|
||||
- (unsigned)resizingMask
|
||||
- (void)setResizingMask:(unsigned)aResizingMask
|
||||
{
|
||||
_resizingMask = aResizingMask;
|
||||
}
|
||||
|
||||
- (float)resizingMask
|
||||
{
|
||||
return _resizingMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resizes the column according to the min, max and set width.
|
||||
*/
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var width = CPRectGetWidth([_headerView frame]);
|
||||
|
||||
if (width < _minWidth)
|
||||
var width = _CGRectGetWidth([_headerView frame]);
|
||||
|
||||
if (width < [self minWidth])
|
||||
[self setMinWidth:width];
|
||||
else if (width > _maxWidth)
|
||||
else if (width > [self maxWidth])
|
||||
[self setMaxWidth:width]
|
||||
|
||||
if (_width != width)
|
||||
if (_width !== width)
|
||||
[self setWidth:width];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the column in this data is editable.
|
||||
@param aFlag <code>YES</code> means the column data is editable
|
||||
*/
|
||||
- (void)setEditable:(BOOL)aFlag
|
||||
//Setting Component Cells
|
||||
- (void)setHeaderView:(CPView)aView
|
||||
{
|
||||
_isEditable = aFlag;
|
||||
if (!aView)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Attempt to set nil header view on " + [self description]];
|
||||
|
||||
_headerView = aView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the column data is editable.
|
||||
- (CPView)headerView
|
||||
{
|
||||
return _headerView;
|
||||
}
|
||||
|
||||
- (void)setDataView:(CPView)aView
|
||||
{
|
||||
if (_dataView === aView)
|
||||
return;
|
||||
|
||||
if (_dataView)
|
||||
_dataViewData[[_dataView UID]] = nil;
|
||||
|
||||
_dataView = aView;
|
||||
_dataViewData[[aView UID]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||
}
|
||||
|
||||
- (CPView)dataView
|
||||
{
|
||||
return _dataView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the CPView object used by the CPTableView to draw values for the receiver.
|
||||
|
||||
By default, this method just calls dataView. Subclassers can override if they need to
|
||||
potentially use different cells for different rows. Subclasses should expect this method
|
||||
to be invoked with row equal to -1 in cases where no actual row is involved but the table
|
||||
view needs to get some generic cell info.
|
||||
*/
|
||||
- (id)dataViewForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataView];
|
||||
}
|
||||
|
||||
- (id)_newDataViewForRow:(int)aRowIndex
|
||||
{
|
||||
var dataView = [self dataViewForRow:aRowIndex],
|
||||
dataViewUID = [dataView UID];
|
||||
|
||||
var x = [self tableView]._cachedDataViews[dataViewUID];
|
||||
if (x && x.length)
|
||||
return x.pop();
|
||||
|
||||
// if we haven't cached an archive of the data view, do it now
|
||||
if (!_dataViewData[dataViewUID])
|
||||
_dataViewData[dataViewUID] = [CPKeyedArchiver archivedDataWithRootObject:dataView];
|
||||
|
||||
// unarchive the data view cache
|
||||
var newDataView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[dataViewUID]];
|
||||
newDataView.identifier = dataViewUID;
|
||||
return newDataView;
|
||||
}
|
||||
|
||||
//Setting the Identifier
|
||||
|
||||
/*
|
||||
Sets the receiver identifier to anIdentifier.
|
||||
*/
|
||||
- (void)setIdentifier:(id)anIdentifier
|
||||
{
|
||||
_identifier = anIdentifier;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the object used by the data source to identify the attribute corresponding to the receiver.
|
||||
*/
|
||||
- (id)identifier
|
||||
{
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
//Controlling Editability
|
||||
|
||||
/*
|
||||
Controls whether the user can edit cells in the receiver by double-clicking them.
|
||||
*/
|
||||
- (void)setEditable:(BOOL)shouldBeEditable
|
||||
{
|
||||
_isEditable = shouldBeEditable;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns YES if the user can edit cells associated with the receiver by double-clicking the
|
||||
column in the NSTableView, NO otherwise.
|
||||
*/
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
//Setting the column header view
|
||||
|
||||
/*!
|
||||
Sets the view that draws the column's header.
|
||||
@param aHeaderView the view that will draws the column header
|
||||
*/
|
||||
|
||||
- (void)setHeaderView:(CPView)aView
|
||||
//Sorting
|
||||
- (void)setSortDescriptorPrototype:(CPSortDescriptor)aSortDescriptor
|
||||
{
|
||||
_headerView = aView;
|
||||
_sortDescriptorPrototype = aSortDescriptor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the view that draws the column's header
|
||||
*/
|
||||
- (CPView)headerView
|
||||
- (CPSortDescriptor)sortDescriptorPrototype
|
||||
{
|
||||
return _headerView;
|
||||
return _sortDescriptorPrototype;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the data cell that draws rows in this column.
|
||||
*/
|
||||
- (void)setDataCell:(CPView <CPCoding>)aView
|
||||
//Setting Column Visibility
|
||||
|
||||
- (void)setHidden:(BOOL)shouldBeHidden
|
||||
{
|
||||
[self setDataView:aView];
|
||||
_isHidden = shouldBeHidden;
|
||||
}
|
||||
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
return _isHidden;
|
||||
}
|
||||
|
||||
//Setting Tool Tips
|
||||
|
||||
/*
|
||||
Sets the data view that draws rows in this column.
|
||||
Sets the tooltip string that is displayed when the cursor pauses over the
|
||||
header cell of the receiver.
|
||||
*/
|
||||
- (void)setDataView:(CPView <CPCoding>)aView
|
||||
- (void)setHeaderToolTip:(CPString)aToolTip
|
||||
{
|
||||
if (_dataView)
|
||||
_dataViewData[[_dataView hash]] = nil;
|
||||
|
||||
_dataView = aView;
|
||||
_dataViewData[[aView hash]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||
_headerToolTip = aToolTip;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the data cell that draws rows in this column
|
||||
*/
|
||||
- (CPCell)dataCell
|
||||
- (CPString)headerToolTip
|
||||
{
|
||||
return _dataView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the data view that draws rows in this column
|
||||
*/
|
||||
- (CPView)dataView
|
||||
{
|
||||
return [self dataCell];
|
||||
}
|
||||
|
||||
/*!
|
||||
By default returns the value from <code>dataCell</code>. This can
|
||||
be overridden by a subclass to return different cells for different
|
||||
rows.
|
||||
@param aRowIndex the index of the row to obtain the cell for
|
||||
*/
|
||||
- (CPCell)dataCellForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataView];
|
||||
}
|
||||
|
||||
- (CPView)dataViewForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataCellForRow:aRowIndex];
|
||||
}
|
||||
/*
|
||||
- (void)_markViewAsPurgable:(CPView)aView
|
||||
{
|
||||
var viewHash = [aView hash],
|
||||
dataViewHash = [_dataViewForView[viewHash] hash];
|
||||
|
||||
if (!_purgableInfosForDataView[dataViewHash])
|
||||
_purgableInfosForDataView[dataViewHash] = [CPDictionary dictionary];
|
||||
|
||||
[_purgableInfosForDataView[dataViewHash] setObject:aView forKey:viewHash];
|
||||
}
|
||||
*/
|
||||
- (void)_markView:(CPView)aView inRow:(unsigned)aRow asPurgable:(BOOL)isPurgable
|
||||
{
|
||||
var viewHash = [aView hash],
|
||||
dataViewHash = [_dataViewForView[viewHash] hash];
|
||||
|
||||
if (!_purgableInfosForDataView[dataViewHash])
|
||||
{
|
||||
if (!isPurgable)
|
||||
return;
|
||||
|
||||
_purgableInfosForDataView[dataViewHash] = {};
|
||||
}
|
||||
|
||||
if (!isPurgable) {
|
||||
if (_purgableInfosForDataView[dataViewHash][viewHash])
|
||||
CPLog.warn("removing unpurgable " + _purgableInfosForDataView[dataViewHash][viewHash]);
|
||||
delete _purgableInfosForDataView[dataViewHash][viewHash];
|
||||
}
|
||||
else
|
||||
_purgableInfosForDataView[dataViewHash][viewHash] = PurgableInfoMake(aView, aRow);
|
||||
}
|
||||
|
||||
- (CPView)_newDataViewForRow:(int)aRowIndex avoidingRows:(CPRange)rows
|
||||
{
|
||||
var view = [self dataViewForRow:aRowIndex],
|
||||
viewHash = [view hash],
|
||||
purgableInfos = _purgableInfosForDataView[viewHash];
|
||||
|
||||
if (purgableInfos)
|
||||
{
|
||||
for (var key in purgableInfos)
|
||||
{
|
||||
var info = purgableInfos[key];
|
||||
//if (!CPLocationInRange(PurgableInfoRow(info), rows))
|
||||
//{
|
||||
//CPLog.debug("yes, a purged view is usable, its called: " + PurgableInfoView(info));
|
||||
delete purgableInfos[key];
|
||||
return PurgableInfoView(info);
|
||||
//}
|
||||
//else
|
||||
// CPLog.warn("avoiding");
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't cached an archive of the data view, do it now
|
||||
if (!_dataViewData[viewHash])
|
||||
_dataViewData[viewHash] = [CPKeyedArchiver archivedDataWithRootObject:view];
|
||||
|
||||
// unarchive the data view cache
|
||||
var newView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[viewHash]];
|
||||
|
||||
// map the new view's hash to it's data view prototype
|
||||
_dataViewForView[[newView hash]] = view;
|
||||
|
||||
CPLog.warn("creating cell: %s", newView);
|
||||
|
||||
return newView;
|
||||
}
|
||||
|
||||
- (void)_purge
|
||||
{
|
||||
for (var viewHash in _purgableInfosForDataView)
|
||||
{
|
||||
var purgableInfos = _purgableInfosForDataView[viewHash];
|
||||
|
||||
for (var key in purgableInfos)
|
||||
{
|
||||
var view = PurgableInfoView(purgableInfos[key]);
|
||||
|
||||
if (!view)
|
||||
CPLog.info("key="+key+" view=" + view + " purgableInfos[key]="+purgableInfos[key])
|
||||
else if (view._superview) {
|
||||
//CPLog.error("PURGING: (removing)" + view);
|
||||
//[view removeFromSuperview];
|
||||
[view setHidden:YES];
|
||||
}
|
||||
//else
|
||||
// CPLog.warn("PURGING: (already removed)" + view);
|
||||
|
||||
//delete purgableInfos[key];
|
||||
}
|
||||
}
|
||||
return _headerToolTip;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
CPTableColumnHeaderViewKey = @"CPTableColumnHeaderViewKey",
|
||||
CPTableColumnDataViewKey = @"CPTableColumnDataViewKey",
|
||||
@@ -438,18 +350,26 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = [aCoder decodeObjectForKey:CPTableColumnIdentifierKey];
|
||||
self = [super init];
|
||||
|
||||
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||
[self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||
|
||||
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
||||
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
||||
_maxWidth = [aCoder decodeFloatForKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
_resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
|
||||
if (self)
|
||||
{
|
||||
_dataViewData = { };
|
||||
|
||||
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
||||
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
||||
_maxWidth = [aCoder decodeFloatForKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
[self setIdentifier:[aCoder decodeObjectForKey:CPTableColumnIdentifierKey]];
|
||||
// [self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||
// [self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||
|
||||
[self setHeaderView:[CPTextField new]];
|
||||
[self setDataView:[CPTextField new]];
|
||||
|
||||
|
||||
// _resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -457,15 +377,49 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPTableColumnIdentifierKey];
|
||||
|
||||
[aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||
|
||||
|
||||
[aCoder encodeObject:_width forKey:CPTableColumnWidthKey];
|
||||
[aCoder encodeObject:_minWidth forKey:CPTableColumnMinWidthKey];
|
||||
[aCoder encodeObject:_maxWidth forKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
[aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
||||
|
||||
// [aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||
// [aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||
|
||||
// [aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTableColumn (NSInCompatibility)
|
||||
|
||||
- (void)setHeaderCell:(CPView)aView
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"setHeaderCell: is not supported. -setHeaderCell:aView instead."];
|
||||
}
|
||||
|
||||
- (CPView)headerCell
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"headCell is not supported. -headerView instead."];
|
||||
}
|
||||
|
||||
- (void)setDataCell:(CPView)aView
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"setDataCell: is not supported. Use -setHeaderCell:aView instead."];
|
||||
}
|
||||
|
||||
- (CPView)dataCell
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"dataCell is not supported. Use -dataCell instead."];
|
||||
}
|
||||
|
||||
- (id)dataCellForRow:(int)row
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"dataCellForRow: is not supported. Use -dataViewForRow:row instead."];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* CPTableHeaderView.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Ross Boucher.
|
||||
* Copyright 2009 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 "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
@implementation CPTableHeaderView : CPView
|
||||
{
|
||||
int _resizedColumn @accessors(readonly, property=resizedColumn);
|
||||
int _draggedColumn @accessors(readonly, property=draggedColumn);
|
||||
|
||||
float _draggedDistance @accessors(readonly, property=draggedDistance);
|
||||
|
||||
CPTableView _tableView @accessors(property=tableView);
|
||||
}
|
||||
|
||||
- (void)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_resizedColumn = CPNotFound;
|
||||
_draggedColumn = CPNotFound;
|
||||
_draggedDistance = 0.0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (int)columnAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
if (!CGRectContainsPoint([self bounds], aPoint))
|
||||
return CPNotFound;
|
||||
|
||||
// at this point, we can essentially ignore height, because all columns have equal heights
|
||||
// and that height is equal to the height of our own bounds, which we know we are inside of
|
||||
|
||||
var index = 0,
|
||||
count = [[_tableView tableColumns] count],
|
||||
tableSpacing = [_tableView intercellSpacing],
|
||||
tableColumns = [_tableView tableColumns],
|
||||
leftOffset = 0,
|
||||
pointX = aPoint.x;
|
||||
|
||||
for (; index < count; index++)
|
||||
{
|
||||
var width = [tableColumns[index] width] + tableSpacing.width;
|
||||
|
||||
if (pointX >= leftOffset && pointX < leftOffset + width)
|
||||
return index;
|
||||
|
||||
leftOffset += width;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (CGRect)headerRectOfColumn:(int)aColumnIndex
|
||||
{
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
tableSpacing = [_tableView intercellSpacing],
|
||||
bounds = [self bounds];
|
||||
|
||||
if (aColumnIndex < 0 || aColumnIndex > [tableColumns count])
|
||||
[CPException raise:"invalid" reason:"tried to get headerRectOfColumn: on invalid column"];
|
||||
|
||||
bounds.size.width = [tableColumns[aColumnIndex] width] + tableSpacing.width;
|
||||
|
||||
while (--aColumnIndex >= 0)
|
||||
bounds.origin.x += [tableColumns[aColumnIndex] width] + tableSpacing.width;
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
count = [tableColumns count],
|
||||
columnRect = [self bounds],
|
||||
spacing = [_tableView intercellSpacing];
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
var column = [tableColumns objectAtIndex:i],
|
||||
headerView = [column headerView];
|
||||
|
||||
columnRect.size.width = [column width] + spacing.width;
|
||||
|
||||
[headerView setFrame:columnRect];
|
||||
|
||||
columnRect.origin.x += [column width] + spacing.width;
|
||||
|
||||
[self addSubview:headerView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
[[_tableView gridColor] setStroke];
|
||||
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
exposedColumnIndexes = exposedColumnIndexes = [_tableView columnIndexesInRect:aRect],
|
||||
columnsArray = [];
|
||||
|
||||
[exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:nil];
|
||||
|
||||
var columnArrayIndex = 0,
|
||||
columnArrayCount = columnsArray.length;
|
||||
|
||||
for(; columnArrayIndex < columnArrayCount; ++columnArrayIndex)
|
||||
{
|
||||
// grab each column rect and add horizontal lines
|
||||
var columnToStroke = [self headerRectOfColumn:columnArrayIndex];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, ROUND(columnToStroke.origin.x + columnToStroke.size.width) - 0.5, ROUND(columnToStroke.origin.y) - 0.5);
|
||||
CGContextAddLineToPoint(context, ROUND(columnToStroke.origin.x + columnToStroke.size.width) - 0.5, ROUND(columnToStroke.origin.y + columnToStroke.size.height) - 0.5);
|
||||
CGContextSetLineWidth(context, 1);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -100,7 +100,7 @@ var CPSecureTextFieldCharacter = "\u2022";
|
||||
@implementation CPString (CPTextFieldAdditions)
|
||||
|
||||
/*!
|
||||
Returns the string (<code>self</code>).
|
||||
Returns the string (\c self).
|
||||
*/
|
||||
- (CPString)string
|
||||
{
|
||||
@@ -389,7 +389,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the textfield is currently editable by the user.
|
||||
Returns \c YES if the textfield is currently editable by the user.
|
||||
*/
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
@@ -398,7 +398,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
/*!
|
||||
Sets whether the field's text is selectable by the user.
|
||||
@param aFlag <code>YES</code> makes the text selectable
|
||||
@param aFlag \c YES makes the text selectable
|
||||
*/
|
||||
- (void)setSelectable:(BOOL)aFlag
|
||||
{
|
||||
@@ -406,7 +406,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the field's text is selectable by the user.
|
||||
Returns \c YES if the field's text is selectable by the user.
|
||||
*/
|
||||
- (BOOL)isSelectable
|
||||
{
|
||||
@@ -415,7 +415,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
/*!
|
||||
Sets whether the field's text is secure.
|
||||
@param aFlag <code>YES</code> makes the text secure
|
||||
@param aFlag \c YES makes the text secure
|
||||
*/
|
||||
- (void)setSecure:(BOOL)aFlag
|
||||
{
|
||||
@@ -423,7 +423,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the field's text is secure (password entry).
|
||||
Returns \c YES if the field's text is secure (password entry).
|
||||
*/
|
||||
- (BOOL)isSecure
|
||||
{
|
||||
@@ -433,7 +433,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
// Setting the Bezel Style
|
||||
/*!
|
||||
Sets whether the textfield will have a bezeled border.
|
||||
@param shouldBeBezeled <code>YES</code> means the textfield will draw a bezeled border
|
||||
@param shouldBeBezeled \c YES means the textfield will draw a bezeled border
|
||||
*/
|
||||
- (void)setBezeled:(BOOL)shouldBeBezeled
|
||||
{
|
||||
@@ -444,7 +444,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the textfield draws a bezeled border.
|
||||
Returns \c YES if the textfield draws a bezeled border.
|
||||
*/
|
||||
- (BOOL)isBezeled
|
||||
{
|
||||
@@ -478,7 +478,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
/*!
|
||||
Sets whether the textfield will have a border drawn.
|
||||
@param shouldBeBordered <code>YES</code> makes the textfield draw a border
|
||||
@param shouldBeBordered \c YES makes the textfield draw a border
|
||||
*/
|
||||
- (void)setBordered:(BOOL)shouldBeBordered
|
||||
{
|
||||
@@ -489,7 +489,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the textfield has a border.
|
||||
Returns \c YES if the textfield has a border.
|
||||
*/
|
||||
- (BOOL)isBordered
|
||||
{
|
||||
@@ -498,7 +498,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
/*!
|
||||
Sets whether the textfield will have a background drawn.
|
||||
@param shouldDrawBackground <code>YES</code> makes the textfield draw a background
|
||||
@param shouldDrawBackground \c YES makes the textfield draw a background
|
||||
*/
|
||||
- (void)setDrawsBackground:(BOOL)shouldDrawBackground
|
||||
{
|
||||
@@ -512,7 +512,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the textfield draws a background.
|
||||
Returns \c YES if the textfield draws a background.
|
||||
*/
|
||||
- (BOOL)drawsBackground
|
||||
{
|
||||
@@ -570,6 +570,15 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
element.style.font = [[self currentValueForThemeAttribute:@"font"] cssString];
|
||||
element.style.zIndex = 1000;
|
||||
|
||||
switch ([self alignment])
|
||||
{
|
||||
case CPCenterTextAlignment: element.style.textAlign = "center";
|
||||
break;
|
||||
case CPRightTextAlignment: element.style.textAlign = "right";
|
||||
break;
|
||||
default: element.style.textAlign = "left";
|
||||
}
|
||||
|
||||
var contentRect = [self contentRectForBounds:[self bounds]];
|
||||
|
||||
element.style.top = _CGRectGetMinY(contentRect) + "px";
|
||||
@@ -588,7 +597,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
//post CPControlTextDidBeginEditingNotification
|
||||
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
|
||||
|
||||
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
|
||||
CPTextFieldInputIsActive = YES;
|
||||
|
||||
@@ -647,7 +656,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
#endif
|
||||
|
||||
//post CPControlTextDidEndEditingNotification
|
||||
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
|
||||
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
|
||||
|
||||
return YES;
|
||||
}
|
||||
@@ -683,7 +692,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
{
|
||||
var string = [self stringValue];
|
||||
|
||||
if ((!string || [string length] === 0) && ![self hasThemeState:CPThemeStateEditing])
|
||||
if ((!string || string.length === 0) && ![self hasThemeState:CPThemeStateEditing])
|
||||
[self setThemeState:CPTextFieldStatePlaceholder];
|
||||
else
|
||||
[self unsetThemeState:CPTextFieldStatePlaceholder];
|
||||
@@ -763,8 +772,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
#if PLATFORM(DOM)
|
||||
var element = [self _inputElement];
|
||||
|
||||
if (element.parentNode == _DOMElement && ([self isEditable] || [self isSelectable]))
|
||||
element.select();
|
||||
if (element.parentNode === _DOMElement && ([self isEditable] || [self isSelectable]))
|
||||
window.setTimeout(function() { element.select(); }, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -531,7 +531,7 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute)
|
||||
|
||||
function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass)
|
||||
{
|
||||
var key = "$a" + anAttributeName;
|
||||
var key = "$a" + anAttributeName;
|
||||
|
||||
if (![aCoder containsValueForKey:key])
|
||||
var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
|
||||
@@ -555,3 +555,34 @@ function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme,
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
/* TO AUTO CREATE THESE:
|
||||
function bit_count(bits)
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
while (bits)
|
||||
{
|
||||
++count;
|
||||
bits &= (bits - 1);
|
||||
}
|
||||
|
||||
return count ;
|
||||
}
|
||||
|
||||
zeros = "000000000";
|
||||
|
||||
function pad(string, digits)
|
||||
{
|
||||
return zeros.substr(0, digits - string.length) + string;
|
||||
}
|
||||
|
||||
var str = ""
|
||||
str += '[';
|
||||
for (i = 0;i < Math.pow(2,6);++i)
|
||||
{
|
||||
str += bit_count(i) + " /*" + pad(i.toString(2),6) + "*" + "/, ";
|
||||
}
|
||||
print(str+']');
|
||||
|
||||
*/
|
||||
@@ -76,9 +76,9 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
Called to obtain a toolbar item. Required.
|
||||
@param toolbar the toolbar the item belongs to
|
||||
@param itemIdentifier the identifier of the toolbar item
|
||||
@param flag <code>YES</code> means the item will be placed in the toolbar. <code>NO</code> means the item will be displayed for
|
||||
@param flag \c YES means the item will be placed in the toolbar. \c NO means the item will be displayed for
|
||||
some other purpose (non-functional)
|
||||
@return the toolbar item or <code>nil</code> if no such item belongs in the toolbar
|
||||
@return the toolbar item or \c nil if no such item belongs in the toolbar
|
||||
*/
|
||||
@implementation CPToolbar : CPObject
|
||||
{
|
||||
@@ -176,7 +176,7 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the toolbar is currently visible
|
||||
Returns \c YES if the toolbar is currently visible
|
||||
*/
|
||||
- (BOOL)isVisible
|
||||
{
|
||||
@@ -185,7 +185,7 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
|
||||
/*!
|
||||
Sets whether the toolbar should be visible.
|
||||
@param aFlag <code>YES</code> makes the toolbar visible
|
||||
@param aFlag \c YES makes the toolbar visible
|
||||
*/
|
||||
- (void)setVisible:(BOOL)aFlag
|
||||
{
|
||||
@@ -304,7 +304,7 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the toolbar items sorted by their <code>visibilityPriority</code>(ies).
|
||||
Returns the toolbar items sorted by their \c visibilityPriority(ies).
|
||||
*/
|
||||
- (CPArray)itemsSortedByVisibilityPriority
|
||||
{
|
||||
@@ -369,7 +369,7 @@ var CPToolbarIdentifierKey = "CPToolbarIdentifierKey",
|
||||
@implementation CPToolbar (CPCoding)
|
||||
|
||||
/*
|
||||
Initializes the toolbar by unarchiving data from <code>aCoder</code>.
|
||||
Initializes the toolbar by unarchiving data from \c aCoder.
|
||||
@param aCoder the coder containing the archived CPToolbar.
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
@@ -725,7 +725,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
|
||||
- (CPView)viewForItem:(CPToolbarItem)anItem
|
||||
{
|
||||
var info = [_itemInfos objectForKey:[anItem hash]];
|
||||
var info = [_itemInfos objectForKey:[anItem UID]];
|
||||
|
||||
if (!info)
|
||||
return nil;
|
||||
@@ -735,7 +735,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
|
||||
- (CPTextField)labelForItem:(CPToolbarItem)anItem
|
||||
{
|
||||
var info = [_itemInfos objectForKey:[anItem hash]];
|
||||
var info = [_itemInfos objectForKey:[anItem UID]];
|
||||
|
||||
if (!info)
|
||||
return nil;
|
||||
@@ -745,7 +745,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
|
||||
- (float)minWidthForItem:(CPToolbarItem)anItem
|
||||
{
|
||||
var info = [_itemInfos objectForKey:[anItem hash]];
|
||||
var info = [_itemInfos objectForKey:[anItem UID]];
|
||||
|
||||
if (!info)
|
||||
return 0;
|
||||
@@ -814,7 +814,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
var minSize = [item minSize],
|
||||
minWidth = MAX(minSize.width, CGRectGetWidth([label frame]));
|
||||
|
||||
[_itemInfos setObject:_CPToolbarItemInfoMake(index, view, label, minWidth) forKey:[item hash]];
|
||||
[_itemInfos setObject:_CPToolbarItemInfoMake(index, view, label, minWidth) forKey:[item UID]];
|
||||
|
||||
_minWidth += minWidth + TOOLBAR_ITEM_MARGIN;
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the target of the action that is triggered when the user clicks this item. <code>nil</code> will cause
|
||||
Sets the target of the action that is triggered when the user clicks this item. \c nil will cause
|
||||
the action to be passed on to the first responder.
|
||||
@param aTarget the new target
|
||||
*/
|
||||
@@ -261,7 +261,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the item is enabled.
|
||||
Returns \c YES if the item is enabled.
|
||||
*/
|
||||
- (BOOL)isEnabled
|
||||
{
|
||||
@@ -273,7 +273,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
|
||||
/*!
|
||||
Sets whether the item is enabled.
|
||||
@param aFlag <code>YES</code> enables the item
|
||||
@param aFlag \c YES enables the item
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)shouldBeEnabled
|
||||
{
|
||||
@@ -458,15 +458,16 @@ CPToolbarItemVisibilityPriorityUser
|
||||
[copy setLabel:_label];
|
||||
[copy setPaletteLabel:_paletteLabel];
|
||||
[copy setToolTip:[self toolTip]];
|
||||
|
||||
|
||||
[copy setTag:[self tag]];
|
||||
[copy setTarget:[self target]];
|
||||
[copy setAction:[self action]];
|
||||
|
||||
[copy setEnabled:[self isEnabled]];
|
||||
[copy setImage:_image];
|
||||
[copy setAlternateImage:_alternateImage];
|
||||
|
||||
|
||||
[copy setImage:[self image]];
|
||||
[copy setAlternateImage:[self alternateImage]];
|
||||
|
||||
[copy setMinSize:_minSize];
|
||||
[copy setMaxSize:_maxSize];
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
@implementation CPTreeNode : CPObject
|
||||
{
|
||||
id _representedObject @accessors(readonly, property=representedObject);
|
||||
|
||||
CPTreeNode _parentNode @accessors(readonly, property=parentNode);
|
||||
CPMutableArray _childNodes @accessors(readonly, property=childNodes);
|
||||
}
|
||||
|
||||
+ (id)treeNodeWithRepresentedObject:(id)anObject
|
||||
{
|
||||
return [[self alloc] initWithRepresentedObject:anObject];
|
||||
}
|
||||
|
||||
- (id)initWithRepresentedObject:(id)anObject
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_representedObject = anObject;
|
||||
_childNodes = [];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isLeaf
|
||||
{
|
||||
return [_childNodes count] <= 0;
|
||||
}
|
||||
|
||||
- (CPMutableArray)mutableChildNodes
|
||||
{
|
||||
return [self mutableArrayValueForKey:@"childNodes"];
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)anObject inChildNodesAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
anObject._parentNode = self;
|
||||
|
||||
[_childNodes addObject:anObject];
|
||||
}
|
||||
|
||||
- (void)removeObjectFromChildNodesAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
anObject._parentNode = nil;
|
||||
|
||||
[_childNodes removeObjectAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)replaceObjectFromChildNodesAtIndex:(CPInteger)anIndex withObject:(id)anObject
|
||||
{
|
||||
var oldObject = [_childNodes objectAtIndex:anIndex];
|
||||
|
||||
oldObject._parentNode = nil;
|
||||
|
||||
[_childNodes replaceObjectAtIndex:anIndex withObject:anObject];
|
||||
}
|
||||
|
||||
- (id)objectInChildNodesAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
return _childNodes[anIndex];
|
||||
}
|
||||
|
||||
- (void)sortWithSortDescriptors:(CPArray)sortDescriptors recursively:(BOOL)shouldSortRecursively
|
||||
{
|
||||
[_childNodes sortUsingDescriptors:sortDescriptors];
|
||||
|
||||
if (!shouldSortRecursively)
|
||||
return;
|
||||
|
||||
var count = [_childNodes count];
|
||||
|
||||
while (count--)
|
||||
[_childNodes[count] sortWithSortDescriptors:sortDescriptors recursively:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -27,11 +27,11 @@
|
||||
@import "CGGeometry.j"
|
||||
|
||||
@import "CPColor.j"
|
||||
@import "CPDOMDisplayServer.j"
|
||||
@import "CPGeometry.j"
|
||||
@import "CPGraphicsContext.j"
|
||||
@import "CPResponder.j"
|
||||
@import "CPTheme.j"
|
||||
@import "_CPDisplayServer.j"
|
||||
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
@@ -94,12 +94,13 @@ var DOMElementPrototype = nil,
|
||||
BackgroundTrivialColor = 0,
|
||||
BackgroundVerticalThreePartImage = 1,
|
||||
BackgroundHorizontalThreePartImage = 2,
|
||||
BackgroundNinePartImage = 3,
|
||||
|
||||
CustomDrawRectViews = {},
|
||||
CustomLayoutSubviewsViews = {};
|
||||
BackgroundNinePartImage = 3;
|
||||
#endif
|
||||
|
||||
var CPViewFlags = { },
|
||||
CPViewHasCustomDrawRect = 1 << 0,
|
||||
CPViewHasCustomLayoutSubviews = 1 << 1;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPView
|
||||
@@ -113,7 +114,7 @@ var DOMElementPrototype = nil,
|
||||
headed by the window's content view. Every other view in a window is a descendant
|
||||
of this view.</p>
|
||||
|
||||
<p>Subclasses can override <code>-drawRect:</code> in order to implement their
|
||||
<p>Subclasses can override \c -drawRect: in order to implement their
|
||||
appearance. Other methods of CPView and CPResponder can
|
||||
also be overridden to handle user generated events.
|
||||
*/
|
||||
@@ -143,8 +144,6 @@ var DOMElementPrototype = nil,
|
||||
BOOL _postsBoundsChangedNotifications;
|
||||
BOOL _inhibitFrameAndBoundsChangedNotifications;
|
||||
|
||||
CPString _displayHash;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
DOMElement _DOMElement;
|
||||
DOMElement _DOMContentsElement;
|
||||
@@ -180,9 +179,14 @@ var DOMElementPrototype = nil,
|
||||
JSObject _themeAttributes;
|
||||
unsigned _themeState;
|
||||
|
||||
JSObject _ephemeralSubviewsForNames;
|
||||
CPSet _ephereralSubviews;
|
||||
|
||||
// Key View Support
|
||||
CPView _nextKeyView;
|
||||
CPView _previousKeyView;
|
||||
|
||||
unsigned _viewClassFlags;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -208,6 +212,32 @@ var DOMElementPrototype = nil,
|
||||
CachedNotificationCenter = [CPNotificationCenter defaultCenter];
|
||||
}
|
||||
|
||||
- (void)setupViewFlags
|
||||
{
|
||||
var theClass = [self class],
|
||||
classUID = [theClass UID];
|
||||
|
||||
if (CPViewFlags[classUID] === undefined)
|
||||
{
|
||||
var flags = 0;
|
||||
|
||||
if ([theClass instanceMethodForSelector:@selector(drawRect:)] !== [CPView instanceMethodForSelector:@selector(drawRect:)])
|
||||
flags |= CPViewHasCustomDrawRect;
|
||||
|
||||
if ([theClass instanceMethodForSelector:@selector(layoutSubviews)] !== [CPView instanceMethodForSelector:@selector(layoutSubviews)])
|
||||
flags |= CPViewHasCustomLayoutSubviews;
|
||||
|
||||
CPViewFlags[classUID] = flags;
|
||||
}
|
||||
|
||||
_viewClassFlags = CPViewFlags[classUID];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingFrame
|
||||
{
|
||||
return [CPSet setWithObjects:@"frameOrigin", @"frameSize"];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithFrame:CGRectMakeZero()];
|
||||
@@ -242,11 +272,9 @@ var DOMElementPrototype = nil,
|
||||
_isHidden = NO;
|
||||
_hitTests = YES;
|
||||
|
||||
_displayHash = [self hash];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement = DOMElementPrototype.cloneNode(false);
|
||||
|
||||
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, _CGRectGetMinX(aFrame), _CGRectGetMinY(aFrame));
|
||||
CPDOMDisplayServerSetStyleSize(_DOMElement, width, height);
|
||||
|
||||
@@ -257,6 +285,8 @@ var DOMElementPrototype = nil,
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
|
||||
[self setupViewFlags];
|
||||
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
@@ -299,10 +329,10 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Makes <code>aSubview</code> a subview of the receiver. It is positioned relative to <code>anotherView</code>
|
||||
Makes \c aSubview a subview of the receiver. It is positioned relative to \c anotherView
|
||||
@param aSubview the view to add as a subview
|
||||
@param anOrderingMode specifies <code>aSubview</code>'s ordering relative to <code>anotherView</code>
|
||||
@param anotherView <code>aSubview</code> will be positioned relative to this argument
|
||||
@param anOrderingMode specifies \c aSubview's ordering relative to \c anotherView
|
||||
@param anotherView \c aSubview will be positioned relative to this argument
|
||||
*/
|
||||
- (void)addSubview:(CPView)aSubview positioned:(CPWindowOrderingMode)anOrderingMode relativeTo:(CPView)anotherView
|
||||
{
|
||||
@@ -390,7 +420,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Called when the receiver has added <code>aSubview</code> to it's child views.
|
||||
Called when the receiver has added \c aSubview to it's child views.
|
||||
@param aSubview the view that was added
|
||||
*/
|
||||
- (void)didAddSubview:(CPView)aSubview
|
||||
@@ -474,7 +504,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the receiver is, or is a descendant of, <code>aView</code>.
|
||||
Returns \c YES if the receiver is, or is a descendant of, \c aView.
|
||||
@param aView the view to test for ancestry
|
||||
*/
|
||||
- (BOOL)isDescendantOf:(CPView)aView
|
||||
@@ -532,7 +562,7 @@ var DOMElementPrototype = nil,
|
||||
|
||||
/*!
|
||||
Returns the menu item containing the receiver or one of its ancestor views.
|
||||
@return a menu item, or <code>nil</code> if the view or one of its ancestors wasn't found
|
||||
@return a menu item, or \c nil if the view or one of its ancestors wasn't found
|
||||
*/
|
||||
- (CPMenuItem)enclosingMenuItem
|
||||
{
|
||||
@@ -585,7 +615,7 @@ var DOMElementPrototype = nil,
|
||||
|
||||
/*!
|
||||
Returns whether the view is flipped.
|
||||
@return <code>YES</code> if the view is flipped. <code>NO</code>, otherwise.
|
||||
@return \c YES if the view is flipped. \c NO, otherwise.
|
||||
*/
|
||||
- (BOOL)isFlipped
|
||||
{
|
||||
@@ -624,6 +654,16 @@ var DOMElementPrototype = nil,
|
||||
return _CGRectMakeCopy(_frame);
|
||||
}
|
||||
|
||||
- (CGPoint)frameOrigin
|
||||
{
|
||||
return _CGPointMakeCopy(_frame.origin);
|
||||
}
|
||||
|
||||
- (CGSize)frameSize
|
||||
{
|
||||
return _CGSizeMakeCopy(_frame.size);
|
||||
}
|
||||
|
||||
/*!
|
||||
Moves the center of the receiver's frame to the provided point. The point is defined in the superview's coordinate system.
|
||||
The method posts a CPViewFrameDidChangeNotification to the default notification center if the receiver
|
||||
@@ -666,12 +706,14 @@ var DOMElementPrototype = nil,
|
||||
[CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, _superview ? _superview._boundsTransform : NULL, origin.x, origin.y);
|
||||
var transform = _superview ? _superview._boundsTransform : NULL;
|
||||
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, transform, origin.x, origin.y);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the receiver's frame size. If <code>aSize</code> is the same as the frame's current dimensions, this
|
||||
Sets the receiver's frame size. If \c aSize is the same as the frame's current dimensions, this
|
||||
method simply returns. The method posts a CPViewFrameDidChangeNotification to the
|
||||
default notification center if the receiver is configured to do so.
|
||||
@param aSize the new size for the frame
|
||||
@@ -888,7 +930,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Initiates <code>superviewSizeChanged:</code> messages to subviews.
|
||||
Initiates \c -superviewSizeChanged: messages to subviews.
|
||||
@param aSize the size for the subviews
|
||||
*/
|
||||
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
@@ -901,9 +943,9 @@ var DOMElementPrototype = nil,
|
||||
|
||||
/*!
|
||||
Specifies whether the receiver view should automatically resize its
|
||||
subviews when its <code>setFrameSize:</code> method receives a change.
|
||||
@param aFlag If <code>YES</code>, then subviews will automatically be resized
|
||||
when this view is resized. <code>NO</code> means the views will not
|
||||
subviews when its \c -setFrameSize: method receives a change.
|
||||
@param aFlag If \c YES, then subviews will automatically be resized
|
||||
when this view is resized. \c NO means the views will not
|
||||
be resized automatically.
|
||||
*/
|
||||
- (void)setAutoresizesSubviews:(BOOL)aFlag
|
||||
@@ -913,7 +955,7 @@ var DOMElementPrototype = nil,
|
||||
|
||||
/*!
|
||||
Reports whether the receiver automatically resizes its subviews when its frame size changes.
|
||||
@return <code>YES</code> means it resizes its subviews on a frame size change.
|
||||
@return \c YES means it resizes its subviews on a frame size change.
|
||||
*/
|
||||
- (BOOL)autoresizesSubviews
|
||||
{
|
||||
@@ -956,7 +998,7 @@ var DOMElementPrototype = nil,
|
||||
{
|
||||
_fullScreenModeState = _CPViewFullScreenModeStateMake(self);
|
||||
|
||||
var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds] styleMask:CPBorderlessWindowMask];
|
||||
var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPPlatformWindow primaryPlatformWindow] contentBounds] styleMask:CPBorderlessWindowMask];
|
||||
|
||||
[fullScreenWindow setLevel:CPScreenSaverWindowLevel];
|
||||
[fullScreenWindow setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
@@ -1005,7 +1047,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the receiver is currently in full screen mode.
|
||||
Returns \c YES if the receiver is currently in full screen mode.
|
||||
*/
|
||||
- (BOOL)isInFullScreenMode
|
||||
{
|
||||
@@ -1014,7 +1056,7 @@ var DOMElementPrototype = nil,
|
||||
|
||||
/*!
|
||||
Sets whether the receiver should be hidden.
|
||||
@param aFlag <code>YES</code> makes the receiver hidden.
|
||||
@param aFlag \c YES makes the receiver hidden.
|
||||
*/
|
||||
- (void)setHidden:(BOOL)aFlag
|
||||
{
|
||||
@@ -1050,7 +1092,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the receiver is hidden.
|
||||
Returns \c YES if the receiver is hidden.
|
||||
*/
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
@@ -1094,8 +1136,8 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the receiver is hidden, or one
|
||||
of it's ancestor views is hidden. <code>NO</code>, otherwise.
|
||||
Returns \c YES if the receiver is hidden, or one
|
||||
of it's ancestor views is hidden. \c NO, otherwise.
|
||||
*/
|
||||
- (BOOL)isHiddenOrHasHiddenAncestor
|
||||
{
|
||||
@@ -1108,9 +1150,9 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns whether the receiver should be sent a <code>mouseDown:</code> message for <code>anEvent</code>.<br/>
|
||||
Returns <code>YES</code> by default.
|
||||
@return <code>YES</code>, if the view object accepts first mouse-down event. <code>NO</code>, otherwise.
|
||||
Returns whether the receiver should be sent a \c -mouseDown: message for \c anEvent.<br/>
|
||||
Returns \c YES by default.
|
||||
@return \c YES, if the view object accepts first mouse-down event. \c NO, otherwise.
|
||||
*/
|
||||
//FIXME: should be NO by default?
|
||||
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
|
||||
@@ -1120,7 +1162,7 @@ var DOMElementPrototype = nil,
|
||||
|
||||
/*!
|
||||
Returns whether or not the view responds to hit tests.
|
||||
@return <code>YES</code> if this view listens to hitTest messages, <code>NO</code> otherwise.
|
||||
@return \c YES if this view listens to \c -hitTest messages, \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)hitTests
|
||||
{
|
||||
@@ -1129,7 +1171,7 @@ var DOMElementPrototype = nil,
|
||||
|
||||
/*!
|
||||
Set whether or not the view should respond to hit tests.
|
||||
@param shouldHitTest should be <code>YES</code> if this view should respond to hit tests, <code>NO</code> otherwise.
|
||||
@param shouldHitTest should be \c YES if this view should respond to hit tests, \c NO otherwise.
|
||||
*/
|
||||
- (void)setHitTests:(BOOL)shouldHitTest
|
||||
{
|
||||
@@ -1161,8 +1203,8 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if mouse events aren't needed by the receiver and can be sent to the superview. The
|
||||
default implementation returns <code>NO</code> if the view is opaque.
|
||||
Returns \c YES if mouse events aren't needed by the receiver and can be sent to the superview. The
|
||||
default implementation returns \c NO if the view is opaque.
|
||||
*/
|
||||
- (BOOL)mouseDownCanMoveWindow
|
||||
{
|
||||
@@ -1208,7 +1250,7 @@ var DOMElementPrototype = nil,
|
||||
|
||||
amount = 0 - _DOMImageParts.length;
|
||||
}
|
||||
|
||||
|
||||
if (amount > 0)
|
||||
while (amount--)
|
||||
{
|
||||
@@ -1236,16 +1278,16 @@ var DOMElementPrototype = nil,
|
||||
else
|
||||
{
|
||||
var slices = [patternImage imageSlices],
|
||||
count = slices.length,
|
||||
count = MIN(_DOMImageParts.length, slices.length),
|
||||
frameSize = _frame.size;
|
||||
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var image = slices[count],
|
||||
size = _DOMImageSizes[count] = image ? [image size] : _CGSizeMakeZero();
|
||||
|
||||
CPDOMDisplayServerSetStyleSize(_DOMImageParts[count], size.width, size.height);
|
||||
|
||||
|
||||
_DOMImageParts[count].style.background = image ? "url(\"" + [image filename] + "\")" : "";
|
||||
}
|
||||
|
||||
@@ -1300,7 +1342,7 @@ var DOMElementPrototype = nil,
|
||||
|
||||
// Converting Coordinates
|
||||
/*!
|
||||
Converts <code>aPoint</code> from the coordinate space of <code>aView</code> to the coordinate space of the receiver.
|
||||
Converts \c aPoint from the coordinate space of \c aView to the coordinate space of the receiver.
|
||||
@param aPoint the point to convert
|
||||
@param aView the view space to convert from
|
||||
@return the converted point
|
||||
@@ -1311,7 +1353,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Converts <code>aPoint</code> from the receiver's coordinate space to the coordinate space of <code>aView</code>.
|
||||
Converts \c aPoint from the receiver's coordinate space to the coordinate space of \c aView.
|
||||
@param aPoint the point to convert
|
||||
@param aView the coordinate space to which the point will be converted
|
||||
@return the converted point
|
||||
@@ -1322,7 +1364,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert's <code>aSize</code> from <code>aView</code>'s coordinate space to the receiver's coordinate space.
|
||||
Convert's \c aSize from \c aView's coordinate space to the receiver's coordinate space.
|
||||
@param aSize the size to convert
|
||||
@param aView the coordinate space to convert from
|
||||
@return the converted size
|
||||
@@ -1333,7 +1375,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert's <code>aSize</code> from the receiver's coordinate space to <code>aView</code>'s coordinate space.
|
||||
Convert's \c aSize from the receiver's coordinate space to \c aView's coordinate space.
|
||||
@param aSize the size to convert
|
||||
@param the coordinate space to which the size will be converted
|
||||
@return the converted size
|
||||
@@ -1344,7 +1386,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Converts <code>aRect</code> from <code>aView</code>'s coordinate space to the receiver's space.
|
||||
Converts \c aRect from \c aView's coordinate space to the receiver's space.
|
||||
@param aRect the rectangle to convert
|
||||
@param aView the coordinate space from which to convert
|
||||
@return the converted rectangle
|
||||
@@ -1355,7 +1397,7 @@ var DOMElementPrototype = nil,
|
||||
}
|
||||
|
||||
/*!
|
||||
Converts <code>aRect</code> from the receiver's coordinate space to <code>aView</code>'s coordinate space.
|
||||
Converts \c aRect from the receiver's coordinate space to \c aView's coordinate space.
|
||||
@param aRect the rectangle to convert
|
||||
@param aView the coordinate space to which the rectangle will be converted
|
||||
@return the converted rectangle
|
||||
@@ -1367,14 +1409,14 @@ var DOMElementPrototype = nil,
|
||||
|
||||
/*!
|
||||
Sets whether the receiver posts a CPViewFrameDidChangeNotification notification
|
||||
to the default notification center when its frame is changed. The default is <code>NO</code>.
|
||||
to the default notification center when its frame is changed. The default is \c NO.
|
||||
Methods that could cause a frame change notification are:
|
||||
<pre>
|
||||
setFrame:
|
||||
setFrameSize:
|
||||
setFrameOrigin:
|
||||
</pre>
|
||||
@param shouldPostFrameChangedNotifications <code>YES</code> makes the receiver post
|
||||
@param shouldPostFrameChangedNotifications \c YES makes the receiver post
|
||||
notifications on frame changes (size or origin)
|
||||
*/
|
||||
- (void)setPostsFrameChangedNotifications:(BOOL)shouldPostFrameChangedNotifications
|
||||
@@ -1391,7 +1433,7 @@ setFrameOrigin:
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the receiver posts a CPViewFrameDidChangeNotification if its frame is changed.
|
||||
Returns \c YES if the receiver posts a CPViewFrameDidChangeNotification if its frame is changed.
|
||||
*/
|
||||
- (BOOL)postsFrameChangedNotifications
|
||||
{
|
||||
@@ -1400,14 +1442,14 @@ setFrameOrigin:
|
||||
|
||||
/*!
|
||||
Sets whether the receiver posts a CPViewBoundsDidChangeNotification notification
|
||||
to the default notification center when its bounds is changed. The default is <code>NO</code>.
|
||||
to the default notification center when its bounds is changed. The default is \c NO.
|
||||
Methods that could cause a bounds change notification are:
|
||||
<pre>
|
||||
setBounds:
|
||||
setBoundsSize:
|
||||
setBoundsOrigin:
|
||||
</pre>
|
||||
@param shouldPostBoundsChangedNotifications <code>YES</code> makes the receiver post
|
||||
@param shouldPostBoundsChangedNotifications \c YES makes the receiver post
|
||||
notifications on bounds changes
|
||||
*/
|
||||
- (void)setPostsBoundsChangedNotifications:(BOOL)shouldPostBoundsChangedNotifications
|
||||
@@ -1424,7 +1466,7 @@ setBoundsOrigin:
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the receiver posts a
|
||||
Returns \c YES if the receiver posts a
|
||||
CPViewBoundsDidChangeNotification when its
|
||||
bounds is changed.
|
||||
*/
|
||||
@@ -1436,9 +1478,9 @@ setBoundsOrigin:
|
||||
/*!
|
||||
Initiates a drag operation from the receiver to another view that accepts dragged data.
|
||||
@param anImage the image to be dragged
|
||||
@param aLocation the lower-left corner coordinate of <code>anImage</code>
|
||||
@param mouseOffset the distance from the <code>mouseDown:</code> location and the current location
|
||||
@param anEvent the <code>mouseDown:</code> that triggered the drag
|
||||
@param aLocation the lower-left corner coordinate of \c anImage
|
||||
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
||||
@param anEvent the \c -mouseDown: that triggered the drag
|
||||
@param aPastebaord the pasteboard that holds the drag data
|
||||
@param aSourceObject the drag operation controller
|
||||
@param slideBack Whether the image should 'slide back' if the drag is rejected
|
||||
@@ -1451,9 +1493,9 @@ setBoundsOrigin:
|
||||
/*!
|
||||
Initiates a drag operation from the receiver to another view that accepts dragged data.
|
||||
@param aView the view to be dragged
|
||||
@param aLocation the top-left corner coordinate of <code>aView</code>
|
||||
@param mouseOffset the distance from the <code>mouseDown:</code> location and the current location
|
||||
@param anEvent the <code>mouseDown:</code> that triggered the drag
|
||||
@param aLocation the top-left corner coordinate of \c aView
|
||||
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
||||
@param anEvent the \c -mouseDown: that triggered the drag
|
||||
@param aPastebaord the pasteboard that holds the drag data
|
||||
@param aSourceObject the drag operation controller
|
||||
@param slideBack Whether the view should 'slide back' if the drag is rejected
|
||||
@@ -1469,7 +1511,7 @@ setBoundsOrigin:
|
||||
*/
|
||||
- (void)registerForDraggedTypes:(CPArray)pasteboardTypes
|
||||
{
|
||||
if (!pasteboardTypes)
|
||||
if (!pasteboardTypes || ![pasteboardTypes count])
|
||||
return;
|
||||
|
||||
var theWindow = [self window];
|
||||
@@ -1505,7 +1547,7 @@ setBoundsOrigin:
|
||||
}
|
||||
|
||||
/*!
|
||||
Draws the receiver into <code>aRect</code>. This method should be overridden by subclasses.
|
||||
Draws the receiver into \c aRect. This method should be overridden by subclasses.
|
||||
@param aRect the area that should be drawn into
|
||||
*/
|
||||
- (void)drawRect:(CPRect)aRect
|
||||
@@ -1522,43 +1564,26 @@ setBoundsOrigin:
|
||||
{
|
||||
if (aFlag)
|
||||
[self setNeedsDisplayInRect:[self bounds]];
|
||||
#if PLATFORM(DOM)
|
||||
else
|
||||
CPDOMDisplayServerRemoveView(self);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Marks the area denoted by <code>aRect</code> as dirty, and initiates a redraw on it.
|
||||
Marks the area denoted by \c aRect as dirty, and initiates a redraw on it.
|
||||
@param aRect the area that needs to be redrawn
|
||||
*/
|
||||
- (void)setNeedsDisplayInRect:(CPRect)aRect
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var hash = [[self class] hash],
|
||||
hasCustomDrawRect = CustomDrawRectViews[hash];
|
||||
|
||||
if (!hasCustomDrawRect && typeof hasCustomDrawRect === "undefined")
|
||||
{
|
||||
hasCustomDrawRect = [self methodForSelector:@selector(drawRect:)] != [CPView instanceMethodForSelector:@selector(drawRect:)];
|
||||
CustomDrawRectViews[hash] = hasCustomDrawRect;
|
||||
}
|
||||
|
||||
if (!hasCustomDrawRect)
|
||||
if (!(_viewClassFlags & CPViewHasCustomDrawRect))
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
if (_CGRectIsEmpty(aRect))
|
||||
return;
|
||||
|
||||
|
||||
if (_dirtyRect && !_CGRectIsEmpty(_dirtyRect))
|
||||
_dirtyRect = CGRectUnion(aRect, _dirtyRect);
|
||||
else
|
||||
_dirtyRect = _CGRectMakeCopy(aRect);
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
CPDOMDisplayServerAddView(self);
|
||||
#endif
|
||||
_CPDisplayServerAddDisplayObject(self);
|
||||
}
|
||||
|
||||
- (BOOL)needsDisplay
|
||||
@@ -1576,7 +1601,7 @@ setBoundsOrigin:
|
||||
}
|
||||
|
||||
/*!
|
||||
Draws the entire area of the receiver as defined by its <code>bounds</code>.
|
||||
Draws the entire area of the receiver as defined by its \c -bounds.
|
||||
*/
|
||||
- (void)display
|
||||
{
|
||||
@@ -1590,7 +1615,7 @@ setBoundsOrigin:
|
||||
}
|
||||
|
||||
/*!
|
||||
Draws the receiver into the area defined by <code>aRect</code>.
|
||||
Draws the receiver into the area defined by \c aRect.
|
||||
@param aRect the area to be drawn
|
||||
*/
|
||||
- (void)displayRect:(CPRect)aRect
|
||||
@@ -1663,26 +1688,12 @@ setBoundsOrigin:
|
||||
|
||||
- (void)setNeedsLayout
|
||||
{
|
||||
_needsLayout = YES;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
var hash = [[self class] hash],
|
||||
hasCustomLayoutSubviews = CustomLayoutSubviewsViews[hash];
|
||||
|
||||
if (hasCustomLayoutSubviews === undefined)
|
||||
{
|
||||
hasCustomLayoutSubviews = [self methodForSelector:@selector(layoutSubviews)] != [CPView instanceMethodForSelector:@selector(layoutSubviews)];
|
||||
CustomLayoutSubviewsViews[hash] = hasCustomLayoutSubviews;
|
||||
}
|
||||
|
||||
if (!hasCustomLayoutSubviews)
|
||||
if (!(_viewClassFlags & CPViewHasCustomLayoutSubviews))
|
||||
return;
|
||||
|
||||
if (_needsLayout)
|
||||
{
|
||||
CPDOMDisplayServerAddView(self);
|
||||
}
|
||||
#endif
|
||||
_needsLayout = YES;
|
||||
|
||||
_CPDisplayServerAddLayoutObject(self);
|
||||
}
|
||||
|
||||
- (void)layoutIfNeeded
|
||||
@@ -1700,7 +1711,7 @@ setBoundsOrigin:
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns whether the receiver is completely opaque. By default, returns <code>NO</code>.
|
||||
Returns whether the receiver is completely opaque. By default, returns \c NO.
|
||||
*/
|
||||
- (BOOL)isOpaque
|
||||
{
|
||||
@@ -1732,7 +1743,7 @@ setBoundsOrigin:
|
||||
}
|
||||
|
||||
/*!
|
||||
Changes the receiver's frame origin to a 'constrained' <code>aPoint</code>.
|
||||
Changes the receiver's frame origin to a 'constrained' \c aPoint.
|
||||
@param aPoint the proposed frame origin
|
||||
*/
|
||||
- (void)scrollPoint:(CGPoint)aPoint
|
||||
@@ -1746,9 +1757,9 @@ setBoundsOrigin:
|
||||
}
|
||||
|
||||
/*!
|
||||
Scrolls the nearest ancestor CPClipView a minimum amount so <code>aRect</code> can become visible.
|
||||
Scrolls the nearest ancestor CPClipView a minimum amount so \c aRect can become visible.
|
||||
@param aRect the area to become visible
|
||||
@return <codeYES</code> if any scrolling occurred, <code>NO</code> otherwise.
|
||||
@return <codeYES if any scrolling occurred, \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)scrollRectToVisible:(CGRect)aRect
|
||||
{
|
||||
@@ -1942,7 +1953,7 @@ setBoundsOrigin:
|
||||
|
||||
/*!
|
||||
Sets whether the receiver wants a core animation layer.
|
||||
@param <code>YES</code> means the receiver wants a layer.
|
||||
@param \c YES means the receiver wants a layer.
|
||||
*/
|
||||
- (void)setWantsLayer:(BOOL)aFlag
|
||||
{
|
||||
@@ -1950,8 +1961,8 @@ setBoundsOrigin:
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the receiver uses a CALayer
|
||||
@returns <code>YES</code> if the receiver uses a CALayer
|
||||
Returns \c YES if the receiver uses a CALayer
|
||||
@returns \c YES if the receiver uses a CALayer
|
||||
*/
|
||||
- (BOOL)wantsLayer
|
||||
{
|
||||
@@ -2184,6 +2195,54 @@ setBoundsOrigin:
|
||||
return [_themeAttributes[aName] valueForState:_themeState];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aViewName
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
|
||||
{
|
||||
return _CGRectMakeZero();
|
||||
}
|
||||
|
||||
- (CPView)layoutEphemeralSubviewNamed:(CPString)aViewName
|
||||
positioned:(CPWindowOrderingMode)anOrderingMode
|
||||
relativeToEphemeralSubviewNamed:(CPString)relativeToViewName
|
||||
{
|
||||
if (!_ephemeralSubviewsForNames)
|
||||
{
|
||||
_ephemeralSubviewsForNames = {};
|
||||
_ephemeralSubviews = [CPSet set];
|
||||
}
|
||||
|
||||
var frame = [self rectForEphemeralSubviewNamed:aViewName];
|
||||
|
||||
if (frame && !_CGRectIsEmpty(frame))
|
||||
{
|
||||
if (!_ephemeralSubviewsForNames[aViewName])
|
||||
{
|
||||
_ephemeralSubviewsForNames[aViewName] = [self createEphemeralSubviewNamed:aViewName];
|
||||
|
||||
[_ephemeralSubviews addObject:_ephemeralSubviewsForNames[aViewName]];
|
||||
|
||||
if (_ephemeralSubviewsForNames[aViewName])
|
||||
[self addSubview:_ephemeralSubviewsForNames[aViewName] positioned:anOrderingMode relativeTo:_ephemeralSubviewsForNames[relativeToViewName]];
|
||||
}
|
||||
|
||||
if (_ephemeralSubviewsForNames[aViewName])
|
||||
[_ephemeralSubviewsForNames[aViewName] setFrame:frame];
|
||||
}
|
||||
else if (_ephemeralSubviewsForNames[aViewName])
|
||||
{
|
||||
[_ephemeralSubviewsForNames[aViewName] removeFromSuperview];
|
||||
|
||||
[_ephemeralSubviews removeObject:_ephemeralSubviewsForNames[aViewName]];
|
||||
delete _ephemeralSubviewsForNames[aViewName];
|
||||
}
|
||||
|
||||
return _ephemeralSubviewsForNames[aViewName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
@@ -2234,6 +2293,10 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
_subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey] || [];
|
||||
_superview = [aCoder decodeObjectForKey:CPViewSuperviewKey];
|
||||
|
||||
// FIXME: Should we encode/decode this?
|
||||
_registeredDraggedTypes = [CPSet set];
|
||||
_registeredDraggedTypesArray = [];
|
||||
|
||||
_autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey] || CPViewNotSizable;
|
||||
_autoresizesSubviews = ![aCoder containsValueForKey:CPViewAutoresizesSubviewsKey] || [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey];
|
||||
|
||||
@@ -2256,7 +2319,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
//_subviews[index]._superview = self;
|
||||
}
|
||||
#endif
|
||||
_displayHash = [self hash];
|
||||
|
||||
if ([aCoder containsValueForKey:CPViewIsHiddenKey])
|
||||
[self setHidden:[aCoder decodeBoolForKey:CPViewIsHiddenKey]];
|
||||
@@ -2270,6 +2332,8 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
|
||||
[self setBackgroundColor:[aCoder decodeObjectForKey:CPViewBackgroundColorKey]];
|
||||
|
||||
[self setupViewFlags];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeState([aCoder decodeIntForKey:CPViewThemeStateKey]);
|
||||
_themeAttributes = {};
|
||||
@@ -2287,6 +2351,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
}
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -2310,8 +2375,20 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
if (_window !== nil)
|
||||
[aCoder encodeConditionalObject:_window forKey:CPViewWindowKey];
|
||||
|
||||
if (_subviews.length > 0)
|
||||
[aCoder encodeObject:_subviews forKey:CPViewSubviewsKey];
|
||||
var count = [_subviews count],
|
||||
encodedSubviews = _subviews;
|
||||
|
||||
if (count > 0 && [_ephemeralSubviews count] > 0)
|
||||
{
|
||||
encodedSubviews = [encodedSubviews copy];
|
||||
|
||||
while (count--)
|
||||
if ([_ephemeralSubviews containsObject:encodedSubviews[count]])
|
||||
encodedSubviews.splice(count, 1);
|
||||
}
|
||||
|
||||
if (encodedSubviews.length > 0)
|
||||
[aCoder encodeObject:encodedSubviews forKey:CPViewSubviewsKey];
|
||||
|
||||
// This will come out nil on the other side with decodeObjectForKey:
|
||||
if (_superview !== nil)
|
||||
@@ -2372,6 +2449,7 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView)
|
||||
{
|
||||
var view = fromView;
|
||||
|
||||
// FIXME: This doesn't handle the case when the outside views are equal.
|
||||
// If we have a fromView, "climb up" the view tree until
|
||||
// we hit the root node or we hit the toLayer.
|
||||
while (view && view != toView)
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* CPViewController.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Nicholas Small and Francisco Tolmasky.
|
||||
* Copyright 2009, 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/CPResponder.j>
|
||||
|
||||
|
||||
/*! @class CPViewController
|
||||
The CPViewController class provides the fundamental view-management controller for Cappuccino applications.
|
||||
The basic view controller class supports the presentation of an associated view in addition to basic support
|
||||
for managing modal views and, in the future, animations. Subclasses such as CPNavigationController and
|
||||
CPTabBarController provide additional behavior for managing complex hierarchies of view controllers and views.
|
||||
|
||||
You use each instance of CPViewController to manage a single view (and hierarchy). For a simple view controller,
|
||||
this entails managing the view hierarchy responsible for presenting your application content.
|
||||
A typical view hierarchy consists of a root viewÑa reference to which is available in the view property of this classÑ
|
||||
and one or more subviews presenting the actual content. In the case of navigation and tab bar controllers, the view
|
||||
controller manages not only the high-level view hierarchy (which provides the navigation controls) but also one
|
||||
or more additional view controllers that handle the presentation of the application content.
|
||||
|
||||
Unlike UIViewController in Cocoa Touch, a CPViewController does not represent an entire screen of content. You
|
||||
will add your root view to an existing view or window's content view. You can manage many view controllers
|
||||
on screen at once. CPViewController is also the preferred way of working with Cibs.
|
||||
|
||||
Subclasses can override -loadView to create their custom view hierarchy, or specify a cib name to be loaded automatically.
|
||||
It has methods that are called when a view appears or disappears.
|
||||
This class is also a good place for delegate & datasource methods, and other controller stuff.
|
||||
*/
|
||||
@implementation CPViewController : CPResponder
|
||||
{
|
||||
CPView _view;
|
||||
|
||||
id _representedObject @accessors(property=representedObject);
|
||||
CPString _title @accessors(property=title);
|
||||
|
||||
CPString _cibName @accessors(property=cibName, readonly);
|
||||
CPBundle _cibBundle @accessors(property=cibBundle, readonly);
|
||||
CPDictionary _cibExternalNameTable @accessors(property=cibExternalNameTable, readonly);
|
||||
}
|
||||
|
||||
/*!
|
||||
Convenience initializer calls -initWithCibName:bundle: with nil for both parameters.
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithCibName:nil bundle:nil];
|
||||
}
|
||||
|
||||
- (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil
|
||||
{
|
||||
return [self initWithCibName:aCibNameOrNil bundle:aCibBundleOrNil externalNameTable:nil];
|
||||
}
|
||||
|
||||
- (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil owner:(id)anOwner
|
||||
{
|
||||
return [self initWithCibName:aCibNameOrNil bundle:aCibBundleOrNil externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]];
|
||||
}
|
||||
|
||||
/*!
|
||||
The designated initializer. If you subclass CPViewController, you must call the super implementation of this method, even if you aren't using a Cib.
|
||||
In the specified Cib, the File's Owner proxy should have its class set to your view controller subclass, with the view outlet connected to the main view.
|
||||
If you pass in a nil Cib name, then you must either call -setView: before -view is invoked, or override -loadView to set up your views.
|
||||
|
||||
@param cibNameOrNil The path to the cib to load for the root view or nil to programmatically create views.
|
||||
@param cibBundleOrNil The bundle that the cib is located in or nil for the main bundle.
|
||||
*/
|
||||
- (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil externalNameTable:(CPDictionary)anExternalNameTable
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
// Don't load the cib until someone actually requests the view. The user may just be intending to use setView:.
|
||||
_cibName = aCibNameOrNil;
|
||||
_cibBundle = aCibBundleOrNil || [CPBundle mainBundle];
|
||||
_cibExternalNameTable = anExternalNameTable || [CPDictionary dictionaryWithObject:self forKey:CPCibOwner];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Programmatically creates the view that the controller manages.
|
||||
You should never call this method directly. The view controller calls this method when the view property is requested but is nil.
|
||||
|
||||
If you create your views manually, you must override this method and use it to create your view and assign it to the view property.
|
||||
The default implementation for programmatic views is to create a plain view. You can invoke super to utilize this view.
|
||||
|
||||
If you use Interface Builder to create your views and initialize the view controllerÑthat is, you initialize the view using the
|
||||
initWithCibName:bundle: methodÑthen you must not override this method. The consequences risk shattering the space-time continuum.
|
||||
|
||||
Note: The cib loading system is currently asynchronous.
|
||||
*/
|
||||
- (void)loadView
|
||||
{
|
||||
if (_view)
|
||||
return;
|
||||
|
||||
// if (_cibName)
|
||||
// [CPException raise: reason:];
|
||||
|
||||
var cib = [[CPCib alloc] initWithContentsOfURL:[_cibBundle pathForResource:_cibName + @".cib"]];
|
||||
|
||||
[cib instantiateCibWithExternalNameTable:_cibExternalNameTable];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the view that the controller manages.
|
||||
If this property is nil, the controller sends loadView to itself to create the view that it manages.
|
||||
Subclasses should override the loadView method to create any custom views. The default value is nil.
|
||||
|
||||
Note: An error will not be thrown if after -loadView, the view property is still nil. -view will simply return nil, but will continue to call -loadView on subsequent calls.
|
||||
*/
|
||||
- (CPView)view
|
||||
{
|
||||
if (!_view)
|
||||
{
|
||||
var cibOwner = [_cibExternalNameTable objectForKey:CPCibOwner];
|
||||
|
||||
if ([cibOwner respondsToSelector:@selector(viewControllerWillLoadCib:)])
|
||||
[cibOwner viewControllerWillLoadCib:self];
|
||||
|
||||
[self loadView];
|
||||
|
||||
if (_view === nil && [cibOwner isKindOfClass:[CPDocument class]])
|
||||
[self setView:[cibOwner valueForKey:@"view"]];
|
||||
|
||||
if ([cibOwner respondsToSelector:@selector(viewControllerDidLoadCib:)])
|
||||
[cibOwner viewControllerDidLoadCib:self];
|
||||
}
|
||||
|
||||
return _view;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Manually sets the view that the controller manages.
|
||||
Setting to nil will cause -loadView to be called on all subsequent calls of -view.
|
||||
|
||||
@param aView The view this controller should represent.
|
||||
*/
|
||||
- (void)setView:(CPView)aView
|
||||
{
|
||||
_view = aView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPViewControllerViewKey = @"CPViewControllerViewKey",
|
||||
CPViewControllerTitleKey = @"CPViewControllerTitleKey";
|
||||
|
||||
@implementation CPViewController (CPCoding)
|
||||
|
||||
/*!
|
||||
Initializes the view item by unarchiving data from a coder.
|
||||
@param aCoder the coder from which the data will be unarchived
|
||||
@return the initialized collection view item
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_view = [aCoder decodeObjectForKey:CPViewControllerViewKey];
|
||||
_title = [aCoder decodeObjectForKey:CPViewControllerTitleKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Archives the colletion view item to the provided coder.
|
||||
@param aCoder the coder to which the view item should be archived
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_view forKey:CPViewControllerViewKey];
|
||||
[aCoder encodeObject:_title forKey:CPViewControllerTitleKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -56,7 +56,7 @@ var _CPTexturedWindowHeadGradientColor = nil,
|
||||
+ (CPColor)solidColor
|
||||
{
|
||||
if (!_CPTexturedWindowHeadSolidColor)
|
||||
_CPTexturedWindowHeadSolidColor = [CPColor colorWithCalibratedRed:182.0 / 255.0 green:182.0 / 255.0 blue:182.0 / 255.0 alpha:1.0];
|
||||
_CPTexturedWindowHeadSolidColor = [CPColor colorWithCalibratedRed:195.0 / 255.0 green:195.0 / 255.0 blue:195.0 / 255.0 alpha:1.0];
|
||||
|
||||
return _CPTexturedWindowHeadSolidColor;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
var theWindow = [self window];
|
||||
|
||||
|
||||
if ((_styleMask & CPResizableWindowMask) && _resizeIndicator)
|
||||
{
|
||||
// FIXME: This should be better
|
||||
@@ -142,10 +142,14 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
|
||||
- (CGPoint)_pointWithinScreenFrame:(CGPoint)aPoint
|
||||
{
|
||||
// FIXME: this is WRONG, all of this is WRONG
|
||||
if (![CPPlatform isBrowser])
|
||||
return aPoint;
|
||||
|
||||
var visibleFrame = _cachedScreenFrame;
|
||||
|
||||
if (!visibleFrame)
|
||||
visibleFrame = [[CPDOMWindowBridge sharedDOMWindowBridge] visibleFrame];
|
||||
visibleFrame = [[CPPlatformWindow primaryPlatformWindow] visibleFrame];
|
||||
|
||||
var restrictedPoint = CGPointMake(0, 0);
|
||||
|
||||
@@ -158,22 +162,24 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
- (void)trackMoveWithEvent:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type];
|
||||
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
_cachedScreenFrame = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
else if (type === CPLeftMouseDown)
|
||||
{
|
||||
_mouseDraggedPoint = [[self window] convertBaseToBridge:[anEvent locationInWindow]];
|
||||
_cachedScreenFrame = [[CPDOMWindowBridge sharedDOMWindowBridge] visibleFrame];
|
||||
_mouseDraggedPoint = [[self window] convertBaseToGlobal:[anEvent locationInWindow]];
|
||||
_cachedScreenFrame = [[CPPlatformWindow primaryPlatformWindow] visibleFrame];
|
||||
}
|
||||
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
var theWindow = [self window],
|
||||
frame = [theWindow frame],
|
||||
location = [theWindow convertBaseToBridge:[anEvent locationInWindow]],
|
||||
location = [theWindow convertBaseToGlobal:[anEvent locationInWindow]],
|
||||
origin = [self _pointWithinScreenFrame:CGPointMake(_CGRectGetMinX(frame) + (location.x - _mouseDraggedPoint.x),
|
||||
_CGRectGetMinY(frame) + (location.y - _mouseDraggedPoint.y))];
|
||||
|
||||
@@ -322,7 +328,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
{
|
||||
var contentRect = [self convertRect:[[theWindow contentView] frame] toView:nil];
|
||||
|
||||
contentRect.origin = [theWindow convertBaseToBridge:contentRect.origin];
|
||||
contentRect.origin = [theWindow convertBaseToGlobal:contentRect.origin];
|
||||
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[theWindow setFrame:[theWindow frameRectForContentRect:contentRect]];
|
||||
|
||||
@@ -41,10 +41,22 @@
|
||||
*/
|
||||
@implementation CPWindowController : CPResponder
|
||||
{
|
||||
id _owner;
|
||||
CPWindow _window;
|
||||
CPDocument _document;
|
||||
CPString _windowCibName;
|
||||
CPWindow _window;
|
||||
|
||||
CPDocument _document;
|
||||
BOOL _shouldCloseDocument;
|
||||
|
||||
id _cibOwner;
|
||||
CPString _windowCibName;
|
||||
CPString _windowCibPath;
|
||||
|
||||
CPViewController _viewController;
|
||||
CPView _viewControllerContainerView;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithWindow:nil];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -55,14 +67,15 @@
|
||||
- (id)initWithWindow:(CPWindow)aWindow
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setWindow:aWindow];
|
||||
|
||||
[self setShouldCloseDocument:NO];
|
||||
|
||||
[self setNextResponder:CPApp];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -84,16 +97,27 @@
|
||||
*/
|
||||
- (id)initWithWindowCibName:(CPString)aWindowCibName owner:(id)anOwner
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
self = [self initWithWindow:nil];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_owner = anOwner;
|
||||
_cibOwner = anOwner;
|
||||
_windowCibName = aWindowCibName;
|
||||
|
||||
[self setNextResponder:CPApp];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithWindowCibPath:(CPString)aWindowCibPath owner:(id)anOwner
|
||||
{
|
||||
self = [self initWithWindow:nil];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cibOwner = anOwner;
|
||||
_windowCibPath = aWindowCibPath;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -102,18 +126,17 @@
|
||||
*/
|
||||
- (void)loadWindow
|
||||
{
|
||||
[self windowWillLoad];
|
||||
//FIXME: ACTUALLY LOAD WINDOW!!!
|
||||
[self setWindow:CPApp._keyWindow = [[CPWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:CPBorderlessBridgeWindowMask|CPTitledWindowMask|CPClosableWindowMask|CPResizableWindowMask]];
|
||||
|
||||
[self windowDidLoad];
|
||||
if (_window)
|
||||
return;
|
||||
|
||||
[[CPBundle bundleForClass:[_cibOwner class]] loadCibFile:[self windowCibPath] externalNameTable:[CPDictionary dictionaryWithObject:_cibOwner forKey:CPCibOwner]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Shows the window.
|
||||
@param aSender the object requesting the show
|
||||
*/
|
||||
- (CFAction)showWindow:(id)aSender
|
||||
- (@action)showWindow:(id)aSender
|
||||
{
|
||||
var theWindow = [self window];
|
||||
|
||||
@@ -124,12 +147,12 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the window has been loaded. Specifically,
|
||||
Returns \c YES if the window has been loaded. Specifically,
|
||||
if loadWindow has been called.
|
||||
*/
|
||||
- (BOOL)isWindowLoaded
|
||||
{
|
||||
return _window;
|
||||
return _window !== nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -138,7 +161,20 @@
|
||||
- (CPWindow)window
|
||||
{
|
||||
if (!_window)
|
||||
[self loadWindow];
|
||||
{
|
||||
[self windowWillLoad];
|
||||
[_document windowControllerWillLoadCib:self];
|
||||
|
||||
[self loadWindow];
|
||||
|
||||
if (_window === nil && [_cibOwner isKindOfClass:[CPDocument class]])
|
||||
[self setWindow:[_cibOwner valueForKey:@"window"]];
|
||||
|
||||
[self windowDidLoad];
|
||||
[_document windowControllerDidLoadCib:self];
|
||||
|
||||
[self synchronizeWindowTitleWithDocumentName];
|
||||
}
|
||||
|
||||
return _window;
|
||||
}
|
||||
@@ -149,8 +185,10 @@
|
||||
*/
|
||||
- (void)setWindow:(CPWindow)aWindow
|
||||
{
|
||||
[_window setWindowController:nil];
|
||||
|
||||
_window = aWindow;
|
||||
|
||||
|
||||
[_window setWindowController:self];
|
||||
[_window setNextResponder:self];
|
||||
}
|
||||
@@ -160,9 +198,6 @@
|
||||
*/
|
||||
- (void)windowDidLoad
|
||||
{
|
||||
[_document windowControllerDidLoadNib:self];
|
||||
|
||||
[self synchronizeWindowTitleWithDocumentName];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -170,7 +205,6 @@
|
||||
*/
|
||||
- (void)windowWillLoad
|
||||
{
|
||||
[_document windowControllerWillLoadNib:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -179,17 +213,17 @@
|
||||
*/
|
||||
- (void)setDocument:(CPDocument)aDocument
|
||||
{
|
||||
if (_document == 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];
|
||||
@@ -198,16 +232,16 @@
|
||||
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
|
||||
@@ -217,13 +251,71 @@
|
||||
selector:@selector(_documentDidFailToSave:)
|
||||
name:CPDocumentDidFailToSaveNotification
|
||||
object:_document];
|
||||
|
||||
|
||||
[self setDocumentEdited:[_document isDocumentEdited]];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var viewController = [_document viewControllerForWindowController:self];
|
||||
|
||||
if (viewController)
|
||||
[self setViewController:viewController];
|
||||
|
||||
[self synchronizeWindowTitleWithDocumentName];
|
||||
}
|
||||
|
||||
- (void)setViewController:(CPViewController)aViewController
|
||||
{
|
||||
var containerView = [self viewControllerContainerView] || [[self window] contentView],
|
||||
view = [_viewController view],
|
||||
frame = view ? [view frame] : [containerView bounds];
|
||||
|
||||
[view removeFromSuperview];
|
||||
|
||||
_viewController = aViewController;
|
||||
|
||||
view = [_viewController view];
|
||||
|
||||
if (view)
|
||||
{
|
||||
[view setFrame:frame];
|
||||
[containerView addSubview:view];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setViewControllerContainerView:(CPView)aView
|
||||
{
|
||||
_viewControllerContainerView = aView;
|
||||
}
|
||||
|
||||
- (void)viewControllerContainerView
|
||||
{
|
||||
return _viewControllerContainerView;
|
||||
}
|
||||
|
||||
- (void)setViewController:(CPViewController)aViewController
|
||||
{
|
||||
var containerView = [self viewControllerContainerView] || [[self window] contentView],
|
||||
view = [_viewController view],
|
||||
frame = view ? [view frame] : [containerView bounds];
|
||||
|
||||
[view removeFromSuperview];
|
||||
|
||||
_viewController = aViewController;
|
||||
|
||||
view = [_viewController view];
|
||||
|
||||
if (view)
|
||||
{
|
||||
[view setFrame:frame];
|
||||
[containerView addSubview:view];
|
||||
}
|
||||
}
|
||||
|
||||
- (CPViewController)viewController
|
||||
{
|
||||
return _viewController;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_documentWillSave:(CPNotification)aNotification
|
||||
{
|
||||
@@ -252,13 +344,49 @@
|
||||
|
||||
/*!
|
||||
Sets whether the document has unsaved changes. The window can use this as a hint to
|
||||
@param isEdited <code>YES</code> means the document has unsaved changes.
|
||||
@param isEdited \c YES means the document has unsaved changes.
|
||||
*/
|
||||
- (void)setDocumentEdited:(BOOL)isEdited
|
||||
{
|
||||
[[self window] setDocumentEdited:isEdited];
|
||||
}
|
||||
|
||||
- (void)close
|
||||
{
|
||||
[[self window] close];
|
||||
}
|
||||
|
||||
- (void)setShouldCloseDocument:(BOOL)shouldCloseDocument
|
||||
{
|
||||
_shouldCloseDocument = shouldCloseDocument;
|
||||
}
|
||||
|
||||
- (BOOL)shouldCloseDocument
|
||||
{
|
||||
return _shouldCloseDocument;
|
||||
}
|
||||
|
||||
- (id)owner
|
||||
{
|
||||
return _cibOwner;
|
||||
}
|
||||
|
||||
- (CPString)windowCibName
|
||||
{
|
||||
if (_windowCibName)
|
||||
return _windowCibName;
|
||||
|
||||
return [[_windowCibPath lastPathComponent] stringByDeletingPathExtension];
|
||||
}
|
||||
|
||||
- (CPString)windowCibPath
|
||||
{
|
||||
if (_windowCibPath)
|
||||
return _windowCibPath;
|
||||
|
||||
return [[CPBundle bundleForClass:[_cibOwner class]] pathForResource:_windowCibName + @".cib"];
|
||||
}
|
||||
|
||||
// Setting and Getting Window Attributes
|
||||
|
||||
/*!
|
||||
@@ -268,7 +396,7 @@
|
||||
{
|
||||
if (!_document || !_window)
|
||||
return;
|
||||
|
||||
|
||||
// [_window setRepresentedFilename:];
|
||||
[_window setTitle:[self windowTitleForDocumentDisplayName:[_document displayName]]];
|
||||
}
|
||||
|
||||
@@ -24,17 +24,20 @@
|
||||
@import <Foundation/CPURLConnection.j>
|
||||
@import <Foundation/CPURLRequest.j>
|
||||
|
||||
@import "_CPCibClassSwapper.j"
|
||||
@import "_CPCibCustomObject.j"
|
||||
@import "_CPCibCustomResource.j"
|
||||
@import "_CPCibCustomView.j"
|
||||
@import "_CPCibKeyedUnarchiver.j"
|
||||
@import "_CPCibObjectData.j"
|
||||
@import "_CPCibProxyObject.j"
|
||||
@import "_CPCibWindowTemplate.j"
|
||||
|
||||
|
||||
CPCibOwner = @"CPCibOwner",
|
||||
CPCibTopLevelObjects = @"CPCibTopLevelObjects",
|
||||
CPCibReplacementClasses = @"CPCibReplacementClasses";
|
||||
CPCibReplacementClasses = @"CPCibReplacementClasses",
|
||||
CPCibExternalObjects = @"CPCibExternalObjects";
|
||||
|
||||
var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
@@ -80,11 +83,25 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCibNamed:(CPString)aName bundle:(CPBundle)aBundle loadDelegate:(id)aLoadDelegate
|
||||
- (id)initWithCibNamed:(CPString)aName bundle:(CPBundle)aBundle
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
|
||||
self = [self initWithContentsOfURL:[aBundle || [CPBundle mainBundle] pathForResource:aName]];
|
||||
|
||||
if (self)
|
||||
_bundle = aBundle;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCibNamed:(CPString)aName bundle:(CPBundle)aBundle loadDelegate:(id)aLoadDelegate
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
|
||||
self = [self initWithContentsOfURL:[aBundle || [CPBundle mainBundle] pathForResource:aName] loadDelegate:aLoadDelegate];
|
||||
|
||||
@@ -124,6 +141,8 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
[unarchiver setClass:[replacementClasses objectForKey:key] forClassName:key];
|
||||
}
|
||||
|
||||
[unarchiver setExternalObjectsForProxyIdentifiers:[anExternalNameTable objectForKey:CPCibExternalObjects]];
|
||||
|
||||
var objectData = [unarchiver decodeObjectForKey:CPCibObjectDataKey];
|
||||
|
||||
if (!objectData || ![objectData isKindOfClass:[_CPCibObjectData class]])
|
||||
@@ -135,14 +154,6 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
[objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects];
|
||||
[objectData awakeWithOwner:owner topLevelObjects:topLevelObjects];
|
||||
|
||||
var menu;
|
||||
|
||||
if ((menu = [objectData mainMenu]) != nil)
|
||||
{
|
||||
[CPApp setMainMenu:menu];
|
||||
[CPMenu setMenuBarVisible:YES];
|
||||
}
|
||||
|
||||
// Display Visible Windows.
|
||||
[objectData displayVisibleWindows];
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* CPCibConnector.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/CPKeyValueCoding.j>
|
||||
|
||||
|
||||
var _CPCibConnectorSourceKey = @"_CPCibConnectorSourceKey",
|
||||
_CPCibConnectorDestinationKey = @"_CPCibConnectorDestinationKey",
|
||||
_CPCibConnectorLabelKey = @"_CPCibConnectorLabelKey";
|
||||
|
||||
@implementation CPCibConnector : CPObject
|
||||
{
|
||||
id _source @accessors(property=source);
|
||||
id _destination @accessors(property=destination);
|
||||
CPString _label @accessors(property=label);
|
||||
}
|
||||
|
||||
- (void)replaceObject:(id)anObject withObject:(id)anotherObject
|
||||
{
|
||||
if (_source === anObject)
|
||||
_source = anotherObject;
|
||||
|
||||
if (_destination === anObject)
|
||||
_destination = anotherObject;
|
||||
}
|
||||
|
||||
- (void)replaceObjects:(Object)replacementObjects
|
||||
{
|
||||
var replacement = replacementObjects[[_source UID]];
|
||||
|
||||
if (replacement !== undefined)
|
||||
_source = replacement;
|
||||
|
||||
replacement = replacementObjects[[_destination UID]];
|
||||
|
||||
if (replacement !== undefined)
|
||||
_destination = replacement;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPCibConnector (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_source = [aCoder decodeObjectForKey:_CPCibConnectorSourceKey];
|
||||
_destination = [aCoder decodeObjectForKey:_CPCibConnectorDestinationKey];
|
||||
_label = [aCoder decodeObjectForKey:_CPCibConnectorLabelKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_source forKey:_CPCibConnectorSourceKey];
|
||||
[aCoder encodeObject:_destination forKey:_CPCibConnectorDestinationKey];
|
||||
[aCoder encodeObject:_label forKey:_CPCibConnectorLabelKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// For backwards compatibility.
|
||||
@implementation _CPCibConnector : CPCibConnector { } @end
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* CPCibControlConnector.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 "CPCibConnector.j"
|
||||
|
||||
|
||||
@implementation CPCibControlConnector : CPCibConnector
|
||||
{
|
||||
}
|
||||
|
||||
- (void)establishConnection
|
||||
{
|
||||
var selectorName = _label,
|
||||
selectorNameLength = [selectorName length];
|
||||
|
||||
if (selectorNameLength && selectorName[selectorNameLength - 1] !== ':')
|
||||
selectorName += ':';
|
||||
|
||||
var selector = CPSelectorFromString(selectorName);
|
||||
|
||||
// Not having a selector is a fatal error.
|
||||
if (!selector)
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] selector " + selectorName + @" does not exist."];
|
||||
|
||||
// If the destination doesn't respond to this selector, warn but don't die.
|
||||
if (_destination && ![_destination respondsToSelector:selector])
|
||||
{
|
||||
CPLog.warn(@"Could not connect the action " + selector + @" to target of class " + [_destination className]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Not being able to set the action is a fatal error.
|
||||
if ([_source respondsToSelector:@selector(setAction:)])
|
||||
objj_msgSend(_source, @selector(setAction:), selector);
|
||||
|
||||
else
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setAction:"];
|
||||
|
||||
// Not being able to set the target is a fatal error.
|
||||
if ([_source respondsToSelector:@selector(setTarget:)])
|
||||
objj_msgSend(_source, @selector(setTarget:), _destination);
|
||||
|
||||
else
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setTarget:"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibControlConnector : CPCibControlConnector { } @end
|
||||
|
||||
@@ -39,16 +39,41 @@ var LoadInfoForCib = {};
|
||||
|
||||
@implementation CPBundle (CPCibLoading)
|
||||
|
||||
+ (void)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable
|
||||
{
|
||||
[[[CPCib alloc] initWithContentsOfURL:anAbsolutePath] instantiateCibWithExternalNameTable:aNameTable];
|
||||
}
|
||||
|
||||
+ (void)loadCibNamed:(CPString)aName owner:(id)anOwner
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// Path is based solely on anOwner:
|
||||
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
|
||||
path = [bundle pathForResource:aName];
|
||||
|
||||
[self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]];
|
||||
}
|
||||
|
||||
- (void)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable
|
||||
{
|
||||
[[[CPCib alloc] initWithContentsOfURL:aFileName] instantiateCibWithExternalNameTable:aNameTable];
|
||||
}
|
||||
|
||||
+ (void)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable loadDelegate:aDelegate
|
||||
{
|
||||
var cib = [[CPCib alloc] initWithContentsOfURL:anAbsolutePath loadDelegate:self];
|
||||
|
||||
LoadInfoForCib[[cib hash]] = { loadDelegate:aDelegate, externalNameTable:aNameTable };
|
||||
LoadInfoForCib[[cib UID]] = { loadDelegate:aDelegate, externalNameTable:aNameTable };
|
||||
}
|
||||
|
||||
+ (void)loadCibNamed:(CPString)aName owner:(id)anOwner loadDelegate:(id)aDelegate
|
||||
{
|
||||
// Path is based solely on anOwner:
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// Path is based solely on anOwner:
|
||||
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
|
||||
path = [bundle pathForResource:aName];
|
||||
|
||||
@@ -59,14 +84,14 @@ var LoadInfoForCib = {};
|
||||
{
|
||||
var cib = [[CPCib alloc] initWithCibNamed:aFileName bundle:self loadDelegate:[self class]];
|
||||
|
||||
LoadInfoForCib[[cib hash]] = { loadDelegate:aDelegate, externalNameTable:aNameTable };
|
||||
LoadInfoForCib[[cib UID]] = { loadDelegate:aDelegate, externalNameTable:aNameTable };
|
||||
}
|
||||
|
||||
+ (void)cibDidFinishLoading:(CPCib)aCib
|
||||
{
|
||||
var loadInfo = LoadInfoForCib[[aCib hash]];
|
||||
var loadInfo = LoadInfoForCib[[aCib UID]];
|
||||
|
||||
delete LoadInfoForCib[[aCib hash]];
|
||||
delete LoadInfoForCib[[aCib UID]];
|
||||
|
||||
[aCib instantiateCibWithExternalNameTable:loadInfo.externalNameTable];
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* CPCibOutletConnector.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 "CPCibConnector.j"
|
||||
|
||||
|
||||
@implementation CPCibOutletConnector : CPCibConnector
|
||||
{
|
||||
}
|
||||
|
||||
- (void)establishConnection
|
||||
{
|
||||
try
|
||||
{
|
||||
[_source setValue:_destination forKey:_label];
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
if ([anException name] === CPUndefinedKeyException)
|
||||
CPLog.warn(@"Could not connect the outlet " + _label + @" of target of class " + [_source className]);
|
||||
|
||||
else
|
||||
throw anException;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibOutletConnector : CPCibOutletConnector { } @end
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* _CPCibClassSwapper.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2009, 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>
|
||||
|
||||
|
||||
var _CPCibClassSwapperClassNameKey = @"_CPCibClassSwapperClassNameKey",
|
||||
_CPCibClassSwapperOriginalClassNameKey = @"_CPCibClassSwapperOriginalClassNameKey";
|
||||
|
||||
@implementation _CPCibClassSwapper : CPObject
|
||||
{
|
||||
}
|
||||
|
||||
+ (void)allocObjectWithCoder:(CPCoder)aCoder className:(CPString)aClassName
|
||||
{
|
||||
// FIXME: Also check class classForClassName:
|
||||
var theClass = [aCoder classForClassName:aClassName];
|
||||
|
||||
if (!theClass)
|
||||
{
|
||||
theClass = objj_lookUpClass(aClassName);
|
||||
|
||||
if (!theClass)
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [theClass alloc];
|
||||
}
|
||||
|
||||
+ (id)allocWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if ([aCoder respondsToSelector:@selector(usesOriginalClasses)] && [aCoder usesOriginalClasses])
|
||||
{
|
||||
var theClassName = [aCoder decodeObjectForKey:_CPCibClassSwapperOriginalClassNameKey],
|
||||
object = [self allocObjectWithCoder:aCoder className:theClassName];
|
||||
}
|
||||
else
|
||||
{
|
||||
var theClassName = [aCoder decodeObjectForKey:_CPCibClassSwapperClassNameKey],
|
||||
object = [self allocObjectWithCoder:aCoder className:theClassName];
|
||||
|
||||
if (!object)
|
||||
{
|
||||
CPLog.error("Unable to find class " + theClassName + " in cib file.");
|
||||
|
||||
object = [self allocObjectWithCoder:aCoder className:[aCoder decodeObjectForKey:_CPCibClassSwapperOriginalClassNameKey]];
|
||||
}
|
||||
}
|
||||
|
||||
if (!object)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Unable to find class " + theClassName + " in cib file."];
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,103 +0,0 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPKeyValueCoding.j>
|
||||
|
||||
|
||||
var _CPCibConnectorSourceKey = @"_CPCibConnectorSourceKey",
|
||||
_CPCibConnectorDestinationKey = @"_CPCibConnectorDestinationKey",
|
||||
_CPCibConnectorLabelKey = @"_CPCibConnectorLabelKey";
|
||||
|
||||
@implementation _CPCibConnector : CPObject
|
||||
{
|
||||
id _source;
|
||||
id _destination;
|
||||
CPString _label;
|
||||
}
|
||||
|
||||
- (void)replaceObjects:(JSObject)replacementObjects
|
||||
{
|
||||
var replacement = replacementObjects[[_source hash]];
|
||||
|
||||
if (replacement !== undefined)
|
||||
_source = replacement;
|
||||
|
||||
replacement = replacementObjects[[_destination hash]];
|
||||
|
||||
if (replacement !== undefined)
|
||||
_destination = replacement;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibConnector (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_source = [aCoder decodeObjectForKey:_CPCibConnectorSourceKey];
|
||||
_destination = [aCoder decodeObjectForKey:_CPCibConnectorDestinationKey];
|
||||
_label = [aCoder decodeObjectForKey:_CPCibConnectorLabelKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_source forKey:_CPCibConnectorSourceKey];
|
||||
[aCoder encodeObject:_destination forKey:_CPCibConnectorDestinationKey];
|
||||
[aCoder encodeObject:_label forKey:_CPCibConnectorLabelKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibControlConnector : _CPCibConnector
|
||||
{
|
||||
}
|
||||
|
||||
- (void)establishConnection
|
||||
{
|
||||
var selectorName = _label;
|
||||
|
||||
if (![selectorName hasSuffix:@":"])
|
||||
selectorName += ':';
|
||||
|
||||
var selector = CPSelectorFromString(selectorName);
|
||||
|
||||
if (!selector)
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] selector " + selectorName + @" does not exist."];
|
||||
|
||||
if ([_source respondsToSelector:@selector(setAction:)])
|
||||
objj_msgSend(_source, @selector(setAction:), selector);
|
||||
|
||||
else
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setAction:"];
|
||||
|
||||
if ([_source respondsToSelector:@selector(setTarget:)])
|
||||
objj_msgSend(_source, @selector(setTarget:), _destination);
|
||||
|
||||
else
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setTarget:"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibOutletConnector : _CPCibConnector
|
||||
{
|
||||
}
|
||||
|
||||
- (void)establishConnection
|
||||
{
|
||||
[_source setValue:_destination forKey:_label];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -14,6 +14,11 @@ var _CPCibCustomObjectClassName = @"_CPCibCustomObjectClassName";
|
||||
return _className;
|
||||
}
|
||||
|
||||
- (void)setCustomClassName:(CPString)aClassName
|
||||
{
|
||||
_className = aClassName;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [super description] + " (" + [self customClassName] + ')';
|
||||
@@ -42,10 +47,21 @@ var _CPCibCustomObjectClassName = @"_CPCibCustomObjectClassName";
|
||||
{
|
||||
var theClass = CPClassFromString(_className);
|
||||
|
||||
#if DEBUG
|
||||
// Hey this is us!
|
||||
if (theClass === [self class])
|
||||
{
|
||||
_className = @"CPObject";
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
if (!theClass)
|
||||
{
|
||||
#if DEBUG
|
||||
CPLog("Unknown class \"" + _className + "\" in cib file");
|
||||
#endif
|
||||
theClass = [CPObject class];
|
||||
}
|
||||
|
||||
if (theClass === [CPApplication class])
|
||||
return [CPApplication sharedApplication];
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
return _className;
|
||||
}
|
||||
|
||||
- (void)setCustomClassName:(CPString)aClassName
|
||||
{
|
||||
_className = aClassName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
@@ -47,33 +52,26 @@ var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
|
||||
if (self)
|
||||
_className = [aCoder decodeObjectForKey:_CPCibCustomViewClassNameKey];
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
[aCoder encodeObject:_className forKey:_CPCibCustomViewClassNameKey];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
- (CPString)customClassName
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextSetLineWidth(context, 1.0);
|
||||
CGContextSetStrokeColor(context, [CPColor colorWithCalibratedRed:169.0 / 255.0 green:173.0 / 255.0 blue:178.0 / 255.0 alpha:1.0]);
|
||||
CGContextStrokeRect(context, CGRectInset(CGRectIntegral(bounds), 0.5, 0.5));
|
||||
CGContextSetFillColor(context, [CPColor colorWithCalibratedRed:224.0 / 255.0 green:236.0 / 255.0 blue:250.0 / 255.0 alpha:1.0]);
|
||||
CGContextFillRect(context, CGRectInset(bounds, 2.0, 2.0));
|
||||
return _className;
|
||||
}
|
||||
|
||||
- (id)_cibInstantiate
|
||||
{
|
||||
var theClass = CPClassFromString(_className);
|
||||
|
||||
|
||||
// If we don't have this class, just use CPView.
|
||||
// FIXME: Should we instead throw an exception?
|
||||
if (!theClass)
|
||||
@@ -83,7 +81,7 @@ var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
#endif
|
||||
theClass = [CPView class];
|
||||
}
|
||||
|
||||
|
||||
// Hey this is us!
|
||||
if (theClass === [self class])
|
||||
{
|
||||
@@ -110,7 +108,7 @@ var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
|
||||
[view setAutoresizingMask:[self autoresizingMask]];
|
||||
[view setAutoresizesSubviews:[self autoresizesSubviews]];
|
||||
|
||||
|
||||
[view setHitTests:[self hitTests]];
|
||||
[view setHidden:[self isHidden]];
|
||||
[view setAlphaValue:[self alphaValue]];
|
||||
@@ -119,7 +117,7 @@ var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
|
||||
[view setBackgroundColor:[self backgroundColor]];
|
||||
}
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
@implementation _CPCibKeyedUnarchiver : CPKeyedUnarchiver
|
||||
{
|
||||
CPBundle _bundle;
|
||||
BOOL _awakenCustomResources;
|
||||
CPBundle _bundle;
|
||||
BOOL _awakenCustomResources;
|
||||
CPDictionary _externalObjectsForProxyIdentifiers;
|
||||
}
|
||||
|
||||
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle awakenCustomResources:(BOOL)shouldAwakenCustomResources
|
||||
@@ -33,6 +34,16 @@
|
||||
return _awakenCustomResources;
|
||||
}
|
||||
|
||||
- (void)setExternalObjectsForProxyIdentifiers:(CPDictionary)externalObjectsForProxyIdentifiers
|
||||
{
|
||||
_externalObjectsForProxyIdentifiers = externalObjectsForProxyIdentifiers;
|
||||
}
|
||||
|
||||
- (id)externalObjectForProxyIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
return [_externalObjectsForProxyIdentifiers objectForKey:anIdentifier];
|
||||
}
|
||||
|
||||
- (void)replaceObjectAtUID:(int)aUID withObject:(id)anObject
|
||||
{
|
||||
_objects[aUID] = anObject;
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import "CPCib.j"
|
||||
@import "_CPCibConnector.j"
|
||||
@import "CPCibConnector.j"
|
||||
@import "CPCibControlConnector.j"
|
||||
@import "CPCibOutletConnector.j"
|
||||
|
||||
|
||||
@implementation _CPCibObjectData : CPObject
|
||||
@@ -98,28 +100,13 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPMenu)mainMenu
|
||||
{
|
||||
var index = [_namesValues indexOfObjectIdenticalTo:"MainMenu"];
|
||||
|
||||
if (index === CPNotFound)
|
||||
{
|
||||
index = [_namesValues indexOfObjectIdenticalTo:"Main Menu"];
|
||||
|
||||
if (index === CPNotFound)
|
||||
return nil;
|
||||
}
|
||||
|
||||
return _namesKeys[index];
|
||||
}
|
||||
|
||||
- (void)displayVisibleWindows
|
||||
{
|
||||
var object = nil,
|
||||
objectEnumerator = [_visibleWindows objectEnumerator];
|
||||
|
||||
while (object = [objectEnumerator nextObject])
|
||||
[_replacementObjects[[object hash]] makeKeyAndOrderFront:self];
|
||||
[_replacementObjects[[object UID]] makeKeyAndOrderFront:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -242,7 +229,7 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
|
||||
if (instantiatedObject !== object)
|
||||
{
|
||||
_replacementObjects[[object hash]] = instantiatedObject;
|
||||
_replacementObjects[[object UID]] = instantiatedObject;
|
||||
|
||||
if ([instantiatedObject isKindOfClass:[CPView class]])
|
||||
{
|
||||
@@ -266,7 +253,7 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
|
||||
- (void)establishConnectionsWithOwner:(id)anOwner topLevelObjects:(CPMutableArray)topLevelObjects
|
||||
{
|
||||
_replacementObjects[[_fileOwner hash]] = anOwner;
|
||||
_replacementObjects[[_fileOwner UID]] = anOwner;
|
||||
|
||||
var index = 0,
|
||||
count = _connections.length;
|
||||
@@ -287,7 +274,7 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
while (count--)
|
||||
{
|
||||
var object = _objectsKeys[count],
|
||||
instantiatedObject = _replacementObjects[[object hash]];
|
||||
instantiatedObject = _replacementObjects[[object UID]];
|
||||
|
||||
if (instantiatedObject)
|
||||
object = instantiatedObject;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
@implementation _CPCibProxyObject : CPObject
|
||||
{
|
||||
CPString _identifier;
|
||||
}
|
||||
@end
|
||||
|
||||
var _CPCibProxyObjectIdentifierKey = @"CPIdentifier";
|
||||
|
||||
@implementation _CPCibProxyObject (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_identifier = [aCoder decodeObjectForKey:_CPCibProxyObjectIdentifierKey];
|
||||
|
||||
if ([aCoder respondsToSelector:@selector(externalObjectForProxyIdentifier:)])
|
||||
return [aCoder externalObjectForProxyIdentifier:_identifier];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:_CPCibProxyObjectIdentifierKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -33,6 +33,25 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeK
|
||||
BOOL _windowIsFullBridge;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_windowClass = @"CPWindow";
|
||||
_windowRect = CGRectMake(0.0, 0.0, 400.0, 200.0);
|
||||
_windowStyleMask = CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask;
|
||||
|
||||
_windowTitle = @"Window";
|
||||
_windowView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 400.0, 200.0)];
|
||||
|
||||
_windowIsFullBridge = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
@@ -78,6 +97,17 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeK
|
||||
[aCoder encodeObject:_windowIsFullBridge forKey:_CPCibWindowTemplateWindowIsFullBridgeKey];
|
||||
}
|
||||
|
||||
- (CPString)customClassName
|
||||
{
|
||||
return _windowClass;
|
||||
}
|
||||
|
||||
|
||||
- (void)setCustomClassName:(CPString)aClassName
|
||||
{
|
||||
_windowClass = aClassName;
|
||||
}
|
||||
|
||||
- (CPString)windowClass
|
||||
{
|
||||
return _windowClass;
|
||||
|
||||
@@ -54,8 +54,8 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code>
|
||||
@return <code>YES</code>
|
||||
Returns \c YES
|
||||
@return \c YES
|
||||
*/
|
||||
- (void)shouldArchiveValueForKey:(CPString)aKey
|
||||
{
|
||||
@@ -63,8 +63,8 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>nil</code>
|
||||
@return <code>nil</code>
|
||||
Returns \c nil
|
||||
@return \c nil
|
||||
*/
|
||||
+ (id)defaultValueForKey:(CPString)aKey
|
||||
{
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
/*!
|
||||
Specifies whether this animation should be removed after it has completed.
|
||||
@param <code>YES</code> means the animation should be removed
|
||||
@param \c YES means the animation should be removed
|
||||
*/
|
||||
- (void)setRemovedOnCompletion:(BOOL)isRemovedOnCompletion
|
||||
{
|
||||
@@ -81,7 +81,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the animation is removed after completion
|
||||
Returns \c YES if the animation is removed after completion
|
||||
*/
|
||||
- (BOOL)removedOnCompletion
|
||||
{
|
||||
@@ -89,7 +89,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the animation is removed after completion
|
||||
Returns \c YES if the animation is removed after completion
|
||||
*/
|
||||
- (BOOL)isRemovedOnCompletion
|
||||
{
|
||||
@@ -97,7 +97,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's timing function. If <code>nil</code>, then it has a linear pacing.
|
||||
Returns the animation's timing function. If \c nil, then it has a linear pacing.
|
||||
*/
|
||||
- (CAMediaTimingFunction)timingFunction
|
||||
{
|
||||
|
||||
@@ -56,12 +56,12 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
|
||||
@delegate -(void)drawLayer:(CALayer)layer inContext:(CGContextRef)ctx;
|
||||
If the delegate implements this method, the CALayer will
|
||||
call this in place of its <code>drawInContext:</code>.
|
||||
call this in place of its \c -drawInContext:.
|
||||
@param layer the layer to draw for
|
||||
@param ctx the context to draw on
|
||||
|
||||
@delegate -(void)displayLayer:(CALayer)layer;
|
||||
The delegate can override the layer's <code>display</code> method
|
||||
The delegate can override the layer's \c -display method
|
||||
by implementing this method.
|
||||
*/
|
||||
@implementation CALayer : CPObject
|
||||
@@ -419,7 +419,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
// Providing Layer Content
|
||||
/*!
|
||||
Returns the CGImage contents of this layer.
|
||||
The default contents are <code>nil</code>.
|
||||
The default contents are \c nil.
|
||||
*/
|
||||
- (CGImage)contents
|
||||
{
|
||||
@@ -547,7 +547,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
// Style Attributes
|
||||
/*!
|
||||
Returns the opacity of the layer. The value is between
|
||||
<code>0.0</code> (transparent) and <code>1.0</code> (opaque).
|
||||
\c 0.0 (transparent) and \c 1.0 (opaque).
|
||||
*/
|
||||
- (float)opacity
|
||||
{
|
||||
@@ -556,7 +556,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
|
||||
/*!
|
||||
Sets the opacity for the layer.
|
||||
@param anOpacity the new opacity (between <code>0.0</code> (transparent) and <code>1.0</code> (opaque)).
|
||||
@param anOpacity the new opacity (between \c 0.0 (transparent) and \c 1.0 (opaque)).
|
||||
*/
|
||||
- (void)setOpacity:(float)anOpacity
|
||||
{
|
||||
@@ -571,7 +571,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
|
||||
/*!
|
||||
Sets whether the layer is hidden.
|
||||
@param isHidden <code>YES</code> means the layer will be hidden. <code>NO</code> means the layer will be visible.
|
||||
@param isHidden \c YES means the layer will be hidden. \c NO means the layer will be visible.
|
||||
*/
|
||||
- (void)setHidden:(BOOL)isHidden
|
||||
{
|
||||
@@ -580,7 +580,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the layer is hidden.
|
||||
Returns \c YES if the layer is hidden.
|
||||
*/
|
||||
- (BOOL)hidden
|
||||
{
|
||||
@@ -588,7 +588,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the layer is hidden.
|
||||
Returns \c YES if the layer is hidden.
|
||||
*/
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
@@ -597,7 +597,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
|
||||
/*!
|
||||
Sets whether content that goes lies outside the bounds is hidden or visible.
|
||||
@param masksToBounds <code>YES</code> hides the excess content. <code>NO</code> makes it visible.
|
||||
@param masksToBounds \c YES hides the excess content. \c NO makes it visible.
|
||||
*/
|
||||
- (void)setMasksToBounds:(BOOL)masksToBounds
|
||||
{
|
||||
@@ -724,7 +724,7 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
||||
Inserts a layer below another layer.
|
||||
@param aLayer the layer to insert
|
||||
@param aSublayer the layer to insert below
|
||||
@throws CALayerNotFoundException if <code>aSublayer</code> is not in the array of sublayers
|
||||
@throws CALayerNotFoundException if \c aSublayer is not in the array of sublayers
|
||||
*/
|
||||
- (void)insertSublayer:(CALayer)aLayer below:(CALayer)aSublayer
|
||||
{
|
||||
@@ -737,7 +737,7 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
||||
Inserts a layer above another layer.
|
||||
@param aLayer the layer to insert
|
||||
@param aSublayer the layer to insert above
|
||||
@throws CALayerNotFoundException if <code>aSublayer</code> is not in the array of sublayers
|
||||
@throws CALayerNotFoundException if \c aSublayer is not in the array of sublayers
|
||||
*/
|
||||
- (void)insertSublayer:(CALayer)aLayer above:(CALayer)aSublayer
|
||||
{
|
||||
@@ -779,9 +779,9 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
||||
+ (void)runLoopUpdateLayers
|
||||
{if (window.oops) {alert(window.latest); objj_debug_print_backtrace();}
|
||||
window.loop = true;
|
||||
for (hash in CALayerRegisteredRunLoopUpdates)
|
||||
for (UID in CALayerRegisteredRunLoopUpdates)
|
||||
{
|
||||
var layer = CALayerRegisteredRunLoopUpdates[hash],
|
||||
var layer = CALayerRegisteredRunLoopUpdates[UID],
|
||||
mask = layer._runLoopUpdateMask;
|
||||
|
||||
if (mask & CALayerDOMUpdateMask)
|
||||
@@ -813,7 +813,7 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
||||
}
|
||||
|
||||
_runLoopUpdateMask |= anUpdateMask;
|
||||
CALayerRegisteredRunLoopUpdates[[self hash]] = self;
|
||||
CALayerRegisteredRunLoopUpdates[[self UID]] = self;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -834,7 +834,7 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
||||
|
||||
/*!
|
||||
Sets whether the layer needs to be redrawn when its bounds are changed.
|
||||
@param needsDisplayOnBoundsChange <code>YES</code> means the display is redraw on a bounds change.
|
||||
@param needsDisplayOnBoundsChange \c YES means the display is redraw on a bounds change.
|
||||
*/
|
||||
- (void)setNeedsDisplayOnBoundsChange:(BOOL)needsDisplayOnBoundsChange
|
||||
{
|
||||
@@ -842,7 +842,7 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the display should be redrawn on a bounds change.
|
||||
Returns \c YES if the display should be redrawn on a bounds change.
|
||||
*/
|
||||
- (BOOL)needsDisplayOnBoundsChange
|
||||
{
|
||||
@@ -906,7 +906,7 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
||||
|
||||
// Hit Testing
|
||||
/*!
|
||||
Returns <code>YES</code> if the layer contains the point.
|
||||
Returns \c YES if the layer contains the point.
|
||||
@param aPoint the point to test
|
||||
*/
|
||||
- (BOOL)containsPoint:(CGPoint)aPoint
|
||||
@@ -917,7 +917,7 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
||||
/*!
|
||||
Returns the farthest descendant of this layer that contains the specified point.
|
||||
@param aPoint the point to test
|
||||
@return the containing layer or <code>nil</code> if there was no hit.
|
||||
@return the containing layer or \c nil if there was no hit.
|
||||
*/
|
||||
- (CALayer)hitTest:(CGPoint)aPoint
|
||||
{
|
||||
|
||||
@@ -101,8 +101,8 @@ function CGColorCreateCopy(aColor)
|
||||
|
||||
/*!
|
||||
Creates a gray color object.
|
||||
@param gray the value to use for the color intensities (<code>0.0-1.0</code>)
|
||||
@param alpha the gray's alpha value (<code>0.0-1.0</code>)
|
||||
@param gray the value to use for the color intensities (\c 0.\c 0-\c 1.\c 0).
|
||||
@param alpha the gray's alpha value (\c 0.\c 0-\c 1.\c 0).
|
||||
@return CGColor the new gray color object
|
||||
@group CGColor
|
||||
*/
|
||||
@@ -113,10 +113,10 @@ function CGColorCreateGenericGray(gray, alpha)
|
||||
|
||||
/*!
|
||||
Creates an RGB color.
|
||||
@param red the red component (<code>0.0-1.0</code>)
|
||||
@param green the green component (<code>0.0-1.0</code>)
|
||||
@param blue the blue component (<code>0.0-1.0</code>)
|
||||
@param alpha the alpha component (<code>0.0-1.0</code>)
|
||||
@param red the red component (\c 0.\c 0-\c 1.\c 0)..
|
||||
@param green the green component (\c 0.\c 0-\c 1.\c 0).
|
||||
@param blue the blue component (\c 0.\c 0-\c 1.\c 0).
|
||||
@param alpha the alpha component (\c 0.\c 0-\c 1.\c 0).
|
||||
@return CGColor the RGB based color
|
||||
@group CGColor
|
||||
*/
|
||||
@@ -127,11 +127,11 @@ function CGColorCreateGenericRGB(red, green, blue, alpha)
|
||||
|
||||
/*!
|
||||
Creates a CMYK color.
|
||||
@param cyan the cyan component (<code>0.0-1.0</code>)
|
||||
@param magenta the magenta component (<code>0.0-1.0</code>)
|
||||
@param yellow the yellow component (<code>0.0-1.0</code>)
|
||||
@param black the black component (<code>0.0-1.0</code>)
|
||||
@param alpha the alpha component (<code>0.0-1.0</code>)
|
||||
@param cyan the cyan component (\c 0.\c 0-\c 1.\c 0).
|
||||
@param magenta the magenta component (\c 0.\c 0-\c 1.\c 0).
|
||||
@param yellow the yellow component (\c 0.\c 0-\c 1.\c 0).
|
||||
@param black the black component (\c 0.\c 0-\c 1.\c 0).
|
||||
@param alpha the alpha component (\c 0.\c 0-\c 1.\c 0).
|
||||
@return CGColor the CMYK based color
|
||||
@group CGColor
|
||||
*/
|
||||
@@ -143,7 +143,7 @@ function CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha)
|
||||
/*!
|
||||
Creates a copy of the color with a specified alpha.
|
||||
@param aColor the color object to copy
|
||||
@param anAlpha the new alpha component for the copy (<code>0.0-1.0</code>)
|
||||
@param anAlpha the new alpha component for the copy (\c 0.\c 0-\c 1.\c 0).
|
||||
@return CGColor the new copy
|
||||
@group CGColor
|
||||
*/
|
||||
@@ -184,8 +184,8 @@ function CGColorCreateWithPattern(aColorSpace, aPattern, components)
|
||||
Determines if two colors are the same.
|
||||
@param lhs the first CGColor
|
||||
@param rhs the second CGColor
|
||||
@return <code>YES</code> if the two colors are equal.
|
||||
<code>NO</code> otherwise.
|
||||
@return \c YES if the two colors are equal.
|
||||
\c NO otherwise.
|
||||
*/
|
||||
function CGColorEqualToColor(lhs, rhs)
|
||||
{
|
||||
@@ -218,7 +218,7 @@ function CGColorEqualToColor(lhs, rhs)
|
||||
/*!
|
||||
Returns the color's alpha component.
|
||||
@param aColor the color
|
||||
@return float the alpha component (<code>0.0-1.0</code>)
|
||||
@return float the alpha component (\c 0.\c 0-\c 1.\c 0).
|
||||
@group CGColor
|
||||
*/
|
||||
function CGColorGetAlpha(aColor)
|
||||
|
||||
@@ -548,7 +548,9 @@ var KAPPA = 4.0 * ((SQRT2 - 1.0) / 3.0);
|
||||
*/
|
||||
function CGContextAddEllipseInRect(aContext, aRect)
|
||||
{
|
||||
CGContextBeginPath(aContext);
|
||||
CGContextAddPath(aContext, CGPathWithEllipseInRect(aRect));
|
||||
CGContextClosePath(aContext);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -650,15 +652,17 @@ function CGContextSetStrokeColor(aContext, aColor)
|
||||
@param aContext the CGContext to draw into
|
||||
@param aRect the base rectangle
|
||||
@param aRadius the distance from the rectange corner to the rounded corner
|
||||
@param ne set it to <code>YES</code> for a rounded northeast corner
|
||||
@param se set it to <code>YES</code> for a rounded southeast corner
|
||||
@param sw set it to <code>YES</code> for a rounded southwest corner
|
||||
@param nw set it to <code>YES</code> for a rounded northwest corner
|
||||
@param ne set it to \c YES for a rounded northeast corner
|
||||
@param se set it to \c YES for a rounded southeast corner
|
||||
@param sw set it to \c YES for a rounded southwest corner
|
||||
@param nw set it to \c YES for a rounded northwest corner
|
||||
@return void
|
||||
*/
|
||||
function CGContextFillRoundedRectangleInRect(aContext, aRect, aRadius, ne, se, sw, nw)
|
||||
{
|
||||
CGContextAddPath(aContext, CGPathWithRoundedRectangleInRect(aRect, aRadius, aRadius, ne, se, sw, nw));
|
||||
CGContextBeginPath(aContext);
|
||||
CGContextAddPath(aContext, CGPathWithRoundedRectangleInRect(aRect, aRadius, aRadius, ne, se, sw, nw));
|
||||
CGContextClosePath(aContext);
|
||||
CGContextFillPath(aContext);
|
||||
}
|
||||
|
||||
@@ -667,15 +671,17 @@ function CGContextFillRoundedRectangleInRect(aContext, aRect, aRadius, ne, se, s
|
||||
@param aContext the CGContext to draw into
|
||||
@param aRect the base rectangle
|
||||
@param aRadius the distance from the rectange corner to the rounded corner
|
||||
@param ne set it to <code>YES</code> for a rounded northeast corner
|
||||
@param se set it to <code>YES</code> for a rounded southeast corner
|
||||
@param sw set it to <code>YES</code> for a rounded southwest corner
|
||||
@param nw set it to <code>YES</code> for a rounded northwest corner
|
||||
@param ne set it to \c YES for a rounded northeast corner
|
||||
@param se set it to \c YES for a rounded southeast corner
|
||||
@param sw set it to \c YES for a rounded southwest corner
|
||||
@param nw set it to \c YES for a rounded northwest corner
|
||||
@return void
|
||||
*/
|
||||
function CGContextStrokeRoundedRectangleInRect(aContext, aRect, aRadius, ne, se, sw, nw)
|
||||
{
|
||||
CGContextBeginPath(aContext);
|
||||
CGContextAddPath(aContext, CGPathWithRoundedRectangleInRect(aRect, aRadius, aRadius, ne, se, sw, nw));
|
||||
CGContextClosePath(aContext);
|
||||
CGContextStrokePath(aContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,12 +76,12 @@ _function(CGInsetIsEmpty(anInset))
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns a <code>BOOL</code> indicating whether CGRect <code>lhsRect</code>
|
||||
contains CGRect <code>rhsRect</code>.
|
||||
Returns a \c BOOL indicating whether CGRect \c lhsRect
|
||||
contains CGRect \c rhsRect.
|
||||
@group CGRect
|
||||
@param lhsRect the CGRect to test if <code>rhsRect</code> is inside of
|
||||
@param rhsRect the CGRect to test if it fits inside <code>lhsRect</code>.
|
||||
@return BOOL <code>YES</code> if <code>rhsRect</code> fits inside <code>lhsRect</code>.
|
||||
@param lhsRect the CGRect to test if \c rhsRect is inside of
|
||||
@param rhsRect the CGRect to test if it fits inside \c lhsRect.
|
||||
@return BOOL \c YES if \c rhsRect fits inside \c lhsRect.
|
||||
*/
|
||||
function CGRectContainsRect(lhsRect, rhsRect)
|
||||
{
|
||||
@@ -91,11 +91,11 @@ function CGRectContainsRect(lhsRect, rhsRect)
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the two rectangles intersect
|
||||
Returns \c YES if the two rectangles intersect
|
||||
@group CGRect
|
||||
@param lhsRect the first CGRect
|
||||
@param rhsRect the second CGRect
|
||||
@return BOOL <code>YES</code> if the two rectangles have any common spaces, and <code>NO</code>, otherwise.
|
||||
@return BOOL \c YES if the two rectangles have any common spaces, and \c NO, otherwise.
|
||||
*/
|
||||
function CGRectIntersectsRect(lhsRect, rhsRect)
|
||||
{
|
||||
@@ -155,26 +155,20 @@ function CGRectStandardize(aRect)
|
||||
{
|
||||
var width = _CGRectGetWidth(aRect),
|
||||
height = _CGRectGetHeight(aRect),
|
||||
standardized = aRect;
|
||||
standardized = _CGRectMakeCopy(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,20 +87,20 @@ function CGPathAddArc(aPath, aTransform, x, y, aRadius, aStartAngle, anEndAngle,
|
||||
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.
|
||||
@@ -109,7 +109,7 @@ function CGPathAddArc(aPath, aTransform, x, y, aRadius, aStartAngle, anEndAngle,
|
||||
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);
|
||||
@@ -169,23 +169,38 @@ function CGPathAddLineToPoint(aPath, aTransform, x, y)
|
||||
|
||||
function CGPathAddPath(aPath, aTransform, anotherPath)
|
||||
{
|
||||
var i = 0,
|
||||
count = anotherPath.count;
|
||||
|
||||
for (; i < count; ++i)
|
||||
for (var i = 0, count = anotherPath.count; i < count; ++i)
|
||||
{
|
||||
var element = anotherPath.elements[i];
|
||||
|
||||
aPath.elements[aPath.count] = { type:element.type, x:element.x, y:element.y,
|
||||
cpx:element.cpx, cpy:element.cpy,
|
||||
radius:element.radius, startAngle:element.startAngle, endAngle:element.endAngle,
|
||||
cp1x:element.cp1x, cp1y:element.cp1y, cp2x:element.cp2x, cp2y:element.cp2y,
|
||||
points: element.points ? element.points.slice() : nil};
|
||||
|
||||
aPath.count++
|
||||
|
||||
switch (element.type)
|
||||
{
|
||||
case kCGPathElementAddLineToPoint: CGPathAddLineToPoint(aPath, aTransform, element.x, element.y);
|
||||
break;
|
||||
|
||||
case kCGPathElementAddCurveToPoint: CGPathAddCurveToPoint(aPath, aTransform,
|
||||
element.cp1x, element.cp1y,
|
||||
element.cp2x, element.cp2y,
|
||||
element.x, element.y);
|
||||
break;
|
||||
|
||||
case kCGPathElementAddArc: CGPathAddArc(aPath, aTransform, element.x, element.y,
|
||||
element.radius, element.startAngle,
|
||||
element.endAngle, element.isClockwise);
|
||||
break;
|
||||
|
||||
case kCGPathElementAddQuadCurveToPoint: CGPathAddQuadCurveToPoint(aPath, aTransform,
|
||||
element.cpx, element.cpy,
|
||||
element.x, element.y);
|
||||
break;
|
||||
|
||||
case kCGPathElementMoveToPoint: CGPathMoveToPoint(aPath, aTransform, element.x, element.y);
|
||||
break;
|
||||
|
||||
case kCGPathElementCloseSubpath: CGPathCloseSubpath(aPath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
aPath.current = anotherPath.current;
|
||||
}
|
||||
|
||||
function CGPathAddQuadCurveToPoint(aPath, aTransform, cpx, cpy, x, y)
|
||||
@@ -250,6 +265,8 @@ function CGPathMoveToPoint(aPath, aTransform, x, y)
|
||||
aPath.elements[aPath.count++] = { type:kCGPathElementMoveToPoint, x:point.x, y:point.y };
|
||||
}
|
||||
|
||||
var KAPPA = 4.0 * ((SQRT2 - 1.0) / 3.0);
|
||||
|
||||
function CGPathWithEllipseInRect(aRect)
|
||||
{
|
||||
var path = CGPathCreateMutable();
|
||||
|
||||
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
|
||||
/*
|
||||
@global
|
||||
@class CPTableColumn
|
||||
*/
|
||||
CPTableColumnNoResizing = 0;
|
||||
/*
|
||||
@global
|
||||
@class CPTableColumn
|
||||
*/
|
||||
CPTableColumnAutoresizingMask = 1;
|
||||
/*
|
||||
@global
|
||||
@class CPTableColumn
|
||||
*/
|
||||
CPTableColumnUserResizingMask = 2;
|
||||
|
||||
#define PurgableInfoMake(aView, aRow) { view:(aView), row:(aRow) }
|
||||
#define PurgableInfoView(anInfo) ((anInfo).view)
|
||||
#define PurgableInfoRow(anInfo) ((anInfo).row)
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTableColumn
|
||||
|
||||
An CPTableColumn object mainly keeps information about the width of the column, its minimum and maximum width; whether the column can be edited or resized; and the cells used to draw the column header and the data in the column. You can change all these attributes of the column by calling the appropriate methods. Please note that the table column does not hold nor has access to the data to be displayed in the column; this data is maintained in the table view's data source.</p>
|
||||
|
||||
<p>Each CPTableColumn object is identified by a CPString, called the column identifier. The reason is that, after a column has been added to a table view, the user might move the columns around, so there is a need to identify the columns regardless of its position in the table.
|
||||
|
||||
@ignore
|
||||
*/
|
||||
@implementation CPTableColumn : CPObject
|
||||
{
|
||||
CPString _identifier;
|
||||
CPView _headerView;
|
||||
|
||||
CPTableView _tableView;
|
||||
|
||||
float _width;
|
||||
float _minWidth;
|
||||
float _maxWidth;
|
||||
|
||||
unsigned _resizingMask;
|
||||
|
||||
CPView _dataView; // default data view for this column
|
||||
|
||||
Object _dataViewData; // cache of data view archives (key=data view UID, value=data view archive)
|
||||
Object _dataViewForView; // mapping from view instances back to their data view prototype (key=view instance UID, value=data view)
|
||||
Object _purgableInfosForDataView; // (key=data view UID, value=)
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the table column with the specified identifier.
|
||||
@param anIdentifier the identifier
|
||||
@return the initialized table column
|
||||
*/
|
||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = anIdentifier;
|
||||
|
||||
_width = 40.0;
|
||||
_minWidth = 8.0;
|
||||
_maxWidth = 1000.0;
|
||||
|
||||
var dataView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[dataView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateHighlighted];
|
||||
|
||||
[self setDataView:dataView];
|
||||
|
||||
_headerView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[_headerView setBackgroundColor:[CPColor greenColor]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_dataViewData = {};
|
||||
_dataViewForView = {};
|
||||
_purgableInfosForDataView = {};
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the table column's identifier
|
||||
@param anIdentifier the new identifier
|
||||
*/
|
||||
- (void)setIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
_identifier = anIdentifier;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the table column's identifier
|
||||
*/
|
||||
- (CPString)identifier
|
||||
{
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
// Setting the CPTableView
|
||||
/*!
|
||||
Sets the table's view. This is called automatically by Cappuccino.
|
||||
@param aTableView the new table view
|
||||
*/
|
||||
- (void)setTableView:(CPTableView)aTableView
|
||||
{
|
||||
_tableView = aTableView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's table view.
|
||||
*/
|
||||
- (CPTableView)tableView
|
||||
{
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
// Controlling size
|
||||
/*!
|
||||
Sets the column's width.
|
||||
@param aWidth the new column width
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth
|
||||
{
|
||||
_width = aWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's width
|
||||
*/
|
||||
- (float)width
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets column's minimum width.
|
||||
@param aWidth the new minimum column width
|
||||
*/
|
||||
- (void)setMinWidth:(float)aWidth
|
||||
{
|
||||
if (_width < (_minWidth = aWidth))
|
||||
[self setWidth:_minWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
The column's minimum width
|
||||
*/
|
||||
- (float)minWidth
|
||||
{
|
||||
return _minWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the column's maximum width.
|
||||
@param aWidth the new maximum width
|
||||
*/
|
||||
- (void)setMaxWidth:(float)aWidth
|
||||
{
|
||||
if (_width > (_maxmimumWidth = aWidth))
|
||||
[self setWidth:_maxWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the resizing mask. The mask is one of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
@param aMask the new resizing mask
|
||||
*/
|
||||
- (void)setResizingMask:(unsigned)aMask
|
||||
{
|
||||
_resizingMask = aMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's resizing mask. One of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
*/
|
||||
- (unsigned)resizingMask
|
||||
{
|
||||
return _resizingMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resizes the column according to the min, max and set width.
|
||||
*/
|
||||
- (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];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the column in this data is editable.
|
||||
@param aFlag <code>YES</code> means the column data is editable
|
||||
*/
|
||||
- (void)setEditable:(BOOL)aFlag
|
||||
{
|
||||
_isEditable = aFlag;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the column data is editable.
|
||||
*/
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
//Setting the column header view
|
||||
|
||||
/*!
|
||||
Sets the view that draws the column's header.
|
||||
@param aHeaderView the view that will draws the column header
|
||||
*/
|
||||
|
||||
- (void)setHeaderView:(CPView)aView
|
||||
{
|
||||
_headerView = aView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the view that draws the column's header
|
||||
*/
|
||||
- (CPView)headerView
|
||||
{
|
||||
return _headerView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the data cell that draws rows in this column.
|
||||
*/
|
||||
- (void)setDataCell:(CPView <CPCoding>)aView
|
||||
{
|
||||
[self setDataView:aView];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the data view that draws rows in this column.
|
||||
*/
|
||||
- (void)setDataView:(CPView <CPCoding>)aView
|
||||
{
|
||||
if (_dataView)
|
||||
_dataViewData[[_dataView UID]] = nil;
|
||||
|
||||
_dataView = aView;
|
||||
_dataViewData[[aView UID]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the data cell that draws rows in this column
|
||||
*/
|
||||
- (CPCell)dataCell
|
||||
{
|
||||
return _dataView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the data view that draws rows in this column
|
||||
*/
|
||||
- (CPView)dataView
|
||||
{
|
||||
return [self dataCell];
|
||||
}
|
||||
|
||||
/*!
|
||||
By default returns the value from <code>dataCell</code>. This can
|
||||
be overridden by a subclass to return different cells for different
|
||||
rows.
|
||||
@param aRowIndex the index of the row to obtain the cell for
|
||||
*/
|
||||
- (CPCell)dataCellForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataView];
|
||||
}
|
||||
|
||||
- (CPView)dataViewForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataCellForRow:aRowIndex];
|
||||
}
|
||||
/*
|
||||
- (void)_markViewAsPurgable:(CPView)aView
|
||||
{
|
||||
var viewUID = [aView UID],
|
||||
dataViewUID = [_dataViewForView[viewUID] UID];
|
||||
|
||||
if (!_purgableInfosForDataView[dataViewUID])
|
||||
_purgableInfosForDataView[dataViewUID] = [CPDictionary dictionary];
|
||||
|
||||
[_purgableInfosForDataView[dataViewUID] setObject:aView forKey:viewUID];
|
||||
}
|
||||
*/
|
||||
- (void)_markView:(CPView)aView inRow:(unsigned)aRow asPurgable:(BOOL)isPurgable
|
||||
{
|
||||
var viewUID = [aView UID],
|
||||
dataViewUID = [_dataViewForView[viewUID] UID];
|
||||
|
||||
if (!_purgableInfosForDataView[dataViewUID])
|
||||
{
|
||||
if (!isPurgable)
|
||||
return;
|
||||
|
||||
_purgableInfosForDataView[dataViewUID] = {};
|
||||
}
|
||||
|
||||
if (!isPurgable) {
|
||||
if (_purgableInfosForDataView[dataViewUID][viewUID])
|
||||
CPLog.warn("removing unpurgable " + _purgableInfosForDataView[dataViewUID][viewUID]);
|
||||
delete _purgableInfosForDataView[dataViewUID][viewUID];
|
||||
}
|
||||
else
|
||||
_purgableInfosForDataView[dataViewUID][viewUID] = PurgableInfoMake(aView, aRow);
|
||||
}
|
||||
|
||||
- (CPView)_newDataViewForRow:(int)aRowIndex avoidingRows:(CPRange)rows
|
||||
{
|
||||
var view = [self dataViewForRow:aRowIndex],
|
||||
viewUID = [view UID],
|
||||
purgableInfos = _purgableInfosForDataView[viewUID];
|
||||
|
||||
if (purgableInfos)
|
||||
{
|
||||
for (var key in purgableInfos)
|
||||
{
|
||||
var info = purgableInfos[key];
|
||||
//if (!CPLocationInRange(PurgableInfoRow(info), rows))
|
||||
//{
|
||||
//CPLog.debug("yes, a purged view is usable, its called: " + PurgableInfoView(info));
|
||||
delete purgableInfos[key];
|
||||
return PurgableInfoView(info);
|
||||
//}
|
||||
//else
|
||||
// CPLog.warn("avoiding");
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't cached an archive of the data view, do it now
|
||||
if (!_dataViewData[viewUID])
|
||||
_dataViewData[viewUID] = [CPKeyedArchiver archivedDataWithRootObject:view];
|
||||
|
||||
// unarchive the data view cache
|
||||
var newView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[viewUID]];
|
||||
|
||||
// map the new view's UID to it's data view prototype
|
||||
_dataViewForView[[newView UID]] = view;
|
||||
|
||||
CPLog.warn("creating cell: %s", newView);
|
||||
|
||||
return newView;
|
||||
}
|
||||
|
||||
- (void)_purge
|
||||
{
|
||||
for (var viewUID in _purgableInfosForDataView)
|
||||
{
|
||||
var purgableInfos = _purgableInfosForDataView[viewUID];
|
||||
|
||||
for (var key in purgableInfos)
|
||||
{
|
||||
var view = PurgableInfoView(purgableInfos[key]);
|
||||
|
||||
if (!view)
|
||||
CPLog.info("key="+key+" view=" + view + " purgableInfos[key]="+purgableInfos[key])
|
||||
else if (view._superview) {
|
||||
//CPLog.error("PURGING: (removing)" + view);
|
||||
//[view removeFromSuperview];
|
||||
[view setHidden:YES];
|
||||
}
|
||||
//else
|
||||
// CPLog.warn("PURGING: (already removed)" + view);
|
||||
|
||||
//delete purgableInfos[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
CPTableColumnHeaderViewKey = @"CPTableColumnHeaderViewKey",
|
||||
CPTableColumnDataViewKey = @"CPTableColumnDataViewKey",
|
||||
CPTableColumnWidthKey = @"CPTableColumnWidthKey",
|
||||
CPTableColumnMinWidthKey = @"CPTableColumnMinWidthKey",
|
||||
CPTableColumnMaxWidthKey = @"CPTableColumnMaxWidthKey",
|
||||
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey";
|
||||
|
||||
@implementation CPTableColumn (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = [aCoder decodeObjectForKey:CPTableColumnIdentifierKey];
|
||||
|
||||
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||
[self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||
|
||||
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
||||
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
||||
_maxWidth = [aCoder decodeFloatForKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
_resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPTableColumnIdentifierKey];
|
||||
|
||||
[aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||
|
||||
[aCoder encodeObject:_width forKey:CPTableColumnWidthKey];
|
||||
[aCoder encodeObject:_minWidth forKey:CPTableColumnMinWidthKey];
|
||||
[aCoder encodeObject:_maxWidth forKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
[aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
|
||||
@implementation CPPlatform : CPObject
|
||||
{
|
||||
}
|
||||
|
||||
+ (void)bootstrap
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var body = document.getElementsByTagName("body")[0];
|
||||
|
||||
body.innerHTML = ""; // Get rid of anything that might be lingering in the body element.
|
||||
body.style.overflow = "hidden";
|
||||
|
||||
if (document.documentElement)
|
||||
document.documentElement.style.overflow = "hidden";
|
||||
#endif
|
||||
|
||||
[CPPlatformString bootstrap];
|
||||
[CPPlatformWindow setPrimaryPlatformWindow:[[CPPlatformWindow alloc] _init]];
|
||||
}
|
||||
|
||||
+ (BOOL)isBrowser
|
||||
{
|
||||
return typeof window.cpIsDesktop === "undefined";
|
||||
}
|
||||
|
||||
+ (BOOL)supportsDragAndDrop
|
||||
{
|
||||
return CPFeatureIsCompatible(CPHTMLDragAndDropFeature);
|
||||
}
|
||||
|
||||
+ (BOOL)supportsNativeMainMenu
|
||||
{
|
||||
return (typeof window["cpSetMainMenu"] === "function");
|
||||
}
|
||||
|
||||
+ (void)terminateApplication
|
||||
{
|
||||
if (typeof window["cpTerminate"] === "function")
|
||||
window.cpTerminate();
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,104 @@
|
||||
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
#include "../CoreGraphics/CGGeometry.h"
|
||||
#include "Platform.h"
|
||||
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
var DOMIFrameElement = nil,
|
||||
DOMSpanElement = nil,
|
||||
DefaultFont = nil;
|
||||
#endif
|
||||
|
||||
@implementation CPPlatformString : CPObject
|
||||
{
|
||||
}
|
||||
|
||||
+ (void)bootstrap
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
DOMIFrameElement = document.createElement("iframe");
|
||||
|
||||
DOMIFrameElement.name = name = "iframe_" + FLOOR(RAND() * 10000);
|
||||
DOMIFrameElement.style.position = "absolute";
|
||||
DOMIFrameElement.style.left = "-100px";
|
||||
DOMIFrameElement.style.top = "-100px";
|
||||
DOMIFrameElement.style.width = "1px";
|
||||
DOMIFrameElement.style.height = "1px";
|
||||
DOMIFrameElement.style.borderWidth = "0px";
|
||||
DOMIFrameElement.style.background = "blue";
|
||||
DOMIFrameElement.style.overflow = "hidden";
|
||||
DOMIFrameElement.style.zIndex = 100000000000;
|
||||
|
||||
document.getElementsByTagName("body")[0].appendChild(DOMIFrameElement);
|
||||
|
||||
var DOMIFrameDocument = (DOMIFrameElement.contentDocument || DOMIFrameElement.contentWindow.document);
|
||||
|
||||
DOMIFrameDocument.write("<html><head></head><body></body></html>");
|
||||
DOMIFrameDocument.close();
|
||||
|
||||
DOMSpanElement = DOMIFrameDocument.createElement("span");
|
||||
|
||||
DOMSpanElement.style.position = "absolute";
|
||||
DOMSpanElement.style.whiteSpace = "pre";
|
||||
DOMSpanElement.style.visibility = "visible";
|
||||
DOMSpanElement.style.padding = "0px";
|
||||
DOMSpanElement.style.margin = "0px";
|
||||
DOMSpanElement.style.background = "red";
|
||||
|
||||
DOMIFrameDocument.getElementsByTagName("body")[0].appendChild(DOMSpanElement);
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (CGSize)sizeOfString:(CPString)aString withFont:(CPFont)aFont forWidth:(float)aWidth
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (!aFont)
|
||||
{
|
||||
if (!DefaultFont)
|
||||
DefaultFont = [CPFont systemFontOfSize:12.0];
|
||||
|
||||
aFont = DefaultFont;
|
||||
}
|
||||
|
||||
var style = DOMSpanElement.style;
|
||||
|
||||
if (aWidth === NULL)
|
||||
{
|
||||
style.width = "";
|
||||
style.whiteSpace = "pre";
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
style.width = ROUND(aWidth) + "px";
|
||||
|
||||
if (document.attachEvent)
|
||||
style.wordWrap = "break-word";
|
||||
|
||||
else
|
||||
{
|
||||
style.whiteSpace = "-o-pre-wrap";
|
||||
style.whiteSpace = "-pre-wrap";
|
||||
style.whiteSpace = "-moz-pre-wrap";
|
||||
style.whiteSpace = "pre-wrap";
|
||||
}
|
||||
}
|
||||
|
||||
style.font = [aFont cssString];
|
||||
|
||||
if (CPFeatureIsCompatible(CPJavascriptInnerTextFeature))
|
||||
DOMSpanElement.innerText = aString;
|
||||
|
||||
else if (CPFeatureIsCompatible(CPJavascriptTextContentFeature))
|
||||
DOMSpanElement.textContent = aString;
|
||||
|
||||
return _CGSizeMake(DOMSpanElement.clientWidth, DOMSpanElement.clientHeight);
|
||||
#else
|
||||
return _CGSizeMakeZero();
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,181 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
#import "Platform.h"
|
||||
#import "../CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
var PrimaryPlatformWindow = NULL;
|
||||
|
||||
@implementation CPPlatformWindow : CPObject
|
||||
{
|
||||
CGRect _contentRect;
|
||||
|
||||
CPInteger _level;
|
||||
BOOL _hasShadow;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
DOMWindow _DOMWindow;
|
||||
|
||||
DOMElement _DOMBodyElement;
|
||||
DOMElement _DOMFocusElement;
|
||||
|
||||
CPArray _windowLevels;
|
||||
CPDictionary _windowLayers;
|
||||
|
||||
BOOL _mouseIsDown;
|
||||
CPWindow _mouseDownWindow;
|
||||
CPTimeInterval _lastMouseUp;
|
||||
CPTimeInterval _lastMouseDown;
|
||||
|
||||
Object _charCodes;
|
||||
unsigned _keyCode;
|
||||
|
||||
BOOL _DOMEventMode;
|
||||
|
||||
// Native Pasteboard Support
|
||||
DOMElement _DOMPasteboardElement;
|
||||
CPEvent _pasteboardKeyDownEvent;
|
||||
|
||||
CPString _overriddenEventType;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (CPPlatformWindow)primaryPlatformWindow
|
||||
{
|
||||
return PrimaryPlatformWindow;
|
||||
}
|
||||
|
||||
+ (void)setPrimaryPlatformWindow:(CPPlatformWindow)aPlatformWindow
|
||||
{
|
||||
PrimaryPlatformWindow = aPlatformWindow;
|
||||
}
|
||||
|
||||
- (id)initWithContentRect:(CGRect)aRect
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_contentRect = _CGRectMakeCopy(aRect);
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_windowLevels = [];
|
||||
_windowLayers = [CPDictionary dictionary];
|
||||
|
||||
_charCodes = {};
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithContentRect:_CGRectMake(0.0, 0.0, 400.0, 500.0)];
|
||||
}
|
||||
|
||||
- (CGRect)contentRect
|
||||
{
|
||||
return _CGRectMakeCopy(_contentRect);
|
||||
}
|
||||
|
||||
- (CGRect)contentBounds
|
||||
{
|
||||
var contentBounds = [self contentRect];
|
||||
|
||||
contentBounds.origin = _CGPointMakeZero();
|
||||
|
||||
return contentBounds;
|
||||
}
|
||||
|
||||
- (CGRect)visibleFrame
|
||||
{
|
||||
var frame = [self contentBounds];
|
||||
|
||||
frame.origin = CGPointMakeZero();
|
||||
|
||||
if ([CPMenu menuBarVisible])
|
||||
{
|
||||
var menuBarHeight = [[CPApp mainMenu] menuBarHeight];
|
||||
|
||||
frame.origin.y += menuBarHeight;
|
||||
frame.size.height -= menuBarHeight;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (void)usableContentFrame
|
||||
{
|
||||
return [self visibleFrame];
|
||||
}
|
||||
|
||||
- (void)setContentRect:(CGRect)aRect
|
||||
{
|
||||
if (!aRect || _CGRectEqualToRect(_contentRect, aRect))
|
||||
return;
|
||||
|
||||
_contentRect = _CGRectMakeCopy(aRect);
|
||||
|
||||
[self updateNativeContentRect];
|
||||
}
|
||||
|
||||
- (void)updateFromNativeContentRect
|
||||
{
|
||||
[self setContentRect:[self nativeContentRect]];
|
||||
}
|
||||
|
||||
- (CGPoint)convertBaseToScreen:(CGPoint)aPoint
|
||||
{
|
||||
var contentRect = [self contentRect];
|
||||
|
||||
return _CGPointMake(aPoint.x + _CGRectGetMinX(contentRect), aPoint.y + _CGRectGetMinY(contentRect));
|
||||
}
|
||||
|
||||
- (CGPoint)convertScreenToBase:(CGPoint)aPoint
|
||||
{
|
||||
var contentRect = [self contentRect];
|
||||
|
||||
return _CGPointMake(aPoint.x - _CGRectGetMinX(contentRect), aPoint.y - _CGRectGetMinY(contentRect));
|
||||
}
|
||||
|
||||
- (BOOL)isVisible
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
return _DOMWindow !== NULL;
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)setLevel:(CPInteger)aLevel
|
||||
{
|
||||
_level = aLevel;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMWindow && _DOMWindow.cpSetLevel)
|
||||
_DOMWindow.cpSetLevel(aLevel);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)setHasShadow:(BOOL)shouldHaveShadow
|
||||
{
|
||||
_hasShadow = shouldHaveShadow;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMWindow && _DOMWindow.cpSetHasShadow)
|
||||
_DOMWindow.cpSetHasShadow(shouldHaveShadow);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)supportsFullPlatformWindows
|
||||
{
|
||||
return [CPPlatform isBrowser];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#if PLATFORM(BROWSER)
|
||||
@import "CPPlatformWindow+DOM.j"
|
||||
#endif
|
||||
@@ -20,6 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define DOM_OPTIMIZATION 0
|
||||
|
||||
#define SetStyleOrigin 0
|
||||
#define SetStyleLeftTop 0
|
||||
#define SetStyleRightTop 1
|
||||
@@ -45,7 +47,49 @@
|
||||
CPDOMDisplayServerInstructions[__index + 2] = aTransform;\
|
||||
CPDOMDisplayServerInstructions[__index + 3] = x;\
|
||||
CPDOMDisplayServerInstructions[__index + 4] = y;
|
||||
|
||||
#if !DOM_OPTIMIZATION
|
||||
#define CPDOMDisplayServerSetStyleLeftTop(aDOMElement, aTransform, aLeft, aTop) \
|
||||
if (aTransform) var ____p = _CGPointApplyAffineTransform(CGPointMake(aLeft, aTop), aTransform); \
|
||||
else var ____p = _CGPointMake(aLeft, aTop); \
|
||||
aDOMElement.style.left = ROUND(____p.x) + "px";\
|
||||
aDOMElement.style.top = ROUND(____p.y) + "px";
|
||||
|
||||
#define CPDOMDisplayServerSetStyleRightTop(aDOMElement, aTransform, aRight, aTop) \
|
||||
if (aTransform) var ____p = _CGPointApplyAffineTransform(CGPointMake(aRight, aTop), aTransform); \
|
||||
else var ____p = _CGPointMake(aRight, aTop); \
|
||||
aDOMElement.style.right = ROUND(____p.x) + "px";\
|
||||
aDOMElement.style.top = ROUND(____p.y) + "px";
|
||||
|
||||
#define CPDOMDisplayServerSetStyleLeftBottom(aDOMElement, aTransform, aLeft, aBottom) \
|
||||
if (aTransform) var ____p = _CGPointApplyAffineTransform(CGPointMake(aLeft, aBottom), aTransform); \
|
||||
else var ____p = _CGPointMake(aLeft, aBottom); \
|
||||
aDOMElement.style.left = ROUND(____p.x) + "px";\
|
||||
aDOMElement.style.bottom = ROUND(____p.y) + "px";
|
||||
|
||||
#define CPDOMDisplayServerSetStyleRightBottom(aDOMElement, aTransform, aRight, aBottom) \
|
||||
if (aTransform) var ____p = _CGPointApplyAffineTransform(CGPointMake(aRight, aBottom), aTransform); \
|
||||
else var ____p = _CGPointMake(aRight, aBottom); \
|
||||
aDOMElement.style.right = ROUND(____p.x) + "px";\
|
||||
aDOMElement.style.bottom = ROUND(____p.y) + "px";
|
||||
|
||||
#define CPDOMDisplayServerSetStyleSize(aDOMElement, aWidth, aHeight) \
|
||||
aDOMElement.style.width = MAX(0.0, ROUND(aWidth)) + "px";\
|
||||
aDOMElement.style.height = MAX(0.0, ROUND(aHeight)) + "px";
|
||||
|
||||
#define CPDOMDisplayServerSetSize(aDOMElement, aWidth, aHeight) \
|
||||
aDOMElement.width = MAX(0.0, ROUND(aWidth));\
|
||||
aDOMElement.height = MAX(0.0, ROUND(aHeight));
|
||||
|
||||
#define CPDOMDisplayServerAppendChild(aParentElement, aChildElement) aParentElement.appendChild(aChildElement)
|
||||
|
||||
#define CPDOMDisplayServerInsertBefore(aParentElement, aChildElement, aBeforeElement) aParentElement.insertBefore(aChildElement, aBeforeElement)
|
||||
|
||||
#define CPDOMDisplayServerRemoveChild(aParentElement, aChildElement) aParentElement.removeChild(aChildElement)
|
||||
|
||||
#define PREPARE_DOM_OPTIMIZATION()
|
||||
#define EXECUTE_DOM_INSTRUCTIONS()
|
||||
|
||||
#else
|
||||
#define CPDOMDisplayServerSetStyleLeftTop(aDOMElement, aTransform, aLeft, aTop) CPDOMDisplayServerSetStyleOrigin(SetStyleLeftTop, aDOMElement, aTransform, aLeft, aTop)
|
||||
|
||||
#define CPDOMDisplayServerSetStyleRightTop(aDOMElement, aTransform, aRight, aTop) CPDOMDisplayServerSetStyleOrigin(SetStyleRightTop, aDOMElement, aTransform, aRight, aTop)
|
||||
@@ -99,27 +143,70 @@
|
||||
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = RemoveChild;\
|
||||
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aParentElement;\
|
||||
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aChildElement;
|
||||
|
||||
//#dfeine CPDOMDisplayServerCustomAction()
|
||||
|
||||
#define CPDOMDisplayServerAddView(aView)\
|
||||
#define PREPARE_DOM_OPTIMIZATION()\
|
||||
CPDOMDisplayServerInstructions = [];\
|
||||
CPDOMDisplayServerInstructionCount = 0;
|
||||
#define EXECUTE_DOM_INSTRUCTIONS()\
|
||||
var index = 0;\
|
||||
while (index < CPDOMDisplayServerInstructionCount)\
|
||||
{\
|
||||
var ___hash = [aView hash];\
|
||||
if (typeof (CPDOMDisplayServerViewsContext[___hash]) == "undefined")\
|
||||
{\
|
||||
CPDOMDisplayServerViews[CPDOMDisplayServerViewsCount++] = aView;\
|
||||
CPDOMDisplayServerViewsContext[___hash] = aView;\
|
||||
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 SetSize: var element = CPDOMDisplayServerInstructions[index];\
|
||||
CPDOMDisplayServerInstructions[index++] = nil;\
|
||||
element.CPDOMDisplayContext[SetSize] = -1;\
|
||||
element.width = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++]));\
|
||||
element.height = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++]));\
|
||||
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) { CPLog("e " + e + " " + instruction); }\
|
||||
}\
|
||||
}\
|
||||
|
||||
#define CPDOMDisplayServerRemoveView(aView)\
|
||||
{\
|
||||
var index = CPDOMDisplayServerViewsContext[[aView hash]];\
|
||||
if (typeof index != "undefined") \
|
||||
{\
|
||||
CPDOMDisplayServerViewsContext[[aView hash]];\
|
||||
CPDOMDisplayServerViews[index] = NULL;\
|
||||
}\
|
||||
}\
|
||||
|
||||
|
||||
CPDOMDisplayServerInstructionCount = 0;
|
||||
#endif
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
CPDOMDisplayServerViews = [];
|
||||
CPDOMDisplayServerViewsCount = 0;
|
||||
CPDOMDisplayServerViewsContext = {};
|
||||
|
||||
@implementation CPDOMDisplayServer : CPObject
|
||||
{
|
||||
}
|
||||
|
||||
+ (void)start
|
||||
{
|
||||
CPDOMDisplayRunLoop = [CPRunLoop currentRunLoop];
|
||||
|
||||
[CPDOMDisplayRunLoop performSelector:@selector(run) target:CPDOMDisplayServer argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
}
|
||||
|
||||
+ (void)run
|
||||
{
|
||||
while (CPDOMDisplayServerInstructionCount || CPDOMDisplayServerViewsCount)
|
||||
{
|
||||
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 SetSize: var element = CPDOMDisplayServerInstructions[index];
|
||||
|
||||
CPDOMDisplayServerInstructions[index++] = nil;
|
||||
|
||||
element.CPDOMDisplayContext[SetSize] = -1;
|
||||
|
||||
element.width = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++]));
|
||||
element.height = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++]));
|
||||
|
||||
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) { CPLog("here?" + instruction) }
|
||||
}
|
||||
|
||||
CPDOMDisplayServerInstructionCount = 0;
|
||||
|
||||
var views = CPDOMDisplayServerViews,
|
||||
index = 0,
|
||||
count = CPDOMDisplayServerViewsCount;
|
||||
|
||||
// We don't reset CPDOMDisplayServerViewsContext because it can serve for displays that are coming...
|
||||
CPDOMDisplayServerViews = [];
|
||||
CPDOMDisplayServerViewsCount = 0;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var view = views[index];
|
||||
|
||||
delete CPDOMDisplayServerViewsContext[[view hash]];
|
||||
|
||||
[view layoutIfNeeded];
|
||||
[view displayIfNeeded];
|
||||
}
|
||||
}
|
||||
|
||||
[CPDOMDisplayRunLoop performSelector:@selector(run) target:CPDOMDisplayServer argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
[CPDOMDisplayServer start];
|
||||
@@ -1,998 +0,0 @@
|
||||
/*
|
||||
* 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 = [];
|
||||
|
||||
// Define up here so compressor knows about em.
|
||||
var CPDOMWindowGetFrame,
|
||||
CPDOMEventGetClickCount,
|
||||
CPDOMEventStop;
|
||||
|
||||
@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;
|
||||
|
||||
JSObject _charCodes;
|
||||
unsigned _keyCode;
|
||||
|
||||
BOOL _DOMEventMode;
|
||||
|
||||
// Native Pasteboard Support
|
||||
DOMElement _DOMPasteboardElement;
|
||||
CPEvent _pasteboardKeyDownEvent;
|
||||
|
||||
CPString _overriddenEventType;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the shared DOMWindowBridge.
|
||||
*/
|
||||
+ (id)sharedDOMWindowBridge
|
||||
{
|
||||
if (!CPSharedDOMWindowBridge)
|
||||
CPSharedDOMWindowBridge = [[CPDOMWindowBridge alloc] _initWithDOMWindow:window];
|
||||
|
||||
return CPSharedDOMWindowBridge;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)aFrame
|
||||
{
|
||||
alert("unimplemented");
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (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";
|
||||
_DOMBodyElement.style.webkitTouchCallout = "none";
|
||||
|
||||
[CPString _resetSize];
|
||||
|
||||
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); },
|
||||
|
||||
touchEventSelector = @selector(_bridgeTouchEvent:),
|
||||
touchEventImplementation = class_getMethodImplementation(theClass, touchEventSelector),
|
||||
touchEventCallback = function (anEvent) { touchEventImplementation(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);
|
||||
|
||||
|
||||
theDocument.addEventListener(CPDOMEventTouchStart, touchEventCallback, NO);
|
||||
theDocument.addEventListener(CPDOMEventTouchEnd, touchEventCallback, NO);
|
||||
theDocument.addEventListener(CPDOMEventTouchMove, touchEventCallback, NO);
|
||||
theDocument.addEventListener(CPDOMEventTouchCancel, touchEventCallback, 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);
|
||||
}
|
||||
|
||||
- (CPArray)orderedWindowsAtLevel:(int)aLevel
|
||||
{
|
||||
var layer = [self layerAtLevel:aLevel create:NO];
|
||||
|
||||
if (!layer)
|
||||
return [];
|
||||
|
||||
return [layer orderedWindows];
|
||||
}
|
||||
|
||||
- (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)];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (id)_dragHitTest:(CPPoint)aPoint pasteboard:(CPPasteboard)aPasteboard
|
||||
{
|
||||
var levels = _windowLevels,
|
||||
layers = _windowLayers,
|
||||
levelCount = levels.length;
|
||||
|
||||
while (levelCount--)
|
||||
{
|
||||
// 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];
|
||||
|
||||
if ([theWindow containsPoint:aPoint])
|
||||
return [theWindow _dragHitTest:aPoint pasteboard:aPasteboard];
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (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)
|
||||
{
|
||||
var candidateWindow = windows[windowCount];
|
||||
|
||||
if (!candidateWindow._ignoresMouseEvents && [candidateWindow containsPoint:location])
|
||||
theWindow = candidateWindow;
|
||||
}
|
||||
}
|
||||
|
||||
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 KeyCodesToPrevent = {},
|
||||
CharacterKeysToPrevent = {},
|
||||
KeyCodesWithoutKeyPressEvents = { '8':1, '9':1, '37':1, '38':1, '39':1, '40':1, '46':1, '33':1, '34':1 };
|
||||
|
||||
var CTRL_KEY_CODE = 17;
|
||||
|
||||
@implementation CPDOMWindowBridge (Events)
|
||||
|
||||
/*!
|
||||
When using command (mac) or control (windows), keys are propagated to the browser by default.
|
||||
To prevent a character key from propagating (to prevent its default action, and instead use it
|
||||
in your own application), use these methods. These methods are additive -- the list builds until you clear it.
|
||||
|
||||
@param characters a list of characters to stop propagating keypresses to the browser.
|
||||
*/
|
||||
- (void)preventCharacterKeysFromPropagating:(CPArray)characters
|
||||
{
|
||||
for(var i=characters.length; i>0; i--)
|
||||
CharacterKeysToPrevent[""+characters[i-1].toLowerCase()] = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
@param character a character to stop propagating keypresses to the browser.
|
||||
*/
|
||||
- (void)preventCharacterKeyFromPropagating:(CPString)character
|
||||
{
|
||||
CharacterKeysToPrevent[character.toLowerCase()] = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
Clear the list of characters for which we are not sending keypresses to the browser.
|
||||
*/
|
||||
- (void)clearCharacterKeysToPreventFromPropagating
|
||||
{
|
||||
CharacterKeysToPrevent = {};
|
||||
}
|
||||
|
||||
/*!
|
||||
Prevent these keyCodes from sending their keypresses to the browser.
|
||||
@param keyCodes an array of keycodes to prevent propagation.
|
||||
*/
|
||||
- (void)preventKeyCodesFromPropagating:(CPArray)keyCodes
|
||||
{
|
||||
for(var i=keyCodes.length; i>0; i--)
|
||||
KeyCodesToPrevent[keyCodes[i-1]] = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
Prevent this keyCode from sending its key events to the browser.
|
||||
@param keyCode a keycode to prevent propagation.
|
||||
*/
|
||||
- (void)preventKeyCodeFromPropagating:(CPString)keyCode
|
||||
{
|
||||
KeyCodesToPrevent[keyCode] = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
Clear the list of keyCodes for which we are not sending keypresses to the browser.
|
||||
*/
|
||||
- (void)clearKeyCodesToPreventFromPropagating
|
||||
{
|
||||
KeyCodesToPrevent = {};
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (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 = _CPEventFromNativeMouseEvent(aDOMEvent, CPLeftMouseUp, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseUp, timestamp, location), 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 = _CPEventFromNativeMouseEvent(aDOMEvent, CPLeftMouseDown, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseDown, timestamp, location), 0);
|
||||
|
||||
_mouseIsDown = YES;
|
||||
_lastMouseDown = event;
|
||||
|
||||
break;
|
||||
|
||||
case CPDOMEventMouseMoved: if (_DOMEventMode)
|
||||
return;
|
||||
|
||||
event = _CPEventFromNativeMouseEvent(aDOMEvent, _mouseIsDown ? CPLeftMouseDragged : CPMouseMoved, location, modifierFlags, timestamp, windowNumber, nil, -1, 1, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (event)
|
||||
{
|
||||
event._DOMEvent = aDOMEvent;
|
||||
|
||||
[CPApp sendEvent:event];
|
||||
}
|
||||
|
||||
if (StopDOMEventPropagation)
|
||||
CPDOMEventStop(aDOMEvent);
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"});
|
||||
}
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (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;
|
||||
|
||||
//We want to stop propagation if this is a command key AND this character or keycode has been added to our blacklist
|
||||
StopDOMEventPropagation = !(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) ||
|
||||
CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] ||
|
||||
KeyCodesToPrevent[aDOMEvent.keyCode];
|
||||
|
||||
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.setNativeTimeout(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.setNativeTimeout(function() { [self _clearPasteboardElement]; }, 0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (StopDOMEventPropagation)
|
||||
CPDOMEventStop(aDOMEvent);
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"});
|
||||
}
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_bridgeScrollEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
if(!aDOMEvent)
|
||||
aDOMEvent = window.event;
|
||||
|
||||
try
|
||||
{
|
||||
if (CPFeatureIsCompatible(CPJavaScriptMouseWheelValues_8_15))
|
||||
{
|
||||
var x = 0.0,
|
||||
y = 0.0,
|
||||
element = aDOMEvent.target;
|
||||
|
||||
while (element.nodeType !== 1)
|
||||
element = element.parentNode;
|
||||
|
||||
if (element.offsetParent)
|
||||
{
|
||||
do
|
||||
{
|
||||
x += element.offsetLeft;
|
||||
y += element.offsetTop;
|
||||
|
||||
} while (element = element.offsetParent);
|
||||
}
|
||||
|
||||
var location = _CGPointMake((x + ((aDOMEvent.clientX - 8) / 15)), (y + ((aDOMEvent.clientY - 8) / 15)));}
|
||||
else
|
||||
var location = _CGPointMake(aDOMEvent.clientX, aDOMEvent.clientY);
|
||||
|
||||
var deltaX = 0.0,
|
||||
deltaY = 0.0,
|
||||
windowNumber = 0,
|
||||
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] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (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] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"});
|
||||
}
|
||||
}
|
||||
|
||||
// DOM event properties used in mouse bridge code
|
||||
|
||||
// type
|
||||
// clientX
|
||||
// clientY
|
||||
// timestamp
|
||||
// target/srcElement
|
||||
// shiftKey,ctrlKey,altKey,metaKey
|
||||
|
||||
/* @ignore */
|
||||
- (void)_bridgeTouchEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
try
|
||||
{
|
||||
if (aDOMEvent.touches && (aDOMEvent.touches.length == 1 || (aDOMEvent.touches.length == 0 && aDOMEvent.changedTouches.length == 1)))
|
||||
{
|
||||
var newEvent = {};
|
||||
|
||||
switch(aDOMEvent.type)
|
||||
{
|
||||
case CPDOMEventTouchStart: newEvent.type = CPDOMEventMouseDown;
|
||||
break;
|
||||
case CPDOMEventTouchEnd: newEvent.type = CPDOMEventMouseUp;
|
||||
break;
|
||||
case CPDOMEventTouchMove: newEvent.type = CPDOMEventMouseMoved;
|
||||
break;
|
||||
case CPDOMEventTouchCancel: newEvent.type = CPDOMEventMouseUp;
|
||||
break;
|
||||
}
|
||||
|
||||
var touch = aDOMEvent.touches.length ? aDOMEvent.touches[0] : aDOMEvent.changedTouches[0];
|
||||
|
||||
newEvent.clientX = touch.clientX;
|
||||
newEvent.clientY = touch.clientY;
|
||||
|
||||
newEvent.timestamp = aDOMEvent.timestamp;
|
||||
newEvent.target = aDOMEvent.target;
|
||||
|
||||
newEvent.shiftKey = newEvent.ctrlKey = newEvent.altKey = newEvent.metaKey = false;
|
||||
|
||||
newEvent.preventDefault = function(){if(aDOMEvent.preventDefault) aDOMEvent.preventDefault()};
|
||||
newEvent.stopPropagation = function(){if(aDOMEvent.stopPropagation) aDOMEvent.stopPropagation()};
|
||||
|
||||
[self _bridgeMouseEvent:newEvent];
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (aDOMEvent.preventDefault)
|
||||
aDOMEvent.preventDefault();
|
||||
|
||||
if (aDOMEvent.stopPropagation)
|
||||
aDOMEvent.stopPropagation();
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
objj_exception_report(e, {path:@"CPDOMWindowBridge.j"});
|
||||
}
|
||||
|
||||
// handle touch cases specifically
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (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] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"});
|
||||
}
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_clearPasteboardElement
|
||||
{
|
||||
_DOMPasteboardElement.value = "";
|
||||
_DOMPasteboardElement.blur();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CLICK_SPACE_DELTA = 5.0,
|
||||
CLICK_TIME_DELTA = (typeof document != "undefined" && 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();
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@
|
||||
aWindow._isVisible = YES;
|
||||
|
||||
if ([aWindow isFullBridge])
|
||||
[aWindow setFrame:[aWindow._bridge visibleFrame]];
|
||||
[aWindow setFrame:[aWindow._platformWindow usableContentFrame]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ AppKitFiles = FileList['**/*.j'].exclude('CoreGraphics/CGContextCanvas.j', 'Core
|
||||
ObjectiveJ::BundleTask.new(:AppKit) do |t|
|
||||
t.name = 'AppKit'
|
||||
t.identifier = 'com.280n.AppKit'
|
||||
t.version = '0.7.0'
|
||||
t.version = '0.7.1'
|
||||
t.author = '280 North, Inc.'
|
||||
t.email = 'feedback @nospam@ 280north.com'
|
||||
t.summary = 'AppKit classes for Cappuccino'
|
||||
@@ -53,6 +53,7 @@ file_d $THEME_PRODUCT => ThemeFiles << $ENVIRONMENT_PRODUCT do
|
||||
puts str
|
||||
end
|
||||
end
|
||||
rake abort if ($? != 0)
|
||||
end
|
||||
|
||||
task :build => [:build_subprojects, :AppKit, $ENVIRONMENT_PRODUCT, $THEME_PRODUCT, $ENVIRONMENT_THEME_PRODUCT]
|
||||
|
||||
|
After Width: | Height: | Size: 956 B |
|
After Width: | Height: | Size: 976 B |
|
After Width: | Height: | Size: 958 B |
|
After Width: | Height: | Size: 938 B |
|
After Width: | Height: | Size: 131 B |
|
After Width: | Height: | Size: 211 B |
|
After Width: | Height: | Size: 202 B |
|
After Width: | Height: | Size: 296 B |
|
After Width: | Height: | Size: 289 B |
|
After Width: | Height: | Size: 598 B |
|
After Width: | Height: | Size: 625 B |
|
After Width: | Height: | Size: 640 B |
|
After Width: | Height: | Size: 657 B |