mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-10 20:57:11 +00:00
Compare commits
+1
-2
@@ -2,6 +2,5 @@
|
|||||||
Frameworks
|
Frameworks
|
||||||
Build
|
Build
|
||||||
Demos
|
Demos
|
||||||
./Aristo
|
Aristo
|
||||||
WebSite
|
WebSite
|
||||||
.push-package
|
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
[submodule "External/narwhal"]
|
||||||
|
path = External/narwhal
|
||||||
|
url = git://github.com/tlrobinson/narwhal.git
|
||||||
|
[submodule "External/jack"]
|
||||||
|
path = External/jack
|
||||||
|
url = git://github.com/tlrobinson/jack.git
|
||||||
|
[submodule "External/browserjs"]
|
||||||
|
path = External/browserjs
|
||||||
|
url = git://github.com/tlrobinson/browserjs.git
|
||||||
|
[submodule "External/ojunit"]
|
||||||
|
path = External/ojunit
|
||||||
|
url = git://github.com/280north/ojunit.git
|
||||||
+2
-20
@@ -25,26 +25,19 @@
|
|||||||
@import "CPAnimation.j"
|
@import "CPAnimation.j"
|
||||||
@import "CPApplication.j"
|
@import "CPApplication.j"
|
||||||
@import "CPBezierPath.j"
|
@import "CPBezierPath.j"
|
||||||
@import "CPBox.j"
|
|
||||||
@import "CPBrowser.j"
|
|
||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
@import "CPButtonBar.j"
|
@import "CPButtonBar.j"
|
||||||
@import "CPCheckBox.j"
|
@import "CPCheckBox.j"
|
||||||
@import "CPCib.j"
|
@import "CPCib.j"
|
||||||
@import "CPCibConnector.j"
|
|
||||||
@import "CPCibControlConnector.j"
|
|
||||||
@import "CPCibLoading.j"
|
@import "CPCibLoading.j"
|
||||||
@import "CPCibOutletConnector.j"
|
|
||||||
@import "CPClipView.j"
|
@import "CPClipView.j"
|
||||||
@import "CPCollectionView.j"
|
@import "CPCollectionView.j"
|
||||||
@import "CPCollectionViewItem.j"
|
|
||||||
@import "CPColor.j"
|
@import "CPColor.j"
|
||||||
@import "CPColorPanel.j"
|
@import "CPColorPanel.j"
|
||||||
@import "CPColorWell.j"
|
@import "CPColorWell.j"
|
||||||
@import "CPCompatibility.j"
|
@import "CPCompatibility.j"
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPCookie.j"
|
@import "CPCookie.j"
|
||||||
@import "CPCursor.j"
|
|
||||||
@import "CPDocument.j"
|
@import "CPDocument.j"
|
||||||
@import "CPDocumentController.j"
|
@import "CPDocumentController.j"
|
||||||
@import "CPEvent.j"
|
@import "CPEvent.j"
|
||||||
@@ -53,41 +46,30 @@
|
|||||||
@import "CPFont.j"
|
@import "CPFont.j"
|
||||||
@import "CPFontManager.j"
|
@import "CPFontManager.j"
|
||||||
@import "CPGeometry.j"
|
@import "CPGeometry.j"
|
||||||
@import "CPGraphics.j"
|
|
||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
@import "CPImageView.j"
|
@import "CPImageView.j"
|
||||||
@import "CPKeyBinding.j"
|
|
||||||
@import "CPMenu.j"
|
@import "CPMenu.j"
|
||||||
@import "CPMenuItem.j"
|
@import "CPMenuItem.j"
|
||||||
@import "CPOpenPanel.j"
|
|
||||||
@import "CPOutlineView.j"
|
|
||||||
@import "CPPanel.j"
|
@import "CPPanel.j"
|
||||||
@import "CPPasteboard.j"
|
@import "CPPasteboard.j"
|
||||||
@import "CPPopUpButton.j"
|
@import "CPPopUpButton.j"
|
||||||
@import "CPProgressIndicator.j"
|
@import "CPProgressIndicator.j"
|
||||||
@import "CPRadio.j"
|
@import "CPRadio.j"
|
||||||
@import "CPResponder.j"
|
@import "CPResponder.j"
|
||||||
@import "CPScroller.j"
|
|
||||||
@import "CPScrollView.j"
|
@import "CPScrollView.j"
|
||||||
@import "CPSearchField.j"
|
@import "CPScroller.j"
|
||||||
@import "CPSecureTextField.j"
|
@import "CPSecureTextField.j"
|
||||||
@import "CPSegmentedControl.j"
|
@import "CPSegmentedControl.j"
|
||||||
@import "CPShadow.j"
|
@import "CPShadow.j"
|
||||||
@import "CPSlider.j"
|
@import "CPSlider.j"
|
||||||
@import "CPSplitView.j"
|
@import "CPSplitView.j"
|
||||||
|
@import "CPTabView.j"
|
||||||
@import "CPTableColumn.j"
|
@import "CPTableColumn.j"
|
||||||
@import "CPTableView.j"
|
@import "CPTableView.j"
|
||||||
@import "CPTabView.j"
|
|
||||||
@import "CPText.j"
|
|
||||||
@import "CPTextField.j"
|
@import "CPTextField.j"
|
||||||
@import "CPToolbar.j"
|
@import "CPToolbar.j"
|
||||||
@import "CPToolbarItem.j"
|
@import "CPToolbarItem.j"
|
||||||
@import "CPTokenField.j"
|
|
||||||
@import "CPTreeNode.j"
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
@import "CPViewAnimation.j"
|
|
||||||
@import "CPViewController.j"
|
|
||||||
@import "CPWebView.j"
|
@import "CPWebView.j"
|
||||||
@import "CPWindow.j"
|
@import "CPWindow.j"
|
||||||
@import "CPWindowController.j"
|
@import "CPWindowController.j"
|
||||||
@import "CPArrayController.j"
|
|
||||||
|
|||||||
@@ -1,416 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "CPView.j"
|
|
||||||
@import "CPButton.j"
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
@class CPAccordionViewItem
|
|
||||||
|
|
||||||
<p>A CPAccordionViewItem represents a single section of a CPAccordionView.</p>
|
|
||||||
*/
|
|
||||||
@implementation CPAccordionViewItem : CPObject
|
|
||||||
{
|
|
||||||
CPString _identifier @accessors(property=identifier);
|
|
||||||
CPView _view @accessors(property=view);
|
|
||||||
CPString _label @accessors(property=label);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
return [self initWithIdentifier:@""];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes a new CPAccordionViewItem with the specified identifier. This identifier may
|
|
||||||
then be used in subsequent methods to provide abstract introspection of the CPAccordionView.
|
|
||||||
*/
|
|
||||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
[self setIdentifier:anIdentifier];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
@class CPAccordionView
|
|
||||||
|
|
||||||
<p>CPAccordionView provides a container for CPAccordionViewItem objects and manages layout state
|
|
||||||
for all sublayout items.</p>
|
|
||||||
|
|
||||||
<strong>Example</strong><br />
|
|
||||||
<pre>
|
|
||||||
var myAccordionView = [[CPAccordionView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
var firstItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"firstSection"]];
|
|
||||||
[firstItem setView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
|
|
||||||
var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection"]];
|
|
||||||
[secondItem setView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
|
|
||||||
[myAccordionView addItem:firstItem];
|
|
||||||
[myAccordionView addItem:secondItem];
|
|
||||||
[myAccordionView setAutoresizingMask: CPViewWidthSizable | CPViewHeightSizable];
|
|
||||||
</pre>
|
|
||||||
*/
|
|
||||||
@implementation CPAccordionView : CPView
|
|
||||||
{
|
|
||||||
CPInteger _dirtyItemIndex;
|
|
||||||
CPView _itemHeaderPrototype;
|
|
||||||
|
|
||||||
CPMutableArray _items;
|
|
||||||
CPMutableArray _itemViews;
|
|
||||||
CPIndexSet _expandedItemIndexes;
|
|
||||||
}
|
|
||||||
/*!
|
|
||||||
Initializes the receiver for usage with the specified bounding rectangle
|
|
||||||
@return the initialized view
|
|
||||||
*/
|
|
||||||
- (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;
|
|
||||||
}
|
|
||||||
/*!
|
|
||||||
Append a CPAccordionViewItem to the receiver. Note that the CPAccordionViewItem must contain
|
|
||||||
a valid CPView derived component or a TypeError will be generated when the contents of the
|
|
||||||
ViewItem are disclosed.
|
|
||||||
*/
|
|
||||||
- (void)addItem:(CPAccordionViewItem)anItem
|
|
||||||
{
|
|
||||||
[self insertItem:anItem atIndex:_items.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertItem:(CPAccordionViewItem)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:(CPAccordionViewItem)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)setEnabled:(BOOL)isEnabled forItemAtIndex:(CPInteger)anIndex
|
|
||||||
{
|
|
||||||
var itemView = _itemViews[anIndex];
|
|
||||||
if (!itemView)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!isEnabled)
|
|
||||||
[self collapseItemAtIndex:anIndex];
|
|
||||||
else
|
|
||||||
[self expandItemAtIndex:anIndex];
|
|
||||||
|
|
||||||
[itemView setEnabled:isEnabled];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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)setEnabled:(BOOL)isEnabled
|
|
||||||
{
|
|
||||||
if ([_headerView respondsToSelector:@selector(setEnabled:)])
|
|
||||||
[_headerView setEnabled:isEnabled];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setContentView:(CPView)aView
|
|
||||||
{
|
|
||||||
if (_contentView === aView)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[_contentView removeObserver:self forKeyPath:@"frame"];
|
|
||||||
|
|
||||||
[_contentView removeFromSuperview];
|
|
||||||
|
|
||||||
_contentView = aView;
|
|
||||||
|
|
||||||
[_contentView addObserver:self forKeyPath:@"frame" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew 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
|
|
||||||
+108
-270
@@ -26,153 +26,144 @@
|
|||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
|
|
||||||
@import "CPApplication.j"
|
@import <AppKit/CPApplication.j>
|
||||||
@import "CPButton.j"
|
@import <AppKit/CPButton.j>
|
||||||
@import "CPColor.j"
|
@import <AppKit/CPColor.j>
|
||||||
@import "CPFont.j"
|
@import <AppKit/CPFont.j>
|
||||||
@import "CPImage.j"
|
@import <AppKit/CPImage.j>
|
||||||
@import "CPImageView.j"
|
@import <AppKit/CPImageView.j>
|
||||||
@import "CPPanel.j"
|
@import <AppKit/CPPanel.j>
|
||||||
@import "CPTextField.j"
|
@import <AppKit/CPTextField.j>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@global
|
@global
|
||||||
@group CPAlertStyle
|
@group CPAlertStyle
|
||||||
*/
|
*/
|
||||||
CPWarningAlertStyle = 0;
|
CPWarningAlertStyle = 0;
|
||||||
/*
|
/*
|
||||||
@global
|
@global
|
||||||
@group CPAlertStyle
|
@group CPAlertStyle
|
||||||
*/
|
*/
|
||||||
CPInformationalAlertStyle = 1;
|
CPInformationalAlertStyle = 1;
|
||||||
/*
|
/*
|
||||||
@global
|
@global
|
||||||
@group CPAlertStyle
|
@group CPAlertStyle
|
||||||
*/
|
*/
|
||||||
CPCriticalAlertStyle = 2;
|
CPCriticalAlertStyle = 2;
|
||||||
|
|
||||||
|
|
||||||
|
var CPAlertWarningImage,
|
||||||
|
CPAlertInformationImage,
|
||||||
|
CPAlertErrorImage;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
|
|
||||||
CPAlert is an alert panel that can be displayed modally to present the
|
CPAlert is an alert panel that can be displayed modally to present the
|
||||||
user with a message and one or more options.
|
user with a message and one or more options.
|
||||||
|
|
||||||
It can be used to display an information message \c CPInformationalAlertStyle,
|
It can be used to display an information message (<pre>CPInformationalAlertStyle</pre>),
|
||||||
a warning message \c CPWarningAlertStyle (the default), or a critical
|
a warning message (<pre>CPWarningAlertStyle</pre> - which is the default), or a critical
|
||||||
alert \c CPCriticalAlertStyle. In each case the user can be presented with one
|
alert (<pre>CPCriticalAlertStyle</pre>). In each case the user can be presented with one
|
||||||
or more options by adding buttons using the \c -addButtonWithTitle: method.
|
or more options by adding buttons using the <pre>addButtonWithTitle:</pre> method.
|
||||||
|
|
||||||
The panel is displayed modally by calling \c -runModal and once the user has
|
The panel is displayed modally by calling <pre>runModal</pre> and once the user has
|
||||||
dismissed the panel, a message will be sent to the panel's delegate (if set), informing
|
dismissed the panel, a message will be sent to the panel's delegate (if set), informing
|
||||||
it which button was clicked (see delegate methods).
|
it which button was clicked (see delegate methods).
|
||||||
|
|
||||||
@delegate -(void)alertDidEnd:(CPAlert)theAlert returnCode:(int)returnCode;
|
@delegate -(void)alertDidEnd:(CPAlert)theAlert returnCode:(int)returnCode;
|
||||||
Called when the user dismisses the alert by clicking one of the buttons.
|
Called when the user dismisses the alert by clicking one of the buttons.
|
||||||
@param theAlert the alert panel that the user dismissed
|
@param theAlert the alert panel that the user dismissed
|
||||||
@param returnCode the index of the button that the user clicked (starting from 0,
|
@param returnCode the index of the button that the user clicked (starting from 0,
|
||||||
representing the first button added to the alert which appears on the
|
representing the first button added to the alert which appears on the
|
||||||
right, 1 representing the next button to the left and so on)
|
right, 1 representing the next button to the left and so on)
|
||||||
*/
|
*/
|
||||||
@implementation CPAlert : CPView
|
@implementation CPAlert : CPObject
|
||||||
{
|
{
|
||||||
CPPanel _alertPanel;
|
CPPanel _alertPanel;
|
||||||
|
|
||||||
CPTextField _messageLabel;
|
CPTextField _messageLabel;
|
||||||
CPTextField _informativeLabel;
|
|
||||||
CPImageView _alertImageView;
|
CPImageView _alertImageView;
|
||||||
|
|
||||||
CPAlertStyle _alertStyle;
|
CPAlertStyle _alertStyle;
|
||||||
CPString _windowTitle;
|
CPString _windowTitle;
|
||||||
int _windowStyle;
|
int _windowStyle;
|
||||||
|
int _buttonCount;
|
||||||
CPArray _buttons;
|
CPArray _buttons;
|
||||||
|
|
||||||
id _delegate;
|
id _delegate;
|
||||||
SEL _didEndSelector;
|
|
||||||
id _modalDelegate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
return @"alert";
|
if (self != CPAlert)
|
||||||
}
|
return;
|
||||||
|
|
||||||
+ (id)themeAttributes
|
var bundle = [CPBundle bundleForClass:[self class]];
|
||||||
{
|
|
||||||
return [CPDictionary dictionaryWithObjects:[CGSizeMake(400.0, 110.0), CGInsetMake(15, 15, 15, 50), 6, 10,
|
CPAlertWarningImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-warning.png"]
|
||||||
CPJustifiedTextAlignment, [CPColor blackColor], [CPFont boldSystemFontOfSize:13.0], [CPNull null], CGSizeMakeZero(),
|
size:CGSizeMake(32.0, 32.0)];
|
||||||
CPJustifiedTextAlignment, [CPColor blackColor], [CPFont systemFontOfSize:12.0], [CPNull null], CGSizeMakeZero(),
|
|
||||||
CGPointMake(15, 12),
|
CPAlertInformationImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-information.png"]
|
||||||
[CPNull null],
|
size:CGSizeMake(32.0, 32.0)];
|
||||||
[CPNull null],
|
|
||||||
[CPNull null]
|
CPAlertErrorImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-error.png"]
|
||||||
]
|
size:CGSizeMake(32.0, 32.0)];
|
||||||
forKeys:[@"size", @"content-inset", @"informative-offset", @"button-offset",
|
|
||||||
@"message-text-alignment", @"message-text-color", @"message-text-font", @"message-text-shadow-color", @"message-text-shadow-offset",
|
|
||||||
@"informative-text-alignment", @"informative-text-color", @"informative-text-font", @"informative-text-shadow-color", @"informative-text-shadow-offset",
|
|
||||||
@"image-offset",
|
|
||||||
@"information-image",
|
|
||||||
@"warning-image",
|
|
||||||
@"error-image"
|
|
||||||
]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Initializes a \c CPAlert panel with the default alert style \c CPWarningAlertStyle.
|
Initializes a <pre>CPAlert</pre> panel with the default alert style (<pre>CPWarningAlertStyle</pre>).
|
||||||
*/
|
*/
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
self = [super init];
|
||||||
|
|
||||||
|
if (self)
|
||||||
{
|
{
|
||||||
|
_buttonCount = 0;
|
||||||
_buttons = [CPArray array];
|
_buttons = [CPArray array];
|
||||||
_alertStyle = CPWarningAlertStyle;
|
_alertStyle = CPWarningAlertStyle;
|
||||||
_alertPanel = nil;
|
|
||||||
_windowStyle = nil;
|
_messageLabel = [[CPTextField alloc] initWithFrame:CGRectMake(57.0, 10.0, 220.0, 80.0)];
|
||||||
_didEndSelector = nil;
|
[_messageLabel setFont:[CPFont systemFontOfSize:12.0]];
|
||||||
|
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
||||||
_messageLabel = [CPTextField labelWithTitle:@"Alert"];
|
[_messageLabel setAlignment:CPJustifiedTextAlignment];
|
||||||
_alertImageView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
_informativeLabel = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
_alertImageView = [[CPImageView alloc] initWithFrame:CGRectMake(15.0, 12.0, 32.0, 32.0)];
|
||||||
|
|
||||||
|
[self setWindowStyle:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the window appearance. If CPHUDBackgroundWindowMask is set, the default HUD theme
|
Sets the window appearance.
|
||||||
will be activated.
|
|
||||||
|
|
||||||
@param styleMask - Either CPHUDBackgroundWindowMask or nil for standard.
|
@param styleMask - Either CPHUDBackgroundWindowMask or nil for standard.
|
||||||
*/
|
*/
|
||||||
- (void)setWindowStyle:(int)styleMask
|
- (void)setWindowStyle:(int)styleMask
|
||||||
{
|
{
|
||||||
_windowStyle = styleMask;
|
_windowStyle = styleMask;
|
||||||
|
|
||||||
[self setTheme:(_windowStyle & CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
|
_alertPanel = [[CPPanel alloc] initWithContentRect:CGRectMake(0.0, 0.0, 300.0, 130.0) styleMask:styleMask ? styleMask | CPTitledWindowMask : CPTitledWindowMask];
|
||||||
|
[_alertPanel setFloatingPanel:YES];
|
||||||
// We'll need to recreate the panel to get the new window style.
|
[_alertPanel center];
|
||||||
_alertPanel = nil;
|
|
||||||
}
|
[_messageLabel setTextColor:(styleMask == CPHUDBackgroundWindowMask) ? [CPColor whiteColor] : [CPColor blackColor]];
|
||||||
|
|
||||||
- (void)_createPanel
|
var count = [_buttons count];
|
||||||
{
|
for(var i=0; i < count; i++)
|
||||||
var frame = CGRectMakeZero();
|
|
||||||
frame.size = [self currentValueForThemeAttribute:@"size"];
|
|
||||||
_alertPanel = [[CPPanel alloc] initWithContentRect:frame styleMask:_windowStyle ? _windowStyle | CPTitledWindowMask : CPTitledWindowMask];
|
|
||||||
|
|
||||||
var contentView = [_alertPanel contentView],
|
|
||||||
count = [_buttons count];
|
|
||||||
|
|
||||||
if (count)
|
|
||||||
{
|
{
|
||||||
while (count--)
|
var button = _buttons[i];
|
||||||
[contentView addSubview:_buttons[count]];
|
|
||||||
|
[button setFrameSize:CGSizeMake([button frame].size.width, (styleMask == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)];
|
||||||
|
[button setBezelStyle:(styleMask == CPHUDBackgroundWindowMask) ? CPHUDBezelStyle : CPRoundedBezelStyle];
|
||||||
|
|
||||||
|
[[_alertPanel contentView] addSubview:button];
|
||||||
}
|
}
|
||||||
else
|
|
||||||
[self addButtonWithTitle:@"OK"];
|
[[_alertPanel contentView] addSubview:_messageLabel];
|
||||||
|
[[_alertPanel contentView] addSubview:_alertImageView];
|
||||||
[contentView addSubview:_messageLabel];
|
|
||||||
[contentView addSubview:_alertImageView];
|
|
||||||
[contentView addSubview:_informativeLabel];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -235,7 +226,7 @@ CPCriticalAlertStyle = 2;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver’s message text, or title, to a given text.
|
Set’s the receiver’s message text, or title, to a given text.
|
||||||
@param messageText - Message text for the alert.
|
@param messageText - Message text for the alert.
|
||||||
*/
|
*/
|
||||||
- (void)setMessageText:(CPString)messageText
|
- (void)setMessageText:(CPString)messageText
|
||||||
@@ -243,228 +234,75 @@ CPCriticalAlertStyle = 2;
|
|||||||
[_messageLabel setStringValue:messageText];
|
[_messageLabel setStringValue:messageText];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's message text body.
|
Return's the receiver's message text body.
|
||||||
*/
|
*/
|
||||||
- (CPString)messageText
|
- (CPString)messageText
|
||||||
{
|
{
|
||||||
return [_messageLabel stringValue];
|
return [_messageLabel stringValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the receiver's informative text, shown below the message text.
|
|
||||||
@param informativeText - The informative text.
|
|
||||||
*/
|
|
||||||
- (void)setInformativeText:(CPString)informativeText
|
|
||||||
{
|
|
||||||
[_informativeLabel setStringValue:informativeText];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the receiver's informative text.
|
|
||||||
*/
|
|
||||||
- (CPString)informativeText
|
|
||||||
{
|
|
||||||
return [_informativeLabel stringValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Adds a button with a given title to the receiver.
|
Adds a button with a given title to the receiver.
|
||||||
Buttons will be added starting from the right hand side of the \c CPAlert panel.
|
Buttons will be added starting from the right hand side of the <pre>CPAlert</pre> panel.
|
||||||
The first button will have the index 0, the second button 1 and so on.
|
The first button will have the index 0, the second button 1 and so on.
|
||||||
|
|
||||||
The first button will automatically be given a key equivalent of Return,
|
|
||||||
and any button titled "Cancel" will be given a key equivalent of Escape.
|
|
||||||
|
|
||||||
You really shouldn't need more than 3 buttons.
|
You really shouldn't need more than 3 buttons.
|
||||||
*/
|
*/
|
||||||
- (void)addButtonWithTitle:(CPString)title
|
- (void)addButtonWithTitle:(CPString)title
|
||||||
{
|
{
|
||||||
var bounds = [[_alertPanel contentView] bounds],
|
var button = [[CPButton alloc] initWithFrame:CGRectMake(200.0 - (_buttonCount * 90.0), 98.0, 80.0, (_windowStyle == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)];
|
||||||
button = [[CPButton alloc] initWithFrame:CGRectMakeZero()],
|
|
||||||
_buttonCount = [_buttons count];
|
|
||||||
|
|
||||||
[button setTitle:title];
|
[button setTitle:title];
|
||||||
[button setTarget:self];
|
[button setTarget:self];
|
||||||
[button setTag:_buttonCount];
|
[button setTag:_buttonCount];
|
||||||
[button setAction:@selector(_dismissAlert:)];
|
[button setAction:@selector(_notifyDelegate:)];
|
||||||
|
|
||||||
|
[button setBezelStyle:(_windowStyle == CPHUDBackgroundWindowMask) ? CPHUDBezelStyle : CPRoundRectBezelStyle];
|
||||||
[[_alertPanel contentView] addSubview:button];
|
[[_alertPanel contentView] addSubview:button];
|
||||||
|
|
||||||
if (_buttonCount == 0)
|
if (_buttonCount == 0)
|
||||||
[button setKeyEquivalent:CPCarriageReturnCharacter];
|
[_alertPanel setDefaultButton:button];
|
||||||
else if ([title lowercaseString] === "cancel")
|
|
||||||
[button setKeyEquivalent:CPEscapeFunctionKey];
|
|
||||||
else
|
|
||||||
[button setKeyEquivalent:nil];
|
|
||||||
|
|
||||||
[_buttons insertObject:button atIndex:0];
|
_buttonCount++;
|
||||||
}
|
[_buttons addObject:button];
|
||||||
|
|
||||||
- (void)layoutPanel
|
|
||||||
{
|
|
||||||
if (!_alertPanel)
|
|
||||||
[self _createPanel];
|
|
||||||
|
|
||||||
var inset = [self currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
iconOffset = [self currentValueForThemeAttribute:@"image-offset"],
|
|
||||||
theTitle,
|
|
||||||
theImage;
|
|
||||||
|
|
||||||
switch (_alertStyle)
|
|
||||||
{
|
|
||||||
case CPWarningAlertStyle: theImage = [self currentValueForThemeAttribute:@"warning-image"];
|
|
||||||
theTitle = @"Warning";
|
|
||||||
break;
|
|
||||||
case CPInformationalAlertStyle: theImage = [self currentValueForThemeAttribute:@"information-image"];
|
|
||||||
theTitle = @"Information";
|
|
||||||
break;
|
|
||||||
case CPCriticalAlertStyle: theImage = [self currentValueForThemeAttribute:@"error-image"];
|
|
||||||
theTitle = @"Error";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[_alertImageView setImage:theImage];
|
|
||||||
|
|
||||||
var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
|
|
||||||
[_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
|
|
||||||
|
|
||||||
[_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle];
|
|
||||||
[_alertPanel setFloatingPanel:YES];
|
|
||||||
[_alertPanel center];
|
|
||||||
|
|
||||||
[_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]];
|
|
||||||
[_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]];
|
|
||||||
[_messageLabel setTextShadowColor:[self currentValueForThemeAttribute:@"message-text-shadow-color"]];
|
|
||||||
[_messageLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"message-text-shadow-offset"]];
|
|
||||||
[_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]];
|
|
||||||
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
|
||||||
|
|
||||||
[_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]];
|
|
||||||
[_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]];
|
|
||||||
[_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]];
|
|
||||||
[_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
|
|
||||||
[_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
|
||||||
|
|
||||||
// FIXME sizeWithFontCorrection shouldn't be needed.
|
|
||||||
var bounds = [[_alertPanel contentView] bounds],
|
|
||||||
offsetX = CGRectGetWidth(bounds) - inset.right,
|
|
||||||
informativeOffset = [self currentValueForThemeAttribute:@"informative-offset"],
|
|
||||||
buttonOffset = [self currentValueForThemeAttribute:@"button-offset"],
|
|
||||||
|
|
||||||
textWidth = offsetX - inset.left,
|
|
||||||
messageSize = [([_messageLabel stringValue] || " ") sizeWithFont:[_messageLabel font] inWidth:textWidth],
|
|
||||||
informationString = [_informativeLabel stringValue],
|
|
||||||
informativeSize = [(informationString || " ") sizeWithFont:[_informativeLabel font] inWidth:textWidth],
|
|
||||||
sizeWithFontCorrection = 6.0;
|
|
||||||
|
|
||||||
[_messageLabel setFrame:CGRectMake(inset.left, inset.top, textWidth, messageSize.height + sizeWithFontCorrection)];
|
|
||||||
[_informativeLabel setFrame:CGRectMake(inset.left, CGRectGetMaxY([_messageLabel frame]) + informativeOffset, textWidth, informativeSize.height + sizeWithFontCorrection)];
|
|
||||||
// Don't let an empty informative label partially cover the buttons.
|
|
||||||
[_informativeLabel setHidden:!informationString];
|
|
||||||
|
|
||||||
var aRepresentativeButton = _buttons[0],
|
|
||||||
buttonY = MAX(CGRectGetMaxY([_alertImageView frame]), CGRectGetMaxY(informationString ? [_informativeLabel frame] : [_messageLabel frame])) + buttonOffset; // the lower of the bottom of the text and the bottom of the icon.
|
|
||||||
|
|
||||||
[aRepresentativeButton setTheme:[self theme]];
|
|
||||||
[aRepresentativeButton sizeToFit];
|
|
||||||
|
|
||||||
// Make the window just tall enough to fit everything. Bit of a hack really.
|
|
||||||
var minimumSize = [self currentValueForThemeAttribute:@"size"],
|
|
||||||
desiredHeight = MAX(minimumSize.height, buttonY + CGRectGetHeight([aRepresentativeButton bounds]) + inset.bottom),
|
|
||||||
deltaY = desiredHeight - CGRectGetHeight(bounds),
|
|
||||||
frameSize = CGSizeMakeCopy([_alertPanel frame].size);
|
|
||||||
|
|
||||||
frameSize.height += deltaY;
|
|
||||||
[_alertPanel setFrameSize:frameSize];
|
|
||||||
|
|
||||||
var count = [_buttons count];
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
var button = _buttons[count];
|
|
||||||
[button setTheme:[self theme]];
|
|
||||||
[button sizeToFit];
|
|
||||||
|
|
||||||
var buttonBounds = [button bounds],
|
|
||||||
width = MAX(80.0, CGRectGetWidth(buttonBounds)),
|
|
||||||
height = CGRectGetHeight(buttonBounds);
|
|
||||||
|
|
||||||
offsetX -= width;
|
|
||||||
[button setFrame:CGRectMake(offsetX, buttonY, width, height)];
|
|
||||||
offsetX -= 10;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Displays the \c CPAlert panel as a modal dialog. The user will not be
|
Displays the <pre>CPAlert</pre> panel as a modal dialog. The user will not be
|
||||||
able to interact with any other controls until s/he has dismissed the alert
|
able to interact with any other controls until s/he has dismissed the alert
|
||||||
by clicking on one of the buttons.
|
by clicking on one of the buttons.
|
||||||
*/
|
*/
|
||||||
- (void)runModal
|
- (void)runModal
|
||||||
{
|
{
|
||||||
[self layoutPanel];
|
var theTitle;
|
||||||
|
|
||||||
|
switch (_alertStyle)
|
||||||
|
{
|
||||||
|
case CPWarningAlertStyle: [_alertImageView setImage:CPAlertWarningImage];
|
||||||
|
theTitle = @"Warning";
|
||||||
|
break;
|
||||||
|
case CPInformationalAlertStyle: [_alertImageView setImage:CPAlertInformationImage];
|
||||||
|
theTitle = @"Information";
|
||||||
|
break;
|
||||||
|
case CPCriticalAlertStyle: [_alertImageView setImage:CPAlertErrorImage];
|
||||||
|
theTitle = @"Error";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle];
|
||||||
|
|
||||||
[CPApp runModalForWindow:_alertPanel];
|
[CPApp runModalForWindow:_alertPanel];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Runs the receiver modally as an alert sheet attached to a specified window.
|
|
||||||
|
|
||||||
@param window The parent window for the sheet.
|
|
||||||
@param modalDelegate The delegate for the modal-dialog session.
|
|
||||||
@param alertDidEndSelector Message the alert sends to modalDelegate after the sheet is dismissed.
|
|
||||||
@param contextInfo Contextual data passed to modalDelegate in didEndSelector message.
|
|
||||||
*/
|
|
||||||
- (void)beginSheetModalForWindow:(CPWindow)window modalDelegate:(id)modalDelegate didEndSelector:(SEL)alertDidEndSelector contextInfo:(void)contextInfo
|
|
||||||
{
|
|
||||||
if (!(_windowStyle & CPDocModalWindowMask))
|
|
||||||
[self setWindowStyle:CPDocModalWindowMask];
|
|
||||||
[self layoutPanel];
|
|
||||||
|
|
||||||
_didEndSelector = alertDidEndSelector;
|
|
||||||
_modalDelegate = modalDelegate;
|
|
||||||
|
|
||||||
[CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Runs the receiver modally as an alert sheet attached to a specified window.
|
|
||||||
|
|
||||||
@param window The parent window for the sheet.
|
|
||||||
*/
|
|
||||||
- (void)beginSheetModalForWindow:(CPWindow)window
|
|
||||||
{
|
|
||||||
if (!(_windowStyle & CPDocModalWindowMask))
|
|
||||||
[self setWindowStyle:CPDocModalWindowMask];
|
|
||||||
[self layoutPanel];
|
|
||||||
|
|
||||||
[CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_alertDidEnd:(CPWindow)aSheet returnCode:(CPInteger)returnCode contextInfo:(id)contextInfo
|
|
||||||
{
|
|
||||||
if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
|
|
||||||
[_delegate alertDidEnd:self returnCode:returnCode];
|
|
||||||
|
|
||||||
if (_didEndSelector)
|
|
||||||
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
|
||||||
|
|
||||||
_didEndSelector = nil;
|
|
||||||
_modalDelegate = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (void)_dismissAlert:(CPButton)button
|
- (void)_notifyDelegate:(id)button
|
||||||
{
|
{
|
||||||
if ([_alertPanel isSheet])
|
if (_delegate && [_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
|
||||||
[CPApp endSheet:_alertPanel returnCode:[button tag]];
|
[_delegate alertDidEnd:self returnCode:[button tag]];
|
||||||
else
|
|
||||||
{
|
|
||||||
[CPApp abortModal];
|
|
||||||
[_alertPanel close];
|
|
||||||
|
|
||||||
[self _alertDidEnd:nil returnCode:[button tag] contextInfo:nil];
|
[CPApp abortModal];
|
||||||
}
|
[_alertPanel close];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+39
-74
@@ -48,19 +48,19 @@ CPAnimationLinear = 3;
|
|||||||
|
|
||||||
ACTUAL_FRAME_RATE = 0;
|
ACTUAL_FRAME_RATE = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPAnimation
|
@class CPAnimation
|
||||||
|
|
||||||
Manages an animation. Contains timing and progress information.
|
Manages an animation. Contains timing and progress information.
|
||||||
|
|
||||||
@par Delegate Methods
|
@par Delegate Methods
|
||||||
|
|
||||||
@delegate -(BOOL)animationShouldStart:(CPAnimation)animation;
|
@delegate -(BOOL)animationShouldStart:(CPAnimation)animation;
|
||||||
Called at the beginning of \c -startAnimation.
|
Called at the beginning of <code>startAnimation</code>.
|
||||||
@param animation the animation that will start
|
@param animation the animation that will start
|
||||||
@return \c YES allows the animation to start.
|
@return <code>YES</code> allows the animation to start.
|
||||||
\c NO stops the animation.
|
<code>NO</code> stops the animation.
|
||||||
|
|
||||||
@delegate -(void)animationDidEnd:(CPAnimation)animation;
|
@delegate -(void)animationDidEnd:(CPAnimation)animation;
|
||||||
Called when an animation has completed.
|
Called when an animation has completed.
|
||||||
@@ -72,7 +72,7 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
|
|
||||||
@delegate - (float)animation:(CPAnimation)animation valueForProgress:(float)progress;
|
@delegate - (float)animation:(CPAnimation)animation valueForProgress:(float)progress;
|
||||||
The value from this method will be returned when CPAnimation's
|
The value from this method will be returned when CPAnimation's
|
||||||
\c currentValue method is called.
|
<code>currentValue</code> method is called.
|
||||||
@param animation the animation to obtain the curve value for
|
@param animation the animation to obtain the curve value for
|
||||||
@param progress the current animation progress
|
@param progress the current animation progress
|
||||||
@return the curve value
|
@return the curve value
|
||||||
@@ -81,13 +81,13 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
{
|
{
|
||||||
CPTimeInterval _lastTime;
|
CPTimeInterval _lastTime;
|
||||||
CPTimeInterval _duration;
|
CPTimeInterval _duration;
|
||||||
|
|
||||||
CPAnimationCurve _animationCurve;
|
CPAnimationCurve _animationCurve;
|
||||||
CAMediaTimingFunction _timingFunction;
|
CAMediaTimingFunction _timingFunction;
|
||||||
|
|
||||||
float _frameRate;
|
float _frameRate;
|
||||||
float _progress;
|
float _progress;
|
||||||
|
|
||||||
id _delegate;
|
id _delegate;
|
||||||
CPTimer _timer;
|
CPTimer _timer;
|
||||||
}
|
}
|
||||||
@@ -101,37 +101,36 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
- (id)initWithDuration:(float)aDuration animationCurve:(CPAnimationCurve)anAnimationCurve
|
- (id)initWithDuration:(float)aDuration animationCurve:(CPAnimationCurve)anAnimationCurve
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_progress = 0.0;
|
_progress = 0.0;
|
||||||
_duration = MAX(0.0, aDuration);
|
_duration = MAX(0.0, aDuration);
|
||||||
|
_animationCurve = anAnimationCurve;
|
||||||
_frameRate = 60.0;
|
_frameRate = 60.0;
|
||||||
|
|
||||||
[self setAnimationCurve:anAnimationCurve];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the animation's pace.
|
Sets the animation's pace.
|
||||||
@param anAnimationCurve the animation's pace
|
@param anAnimationCurve the animation's pace
|
||||||
@throws CPInvalidArgumentException if an invalid animation curve is specified
|
@throws CPInvalidArgumentException if an invalid animation curve is specified
|
||||||
*/
|
*/
|
||||||
- (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve
|
- (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve
|
||||||
{
|
{
|
||||||
switch (anAnimationCurve)
|
switch (_animationCurve)
|
||||||
{
|
{
|
||||||
case CPAnimationEaseInOut: timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut;
|
case CPAnimationEaseInOut: timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPAnimationEaseIn: timingFunctionName = kCAMediaTimingFunctionEaseIn;
|
case CPAnimationEaseIn: timingFunctionName = kCAMediaTimingFunctionEaseIn;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPAnimationEaseOut: timingFunctionName = kCAMediaTimingFunctionEaseOut;
|
case CPAnimationEaseOut: timingFunctionName = kCAMediaTimingFunctionEaseOut;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPAnimationLinear: timingFunctionName = kCAMediaTimingFunctionLinear;
|
case CPAnimationLinear: timingFunctionName = kCAMediaTimingFunctionLinear;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -139,7 +138,7 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
reason:"Invalid value provided for animation curve"];
|
reason:"Invalid value provided for animation curve"];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_animationCurve = anAnimationCurve;
|
_animationCurve = anAnimationCurve;
|
||||||
_timingFunction = [CAMediaTimingFunction functionWithName:timingFunctionName];
|
_timingFunction = [CAMediaTimingFunction functionWithName:timingFunctionName];
|
||||||
}
|
}
|
||||||
@@ -155,13 +154,13 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
/*!
|
/*!
|
||||||
Sets the animation's length.
|
Sets the animation's length.
|
||||||
@param aDuration the new animation length
|
@param aDuration the new animation length
|
||||||
@throws CPInvalidArgumentException if \c aDuration is negative
|
@throws CPInvalidArgumentException if <code>aDuration</code> is negative
|
||||||
*/
|
*/
|
||||||
- (void)setDuration:(CPTimeInterval)aDuration
|
- (void)setDuration:(CPTimeInterval)aDuration
|
||||||
{
|
{
|
||||||
if (aDuration < 0)
|
if (aDuration < 0)
|
||||||
[CPException raise:CPInvalidArgumentException reason:"aDuration can't be negative"];
|
[CPException raise:CPInvalidArgumentException reason:"aDuration can't be negative"];
|
||||||
|
|
||||||
_duration = aDuration;
|
_duration = aDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,13 +175,13 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
/*!
|
/*!
|
||||||
Sets the animation frame rate. This is not a guaranteed frame rate. 0 means to go as fast as possible.
|
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
|
@param frameRate the new desired frame rate
|
||||||
@throws CPInvalidArgumentException if \c frameRate is negative
|
@throws CPInvalidArgumentException if <code>frameRate</code> is negative
|
||||||
*/
|
*/
|
||||||
- (void)setFrameRate:(float)frameRate
|
- (void)setFrameRate:(float)frameRate
|
||||||
{
|
{
|
||||||
if (frameRate < 0)
|
if (frameRate < 0)
|
||||||
[CPException raise:CPInvalidArgumentException reason:"frameRate can't be negative"];
|
[CPException raise:CPInvalidArgumentException reason:"frameRate can't be negative"];
|
||||||
|
|
||||||
_frameRate = frameRate;
|
_frameRate = frameRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,22 +211,22 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Starts the animation. The method calls \c -animationShouldStart:
|
Starts the animation. The method calls <code>animationShouldStart:</code>
|
||||||
on the delegate (if it implements it) to see if the animation
|
on the delegate (if it implements it) to see if the animation
|
||||||
should begin.
|
should begin.
|
||||||
*/
|
*/
|
||||||
- (void)startAnimation
|
- (void)startAnimation
|
||||||
{
|
{
|
||||||
// If we're already animating, or our delegate stops us, animate.
|
// If we're already animating, or our delegate stops us, animate.
|
||||||
if (_timer || _delegate && [_delegate respondsToSelector:@selector(animationShouldStart:)] && ![_delegate animationShouldStart:self])
|
if (_timer || _delegate && [_delegate respondsToSelector:@selector(animationShouldStart)] && ![_delegate animationShouldStart:self])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_progress === 1.0)
|
if (_progress === 1.0)
|
||||||
_progress = 0.0;
|
_progress = 0.0;
|
||||||
|
|
||||||
ACTUAL_FRAME_RATE = 0;
|
ACTUAL_FRAME_RATE = 0;
|
||||||
_lastTime = new Date();
|
_lastTime = new Date();
|
||||||
|
|
||||||
_timer = [CPTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(animationTimerDidFire:) userInfo:nil repeats:YES];
|
_timer = [CPTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(animationTimerDidFire:) userInfo:nil repeats:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,14 +241,14 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
_lastTime = currentTime;
|
_lastTime = currentTime;
|
||||||
|
|
||||||
++ACTUAL_FRAME_RATE;
|
++ACTUAL_FRAME_RATE;
|
||||||
|
|
||||||
[self setCurrentProgress:progress];
|
[self setCurrentProgress:progress];
|
||||||
|
|
||||||
if (progress === 1.0)
|
if (progress === 1.0)
|
||||||
{
|
{
|
||||||
[_timer invalidate];
|
[_timer invalidate];
|
||||||
_timer = nil;
|
_timer = nil;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(animationDidEnd:)])
|
if ([_delegate respondsToSelector:@selector(animationDidEnd:)])
|
||||||
[_delegate animationDidEnd:self];
|
[_delegate animationDidEnd:self];
|
||||||
}
|
}
|
||||||
@@ -262,16 +261,16 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
{
|
{
|
||||||
if (!_timer)
|
if (!_timer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[_timer invalidate];
|
[_timer invalidate];
|
||||||
_timer = nil;
|
_timer = nil;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(animationDidStop:)])
|
if ([_delegate respondsToSelector:@selector(animationDidStop:)])
|
||||||
[_delegate animationDidStop:self];
|
[_delegate animationDidStop:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the animation
|
Returns <code>YES</code> if the animation
|
||||||
is running.
|
is running.
|
||||||
*/
|
*/
|
||||||
- (BOOL)isAnimating
|
- (BOOL)isAnimating
|
||||||
@@ -301,47 +300,13 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
*/
|
*/
|
||||||
- (float)currentValue
|
- (float)currentValue
|
||||||
{
|
{
|
||||||
var t = [self currentProgress];
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
|
if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
|
||||||
return [_delegate animation:self valueForProgress:t];
|
return [_delegate animation:self valueForProgress:_progress];
|
||||||
|
|
||||||
var c1 = [],
|
if (_animationCurve == CPAnimationLinear)
|
||||||
c2 = [];
|
return _progress;
|
||||||
|
|
||||||
[_timingFunction getControlPointAtIndex:1 values:c1];
|
alert("IMPLEMENT ANIMATION CURVES!!!");
|
||||||
[_timingFunction getControlPointAtIndex:2 values:c2];
|
|
||||||
|
|
||||||
return CubicBezierAtTime(t,c1[0],c1[1],c2[0],c2[1],_duration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
// currently used function to determine time
|
|
||||||
// 1:1 conversion to js from webkit source files
|
|
||||||
// UnitBezier.h, WebCore_animation_AnimationBase.cpp
|
|
||||||
var CubicBezierAtTime = function CubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration)
|
|
||||||
{
|
|
||||||
var ax=0,bx=0,cx=0,ay=0,by=0,cy=0;
|
|
||||||
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
|
|
||||||
function sampleCurveX(t) {return ((ax*t+bx)*t+cx)*t;};
|
|
||||||
function sampleCurveY(t) {return ((ay*t+by)*t+cy)*t;};
|
|
||||||
function sampleCurveDerivativeX(t) {return (3.0*ax*t+2.0*bx)*t+cx;};
|
|
||||||
// The epsilon value to pass given that the animation is going to run over |duration| seconds. The longer the animation, the more precision is needed in the timing function result to avoid ugly discontinuities.
|
|
||||||
function solveEpsilon(duration) {return 1.0/(200.0*duration);};
|
|
||||||
function solve(x,epsilon) {return sampleCurveY(solveCurveX(x,epsilon));};
|
|
||||||
// Given an x value, find a parametric value it came from.
|
|
||||||
function solveCurveX(x,epsilon) {var t0,t1,t2,x2,d2,i;
|
|
||||||
function fabs(n) {if(n>=0) {return n;}else {return 0-n;}};
|
|
||||||
// First try a few iterations of Newton's method -- normally very fast.
|
|
||||||
for(t2=x, i=0; i<8; i++) {x2=sampleCurveX(t2)-x; if(fabs(x2)<epsilon) {return t2;} d2=sampleCurveDerivativeX(t2); if(fabs(d2)<1e-6) {break;} t2=t2-x2/d2;}
|
|
||||||
// Fall back to the bisection method for reliability.
|
|
||||||
t0=0.0; t1=1.0; t2=x; if(t2<t0) {return t0;} if(t2>t1) {return t1;}
|
|
||||||
while(t0<t1) {x2=sampleCurveX(t2); if(fabs(x2-x)<epsilon) {return t2;} if(x>x2) {t0=t2;}else {t1=t2;} t2=(t1-t0)*.5+t0;}
|
|
||||||
return t2; // Failure.
|
|
||||||
};
|
|
||||||
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
|
|
||||||
cx=3.0*p1x; bx=3.0*(p2x-p1x)-cx; ax=1.0-cx-bx; cy=3.0*p1y; by=3.0*(p2y-p1y)-cy; ay=1.0-cy-by;
|
|
||||||
// Convert from input time to parametric value in curve, then from that to output time.
|
|
||||||
return solve(t, solveEpsilon(duration));
|
|
||||||
};
|
|
||||||
|
|||||||
+229
-600
File diff suppressed because it is too large
Load Diff
@@ -1,675 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CPArrayController.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Adapted from Cocotron, by Johannes Fortmann
|
|
||||||
*
|
|
||||||
* 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 <Foundation/CPIndexSet.j>
|
|
||||||
|
|
||||||
@import "CPObjectController.j"
|
|
||||||
@import "CPKeyValueBinding.j"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPArrayController : CPObjectController
|
|
||||||
{
|
|
||||||
BOOL _avoidsEmptySelection;
|
|
||||||
BOOL _clearsFilterPredicateOnInsertion;
|
|
||||||
BOOL _filterRestrictsInsertion;
|
|
||||||
BOOL _preservesSelection;
|
|
||||||
BOOL _selectsInsertedObjects;
|
|
||||||
BOOL _alwaysUsesMultipleValuesMarker;
|
|
||||||
|
|
||||||
id _selectionIndexes;
|
|
||||||
id _sortDescriptors;
|
|
||||||
id _filterPredicate;
|
|
||||||
id _arrangedObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self !== [CPArrayController class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self exposeBinding:@"contentArray"];
|
|
||||||
[self exposeBinding:@"contentSet"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingContentArray
|
|
||||||
{
|
|
||||||
return [CPSet setWithObjects:"content"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingArrangedObjects
|
|
||||||
{
|
|
||||||
return [CPSet setWithObjects:"content", "filterPredicate", "sortDescriptors"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingSelection
|
|
||||||
{
|
|
||||||
return [CPSet setWithObjects:"selectionIndexes"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingSelectionIndex
|
|
||||||
{
|
|
||||||
return [CPSet setWithObjects:"selectionIndexes"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingSelectionIndexes
|
|
||||||
{
|
|
||||||
// When the arranged objects change, selection preservation may cause the indexes
|
|
||||||
// to change.
|
|
||||||
return [CPSet setWithObjects:"arrangedObjects"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingSelectedObjects
|
|
||||||
{
|
|
||||||
// Don't need to depend on arrangedObjects here because selectionIndexes already does.
|
|
||||||
return [CPSet setWithObjects:"selectionIndexes"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingCanRemove
|
|
||||||
{
|
|
||||||
return [CPSet setWithObjects:"selectionIndexes"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingCanSelectNext
|
|
||||||
{
|
|
||||||
return [CPSet setWithObjects:"selectionIndexes"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingCanSelectPrevious
|
|
||||||
{
|
|
||||||
return [CPSet setWithObjects:"selectionIndexes"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_selectionIndexes = [CPIndexSet indexSet];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)prepareContent
|
|
||||||
{
|
|
||||||
[self _setContentArray:[[self newObject]]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)preservesSelection
|
|
||||||
{
|
|
||||||
return _preservesSelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPreservesSelection:(BOOL)value
|
|
||||||
{
|
|
||||||
_preservesSelection = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)selectsInsertedObjects
|
|
||||||
{
|
|
||||||
return _selectsInsertedObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setSelectsInsertedObjects:(BOOL)value
|
|
||||||
{
|
|
||||||
_selectsInsertedObjects = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)avoidsEmptySelection
|
|
||||||
{
|
|
||||||
return _avoidsEmptySelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAvoidsEmptySelection:(BOOL)value
|
|
||||||
{
|
|
||||||
_avoidsEmptySelection = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setContent:(id)value
|
|
||||||
{
|
|
||||||
if (value === nil)
|
|
||||||
value = [];
|
|
||||||
|
|
||||||
if (![value isKindOfClass:[CPArray class]])
|
|
||||||
value = [value];
|
|
||||||
|
|
||||||
var oldSelectedObjects = nil,
|
|
||||||
oldSelectionIndexes = nil;
|
|
||||||
|
|
||||||
if ([self preservesSelection])
|
|
||||||
oldSelectedObjects = [self selectedObjects];
|
|
||||||
else
|
|
||||||
oldSelectionIndexes = [self selectionIndexes];
|
|
||||||
|
|
||||||
/*
|
|
||||||
When the contents are changed, the selected indexes may no longer refer to the
|
|
||||||
same items. This would cause problems when setSelectedObjects is called below.
|
|
||||||
Any KVO observation would try to retrieve the 'before' value which could be
|
|
||||||
wrong or even throw an exception for no longer existing indexes.
|
|
||||||
|
|
||||||
To avoid that, use the internal __setSelectedObjects which fires no notifications.
|
|
||||||
The selectionIndexes notifications will fire later since they depend on the
|
|
||||||
content key. This pattern is also applied for many other methods throughout this
|
|
||||||
class.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (_clearsFilterPredicateOnInsertion)
|
|
||||||
[self willChangeValueForKey:@"filterPredicate"];
|
|
||||||
|
|
||||||
// Don't use [super setContent:] as that would fire the contentObject change.
|
|
||||||
// We need to be in control of when notifications fire.
|
|
||||||
_contentObject = value;
|
|
||||||
|
|
||||||
if (_clearsFilterPredicateOnInsertion)
|
|
||||||
[self __setFilterPredicate:nil]; // Causes a _rearrangeObjects.
|
|
||||||
else
|
|
||||||
[self _rearrangeObjects];
|
|
||||||
|
|
||||||
if ([self preservesSelection])
|
|
||||||
[self __setSelectedObjects:oldSelectedObjects];
|
|
||||||
else
|
|
||||||
[self __setSelectionIndexes:oldSelectionIndexes];
|
|
||||||
|
|
||||||
if (_clearsFilterPredicateOnInsertion)
|
|
||||||
[self didChangeValueForKey:@"filterPredicate"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setContentArray:(id)anArray
|
|
||||||
{
|
|
||||||
[self setContent:anArray];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setContentSet:(id)aSet
|
|
||||||
{
|
|
||||||
[self setContent:aSet];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)contentArray
|
|
||||||
{
|
|
||||||
return [self content];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)contentSet
|
|
||||||
{
|
|
||||||
return [self content];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)arrangeObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
var filterPredicate = [self filterPredicate],
|
|
||||||
sortDescriptors = [self sortDescriptors];
|
|
||||||
|
|
||||||
if (filterPredicate && sortDescriptors)
|
|
||||||
{
|
|
||||||
var sortedObjects = [objects filteredArrayUsingPredicate:filterPredicate];
|
|
||||||
[sortedObjects sortUsingDescriptors:sortDescriptors];
|
|
||||||
return sortedObjects;
|
|
||||||
}
|
|
||||||
else if (filterPredicate)
|
|
||||||
return [objects filteredArrayUsingPredicate:filterPredicate];
|
|
||||||
else if (sortDescriptors)
|
|
||||||
return [objects sortedArrayUsingDescriptors:sortDescriptors];
|
|
||||||
|
|
||||||
return [objects copy];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)rearrangeObjects
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"arrangedObjects"];
|
|
||||||
[self _rearrangeObjects];
|
|
||||||
[self didChangeValueForKey:@"arrangedObjects"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Like rearrangeObjects but don't fire any change notifications.
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_rearrangeObjects
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Rearranging reapplies the selection criteria and may cause objects to disappear,
|
|
||||||
so take care of the selection.
|
|
||||||
*/
|
|
||||||
var oldSelectedObjects = nil,
|
|
||||||
oldSelectionIndexes = nil;
|
|
||||||
|
|
||||||
if ([self preservesSelection])
|
|
||||||
oldSelectedObjects = [self selectedObjects];
|
|
||||||
else
|
|
||||||
oldSelectionIndexes = [self selectionIndexes];
|
|
||||||
|
|
||||||
[self __setArrangedObjects:[self arrangeObjects:[self contentArray]]];
|
|
||||||
|
|
||||||
if ([self preservesSelection])
|
|
||||||
[self __setSelectedObjects:oldSelectedObjects];
|
|
||||||
else
|
|
||||||
[self __setSelectionIndexes:oldSelectionIndexes];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)__setArrangedObjects:(id)value
|
|
||||||
{
|
|
||||||
if (_arrangedObjects === value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_arrangedObjects = [[_CPObservableArray alloc] initWithArray:value];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)arrangedObjects
|
|
||||||
{
|
|
||||||
return _arrangedObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)sortDescriptors
|
|
||||||
{
|
|
||||||
return _sortDescriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setSortDescriptors:(CPArray)value
|
|
||||||
{
|
|
||||||
if (_sortDescriptors === value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_sortDescriptors = [value copy];
|
|
||||||
// Use the non-notification version since arrangedObjects already depends
|
|
||||||
// on sortDescriptors.
|
|
||||||
[self _rearrangeObjects];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPPredicate)filterPredicate
|
|
||||||
{
|
|
||||||
return _filterPredicate;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFilterPredicate:(CPPredicate)value
|
|
||||||
{
|
|
||||||
[self __setFilterPredicate:value];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Like setFilterPredicate but don't fire any change notifications.
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)__setFilterPredicate:(CPPredicate)value
|
|
||||||
{
|
|
||||||
if (_filterPredicate === value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_filterPredicate = value;
|
|
||||||
// Use the non-notification version since arrangedObjects already depends
|
|
||||||
// on filterPredicate.
|
|
||||||
[self _rearrangeObjects];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)alwaysUsesMultipleValuesMarker
|
|
||||||
{
|
|
||||||
return _alwaysUsesMultipleValuesMarker;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Selection
|
|
||||||
|
|
||||||
- (unsigned)selectionIndex
|
|
||||||
{
|
|
||||||
return [_selectionIndexes firstIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)setSelectionIndex:(unsigned)index
|
|
||||||
{
|
|
||||||
return [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPIndexSet)selectionIndexes
|
|
||||||
{
|
|
||||||
return _selectionIndexes;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes
|
|
||||||
{
|
|
||||||
[self _selectionWillChange]
|
|
||||||
[self __setSelectionIndexes:indexes];
|
|
||||||
[self _selectionDidChange];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Like setSelectionIndex but don't fire any change notifications.
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (BOOL)__setSelectionIndex:(int)theIndex
|
|
||||||
{
|
|
||||||
[self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Like setSelectionIndexes but don't fire any change notifications.
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (BOOL)__setSelectionIndexes:(CPIndexSet)indexes
|
|
||||||
{
|
|
||||||
if (!indexes)
|
|
||||||
indexes = [CPIndexSet indexSet];
|
|
||||||
|
|
||||||
if (![indexes count])
|
|
||||||
{
|
|
||||||
if (_avoidsEmptySelection && [[self arrangedObjects] count])
|
|
||||||
indexes = [CPIndexSet indexSetWithIndex:0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var objectsCount = [[self arrangedObjects] count];
|
|
||||||
// Remove out of bounds indexes.
|
|
||||||
[indexes removeIndexesInRange:CPMakeRange(objectsCount, [indexes lastIndex] + 1)];
|
|
||||||
// When avoiding empty selection and the deleted selection was at the bottom, select the last item.
|
|
||||||
if (![indexes count] && _avoidsEmptySelection && objectsCount)
|
|
||||||
indexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([_selectionIndexes isEqualToIndexSet:indexes])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
_selectionIndexes = [indexes copy];
|
|
||||||
|
|
||||||
// Push back the new selection to the model for selectionIndexes if we have one.
|
|
||||||
// There won't be an infinite loop because of the equality check above.
|
|
||||||
[[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)selectedObjects
|
|
||||||
{
|
|
||||||
var objects = [[self arrangedObjects] objectsAtIndexes:[self selectionIndexes]];
|
|
||||||
|
|
||||||
return [_CPObservableArray arrayWithArray:(objects || [])];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)setSelectedObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"selectionIndexes"];
|
|
||||||
[self _selectionWillChange];
|
|
||||||
|
|
||||||
[self __setSelectedObjects:objects];
|
|
||||||
|
|
||||||
[self didChangeValueForKey:@"selectionIndexes"];
|
|
||||||
[self _selectionDidChange];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Like setSelectedObjects but don't fire any change notifications.
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (BOOL)__setSelectedObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
var set = [CPIndexSet indexSet],
|
|
||||||
count = [objects count],
|
|
||||||
arrangedObjects = [self arrangedObjects];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var index = [arrangedObjects indexOfObject:[objects objectAtIndex:i]];
|
|
||||||
|
|
||||||
if (index !== CPNotFound)
|
|
||||||
[set addIndex:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self __setSelectionIndexes:set];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Moving selection
|
|
||||||
|
|
||||||
- (BOOL)canSelectPrevious
|
|
||||||
{
|
|
||||||
return [[self selectionIndexes] firstIndex] > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)selectPrevious:(id)sender
|
|
||||||
{
|
|
||||||
var index = [[self selectionIndexes] firstIndex] - 1;
|
|
||||||
|
|
||||||
if (index >= 0)
|
|
||||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canSelectNext
|
|
||||||
{
|
|
||||||
return [[self selectionIndexes] firstIndex] < [[self arrangedObjects] count] - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)selectNext:(id)sender
|
|
||||||
{
|
|
||||||
var index = [[self selectionIndexes] firstIndex] + 1;
|
|
||||||
|
|
||||||
if (index < [[self arrangedObjects] count])
|
|
||||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add/Remove
|
|
||||||
|
|
||||||
- (void)addObject:(id)object
|
|
||||||
{
|
|
||||||
if (![self canAdd])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_clearsFilterPredicateOnInsertion)
|
|
||||||
[self willChangeValueForKey:@"filterPredicate"];
|
|
||||||
|
|
||||||
[self willChangeValueForKey:@"content"];
|
|
||||||
[_contentObject addObject:object];
|
|
||||||
|
|
||||||
if (_clearsFilterPredicateOnInsertion)
|
|
||||||
[self __setFilterPredicate:nil];
|
|
||||||
|
|
||||||
if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
|
|
||||||
{
|
|
||||||
var pos = [_arrangedObjects insertObject:object inArraySortedByDescriptors:_sortDescriptors];
|
|
||||||
|
|
||||||
// selectionIndexes change notification will be fired as a result of the
|
|
||||||
// content change. Don't fire manually.
|
|
||||||
if (_selectsInsertedObjects)
|
|
||||||
[self __setSelectionIndex:pos];
|
|
||||||
else
|
|
||||||
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[self _rearrangeObjects];
|
|
||||||
|
|
||||||
[self didChangeValueForKey:@"content"];
|
|
||||||
if (_clearsFilterPredicateOnInsertion)
|
|
||||||
[self didChangeValueForKey:@"filterPredicate"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertObject:(id)anObject atArrangedObjectIndex:(int)anIndex
|
|
||||||
{
|
|
||||||
if (![self canAdd])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_clearsFilterPredicateOnInsertion)
|
|
||||||
[self willChangeValueForKey:@"filterPredicate"];
|
|
||||||
|
|
||||||
[self willChangeValueForKey:@"content"];
|
|
||||||
[_contentObject insertObject:anObject atIndex:anIndex];
|
|
||||||
|
|
||||||
if (_clearsFilterPredicateOnInsertion)
|
|
||||||
[self __setFilterPredicate:nil];
|
|
||||||
|
|
||||||
[[self arrangedObjects] insertObject:anObject atIndex:anIndex];
|
|
||||||
|
|
||||||
// selectionIndexes change notification will be fired as a result of the
|
|
||||||
// content change. Don't fire manually.
|
|
||||||
if ([self selectsInsertedObjects])
|
|
||||||
[self __setSelectionIndex:anIndex];
|
|
||||||
else
|
|
||||||
[[self selectionIndexes] shiftIndexesStartingAtIndex:anIndex by:1];
|
|
||||||
|
|
||||||
if ([self avoidsEmptySelection] && [[self selectionIndexes] count] <= 0 && [_contentObject count] > 0)
|
|
||||||
[self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]];
|
|
||||||
|
|
||||||
[self didChangeValueForKey:@"content"];
|
|
||||||
if (_clearsFilterPredicateOnInsertion)
|
|
||||||
[self didChangeValueForKey:@"filterPredicate"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObject:(id)object
|
|
||||||
{
|
|
||||||
if (![self canRemove])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self willChangeValueForKey:@"content"];
|
|
||||||
[_contentObject removeObject:object];
|
|
||||||
|
|
||||||
if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
|
|
||||||
{
|
|
||||||
// selectionIndexes change notification will be fired as a result of the
|
|
||||||
// content change. Don't fire manually.
|
|
||||||
var pos = [_arrangedObjects indexOfObject:object];
|
|
||||||
|
|
||||||
[_arrangedObjects removeObjectAtIndex:pos];
|
|
||||||
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self didChangeValueForKey:@"content"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)add:(id)sender
|
|
||||||
{
|
|
||||||
if (![self canAdd])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self insert:sender];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insert:(id)sender
|
|
||||||
{
|
|
||||||
if (![self canInsert])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject];
|
|
||||||
|
|
||||||
[self addObject:newObject];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)remove:(id)sender
|
|
||||||
{
|
|
||||||
[self removeObjects:[[self arrangedObjects] objectsAtIndexes:[self selectionIndexes]]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObjectsAtArrangedObjectIndexes:(CPIndexSet)indexes
|
|
||||||
{
|
|
||||||
[self _removeObjects:[[self arrangedObjects] objectsAtIndexes:indexes]];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)addObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
if (![self canAdd])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var contentArray = [self contentArray],
|
|
||||||
count = [objects count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
[contentArray addObject:[objects objectAtIndex:i]];
|
|
||||||
|
|
||||||
[self setContent:contentArray];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
if (![self canRemove])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self _removeObjects:objects];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_removeObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"content"];
|
|
||||||
[_contentObject removeObjectsInArray:objects];
|
|
||||||
|
|
||||||
var arrangedObjects = [self arrangedObjects],
|
|
||||||
position = [arrangedObjects indexOfObject:[objects objectAtIndex:0]];
|
|
||||||
|
|
||||||
[arrangedObjects removeObjectsInArray:objects];
|
|
||||||
|
|
||||||
var objectsCount = [arrangedObjects count],
|
|
||||||
selectionIndexes = [CPIndexSet indexSet];
|
|
||||||
|
|
||||||
if ([self preservesSelection] || [self avoidsEmptySelection])
|
|
||||||
{
|
|
||||||
selectionIndexes = [CPIndexSet indexSetWithIndex:position];
|
|
||||||
|
|
||||||
// Remove the selection if there are no objects
|
|
||||||
if (objectsCount <= 0)
|
|
||||||
selectionIndexes = [CPIndexSet indexSet];
|
|
||||||
|
|
||||||
// Shift selection to last object if position is out of bounds
|
|
||||||
else if (position >= objectsCount)
|
|
||||||
selectionIndexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
_selectionIndexes = selectionIndexes;
|
|
||||||
|
|
||||||
[self didChangeValueForKey:@"content"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canInsert
|
|
||||||
{
|
|
||||||
return [self isEditable];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPArrayController (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:coder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_avoidsEmptySelection = [coder decodeBoolForKey:@"CPArrayControllerAvoidsEmptySelection"];
|
|
||||||
_clearsFilterPredicateOnInsertion = [coder decodeBoolForKey:@"CPClearsFilterPredicateOnInsertion"];
|
|
||||||
_filterRestrictsInsertion = [coder decodeBoolForKey:@"CPArrayControllerFilterRestrictsInsertion"];
|
|
||||||
_preservesSelection = [coder decodeBoolForKey:@"CPArrayControllerPreservesSelection"];
|
|
||||||
_selectsInsertedObjects = [coder decodeBoolForKey:@"CPArrayControllerSelectsInsertedObjects"];
|
|
||||||
_alwaysUsesMultipleValuesMarker = [coder decodeBoolForKey:@"CPArrayControllerAlwaysUsesMultipleValuesMarker"];
|
|
||||||
|
|
||||||
if ([self automaticallyPreparesContent])
|
|
||||||
[self prepareContent];
|
|
||||||
else
|
|
||||||
[self _setContentArray:[]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromCib
|
|
||||||
{
|
|
||||||
[self _selectionWillChange];
|
|
||||||
[self _selectionDidChange];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+19
-97
@@ -4,7 +4,7 @@
|
|||||||
* Created by Ross Boucher.
|
* Created by Ross Boucher.
|
||||||
* Copyright 2009, 280 North, Inc.
|
* Copyright 2009, 280 North, Inc.
|
||||||
*
|
*
|
||||||
* Adapted from Kevin Wojniak, portions Copyright 2009 Kevin Wojniak.
|
* Adapted from Kevin Wojniak, portions Copyright 2009 Kevin Wojniak.
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@@ -20,27 +20,26 @@
|
|||||||
* License along with this library; if not, write to the Free Software
|
* License along with this library; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
* Copyright 2009 280 North, Inc.
|
* Copyright 2009 280 North, Inc.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@import <AppKit/CPGraphicsContext.j>
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
|
||||||
@import "CPGraphicsContext.j"
|
|
||||||
|
|
||||||
|
|
||||||
// Class defaults
|
// Class defaults
|
||||||
|
|
||||||
var DefaultLineWidth = 1.0;
|
var DefaultLineWidth = 1.0;
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPBezierPath
|
@class CPBezierPath
|
||||||
|
|
||||||
A CPBezierPath allows you to create paths for drawing to the screen using a simpler API than CoreGraphics.
|
A CPBezierPath allows you to create paths for drawing to the screen using a simpler API than CoreGraphics.
|
||||||
Paths can form any shape, including regular polgyons like squares and triangles; circles, arcs; or complex
|
Paths can form any shape, including regular polgyons like squares and triangles; circles, arcs; or complex
|
||||||
line segments.
|
line segments.
|
||||||
|
|
||||||
A path can be stroked and filled using the relevant method. The currently active fill and stroke color will
|
A path can be stroked and filled using the relevant method. The currently active fill and stroke color will
|
||||||
be used, which can be set by calling setFill: and setStroke: on any CPColor object (or set: for both).
|
be used, which can be set by calling setFill: and setStroke: on any CPColor object (or set: for both).
|
||||||
*/
|
*/
|
||||||
@@ -51,76 +50,52 @@ var DefaultLineWidth = 1.0;
|
|||||||
float _lineWidth;
|
float _lineWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Create a new CPBezierPath object.
|
|
||||||
*/
|
|
||||||
+ (CPBezierPath)bezierPath
|
+ (CPBezierPath)bezierPath
|
||||||
{
|
{
|
||||||
return [[self alloc] init];
|
return [[[self class] alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Create a new CPBezierPath object initialized with an oval path drawn within a rectangular path.
|
|
||||||
*/
|
|
||||||
+ (CPBezierPath)bezierPathWithOvalInRect:(CGRect)rect
|
+ (CPBezierPath)bezierPathWithOvalInRect:(CGRect)rect
|
||||||
{
|
{
|
||||||
var path = [self bezierPath];
|
var path = [[self class] bezierPath];
|
||||||
|
|
||||||
[path appendBezierPathWithOvalInRect:rect];
|
[path appendBezierPathWithOvalInRect:rect];
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Create a new CPBezierPath object initialized with a rectangular path.
|
|
||||||
*/
|
|
||||||
+ (CPBezierPath)bezierPathWithRect:(CGRect)rect
|
+ (CPBezierPath)bezierPathWithRect:(CGRect)rect
|
||||||
{
|
{
|
||||||
var path = [self bezierPath];
|
var path = [[self class] bezierPath];
|
||||||
|
|
||||||
[path appendBezierPathWithRect:rect];
|
[path appendBezierPathWithRect:rect];
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Get default line width.
|
|
||||||
*/
|
|
||||||
+ (float)defaultLineWidth
|
+ (float)defaultLineWidth
|
||||||
{
|
{
|
||||||
return DefaultLineWidth;
|
return DefaultLineWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Set default line width.
|
|
||||||
*/
|
|
||||||
+ (void)setDefaultLineWidth:(float)width
|
+ (void)setDefaultLineWidth:(float)width
|
||||||
{
|
{
|
||||||
DefaultLineWidth = width;
|
DefaultLineWidth = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
+ (void)fillRect:(CGRect)rect
|
||||||
Fill rectangular path with current fill color.
|
|
||||||
*/
|
|
||||||
+ (void)fillRect:(CGRect)aRect
|
|
||||||
{
|
{
|
||||||
[[self bezierPathWithRect:aRect] fill];
|
[[[self class] bezierPathWithRect:rect] 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 bezierPathWithRect:aRect] stroke];
|
[[[self class] bezierPathWithRect:rect] stroke];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Using the current stroke color and default drawing attributes, strokes a line between two points.
|
|
||||||
*/
|
|
||||||
+ (void)strokeLineFromPoint:(CGPoint)point1 toPoint:(CGPoint)point2
|
+ (void)strokeLineFromPoint:(CGPoint)point1 toPoint:(CGPoint)point2
|
||||||
{
|
{
|
||||||
var path = [self bezierPath];
|
var path = [[self class] bezierPath];
|
||||||
|
|
||||||
[path moveToPoint:point1];
|
[path moveToPoint:point1];
|
||||||
[path lineToPoint:point2];
|
[path lineToPoint:point2];
|
||||||
@@ -128,9 +103,6 @@ var DefaultLineWidth = 1.0;
|
|||||||
[path stroke];
|
[path stroke];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Create a new CPBezierPath object using the default line width.
|
|
||||||
*/
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
@@ -142,41 +114,26 @@ var DefaultLineWidth = 1.0;
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Moves the current point to another location.
|
|
||||||
*/
|
|
||||||
- (void)moveToPoint:(CGPoint)point
|
- (void)moveToPoint:(CGPoint)point
|
||||||
{
|
{
|
||||||
CGPathMoveToPoint(_path, nil, point.x, point.y);
|
CGPathMoveToPoint(_path, nil, point.x, point.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Append a straight line to the path.
|
|
||||||
*/
|
|
||||||
- (void)lineToPoint:(CGPoint)point
|
- (void)lineToPoint:(CGPoint)point
|
||||||
{
|
{
|
||||||
CGPathAddLineToPoint(_path, nil, point.x, point.y);
|
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
|
- (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);
|
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
|
- (void)closePath
|
||||||
{
|
{
|
||||||
CGPathCloseSubpath(_path);
|
CGPathCloseSubpath(_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Draw a line along the path with the current stroke color and default drawing attributes.
|
|
||||||
*/
|
|
||||||
- (void)stroke
|
- (void)stroke
|
||||||
{
|
{
|
||||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||||
@@ -188,9 +145,6 @@ var DefaultLineWidth = 1.0;
|
|||||||
CGContextStrokePath(ctx);
|
CGContextStrokePath(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Fill the path with the current fill color.
|
|
||||||
*/
|
|
||||||
- (void)fill
|
- (void)fill
|
||||||
{
|
{
|
||||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||||
@@ -202,88 +156,56 @@ var DefaultLineWidth = 1.0;
|
|||||||
CGContextFillPath(ctx);
|
CGContextFillPath(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Get the line width.
|
|
||||||
*/
|
|
||||||
- (float)lineWidth
|
- (float)lineWidth
|
||||||
{
|
{
|
||||||
return _lineWidth;
|
return _lineWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the line width.
|
|
||||||
*/
|
|
||||||
- (void)setLineWidth:(float)lineWidth
|
- (void)setLineWidth:(float)lineWidth
|
||||||
{
|
{
|
||||||
_lineWidth = lineWidth;
|
_lineWidth = lineWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Get the total number of elements.
|
|
||||||
*/
|
|
||||||
- (unsigned)elementCount
|
- (unsigned)elementCount
|
||||||
{
|
{
|
||||||
return _path.count;
|
return _path.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Check if receiver is empty, returns appropriate Boolean value.
|
|
||||||
*/
|
|
||||||
- (BOOL)isEmpty
|
- (BOOL)isEmpty
|
||||||
{
|
{
|
||||||
return CGPathIsEmpty(_path);
|
return CGPathIsEmpty(_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Get the current point.
|
|
||||||
*/
|
|
||||||
- (CGPoint)currentPoint
|
- (CGPoint)currentPoint
|
||||||
{
|
{
|
||||||
return CGPathGetCurrentPoint(_path);
|
return CGPathGetCurrentPoint(_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Append a series of line segments.
|
|
||||||
*/
|
|
||||||
- (void)appendBezierPathWithPoints:(CPArray)points count:(unsigned)count
|
- (void)appendBezierPathWithPoints:(CPArray)points count:(unsigned)count
|
||||||
{
|
{
|
||||||
CGPathAddLines(_path, nil, points, count);
|
CGPathAddLines(_path, nil, points, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Append a rectangular path.
|
|
||||||
*/
|
|
||||||
- (void)appendBezierPathWithRect:(CGRect)rect
|
- (void)appendBezierPathWithRect:(CGRect)rect
|
||||||
{
|
{
|
||||||
CGPathAddRect(_path, nil, rect);
|
CGPathAddRect(_path, nil, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Append an oval path; oval is drawn within the rectangular path.
|
|
||||||
*/
|
|
||||||
- (void)appendBezierPathWithOvalInRect:(CGRect)rect
|
- (void)appendBezierPathWithOvalInRect:(CGRect)rect
|
||||||
{
|
{
|
||||||
CGPathAddPath(_path, nil, CGPathWithEllipseInRect(rect));
|
CGPathAddPath(_path, nil, CGPathWithEllipseInRect(rect));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Append a rounded rectangular path.
|
|
||||||
*/
|
|
||||||
- (void)appendBezierPathWithRoundedRect:(CGRect)rect xRadius:(float)xRadius yRadius:(float)yRadius
|
- (void)appendBezierPathWithRoundedRect:(CGRect)rect xRadius:(float)xRadius yRadius:(float)yRadius
|
||||||
{
|
{
|
||||||
CGPathAddPath(_path, nil, CGPathWithRoundedRectangleInRect(rect, xRadius, yRadius, YES, YES, YES, YES));
|
CGPathAddPath(_path, nil, CGPathWithRoundedRectangleInRect(rect, xRadius, yRadius, YES, YES, YES, YES));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Append the contents of a CPBezierPath object.
|
|
||||||
*/
|
|
||||||
- (void)appendBezierPath:(NSBezierPath *)other
|
- (void)appendBezierPath:(NSBezierPath *)other
|
||||||
{
|
{
|
||||||
CGPathAddPath(_path, nil, other._path);
|
CGPathAddPath(_path, nil, other._path);
|
||||||
}
|
}
|
||||||
/*!
|
|
||||||
Remove all path elements; clears path.
|
|
||||||
*/
|
|
||||||
- (void)removeAllPoints
|
- (void)removeAllPoints
|
||||||
{
|
{
|
||||||
_path = CGPathCreateMutable();
|
_path = CGPathCreateMutable();
|
||||||
|
|||||||
-368
@@ -1,368 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPBox.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 "CPView.j"
|
|
||||||
|
|
||||||
// CPBoxType
|
|
||||||
CPBoxPrimary = 0;
|
|
||||||
CPBoxSecondary = 1;
|
|
||||||
CPBoxSeperator = 2;
|
|
||||||
CPBoxOldStyle = 3;
|
|
||||||
CPBoxCustom = 4;
|
|
||||||
|
|
||||||
// CPBorderType
|
|
||||||
CPNoBorder = 0;
|
|
||||||
CPLineBorder = 1;
|
|
||||||
CPBezelBorder = 2;
|
|
||||||
CPGrooveBorder = 3;
|
|
||||||
|
|
||||||
@implementation CPBox : CPView
|
|
||||||
{
|
|
||||||
CPBoxType _boxType;
|
|
||||||
CPBorderType _borderType;
|
|
||||||
|
|
||||||
CPColor _borderColor;
|
|
||||||
CPColor _fillColor;
|
|
||||||
|
|
||||||
float _cornerRadius;
|
|
||||||
float _borderWidth;
|
|
||||||
|
|
||||||
CPSize _contentMargin;
|
|
||||||
CPView _contentView;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)boxEnclosingView:(CPView)aView
|
|
||||||
{
|
|
||||||
var box = [[self alloc] initWithFrame:CGRectMakeZero()],
|
|
||||||
enclosingView = [aView superview];
|
|
||||||
|
|
||||||
[box setFrameFromContentFrame:[aView frame]];
|
|
||||||
|
|
||||||
[enclosingView replaceSubview:aView with:box];
|
|
||||||
|
|
||||||
[box setContentView:aView];
|
|
||||||
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CPRect)frameRect
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:frameRect];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_borderType = CPBezelBorder;
|
|
||||||
_fillColor = [CPColor clearColor];
|
|
||||||
_borderColor = [CPColor blackColor];
|
|
||||||
|
|
||||||
_borderWidth = 1.0;
|
|
||||||
_contentMargin = CGSizeMake(0.0, 0.0);
|
|
||||||
|
|
||||||
_contentView = [[CPView alloc] initWithFrame:[self bounds]];
|
|
||||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
||||||
|
|
||||||
[self setAutoresizesSubviews:YES];
|
|
||||||
[self addSubview:_contentView];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configuring Boxes
|
|
||||||
|
|
||||||
- (CPRect)borderRect
|
|
||||||
{
|
|
||||||
return [self bounds];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPBorderType)borderType
|
|
||||||
{
|
|
||||||
return _borderType;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setBorderType:(CPBorderType)aBorderType
|
|
||||||
{
|
|
||||||
if (_borderType === aBorderType)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_borderType = aBorderType;
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPBoxType)boxType
|
|
||||||
{
|
|
||||||
return _boxType;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setBoxType:(CPBoxType)aBoxType
|
|
||||||
{
|
|
||||||
if (_boxType === aBoxType)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_boxType = aBoxType;
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)borderColor
|
|
||||||
{
|
|
||||||
return _borderColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setBorderColor:(CPColor)color
|
|
||||||
{
|
|
||||||
if ([color isEqual:_borderColor])
|
|
||||||
return;
|
|
||||||
|
|
||||||
_borderColor = color;
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)borderWidth
|
|
||||||
{
|
|
||||||
return _borderWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setBorderWidth:(float)width
|
|
||||||
{
|
|
||||||
if (width === _borderWidth)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_borderWidth = width;
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)cornerRadius
|
|
||||||
{
|
|
||||||
return _cornerRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setCornerRadius:(float)radius
|
|
||||||
{
|
|
||||||
if (radius === _cornerRadius)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_cornerRadius = radius;
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)fillColor
|
|
||||||
{
|
|
||||||
return _fillColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFillColor:(CPColor)color
|
|
||||||
{
|
|
||||||
if ([color isEqual:_fillColor])
|
|
||||||
return;
|
|
||||||
|
|
||||||
_fillColor = color;
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)contentView
|
|
||||||
{
|
|
||||||
return _contentView;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setContentView:(CPView)aView
|
|
||||||
{
|
|
||||||
if (aView === _contentView)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[aView setFrame:CGRectInset([self bounds], _contentMargin.width + _borderWidth, _contentMargin.height + _borderWidth)];
|
|
||||||
[aView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
||||||
[self replaceSubview:_contentView with:aView];
|
|
||||||
|
|
||||||
_contentView = aView;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPSize)contentViewMargins
|
|
||||||
{
|
|
||||||
return _contentMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setContentViewMargins:(CPSize)size
|
|
||||||
{
|
|
||||||
if (size.width < 0 || size.height < 0)
|
|
||||||
[CPException raise:CPGenericException reason:@"Margins must be positive"];
|
|
||||||
|
|
||||||
_contentMargin = CGSizeMakeCopy(size);
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFrameFromContentFrame:(CPRect)aRect
|
|
||||||
{
|
|
||||||
[self setFrame:CGRectInset(aRect, -(_contentMargin.width + _borderWidth), -(_contentMargin.height + _borderWidth))];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)sizeToFit
|
|
||||||
{
|
|
||||||
var contentFrame = [_contentView frame];
|
|
||||||
|
|
||||||
[self setFrameSize:CGSizeMake(contentFrame.size.width + _contentMargin.width * 2,
|
|
||||||
contentFrame.size.height + _contentMargin.height * 2)];
|
|
||||||
|
|
||||||
[_contentView setFrameOrigin:CGPointMake(_contentMargin.width, _contentMargin.height)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CPRect)rect
|
|
||||||
{
|
|
||||||
if (_borderType === CPNoBorder)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var bounds = [self bounds];
|
|
||||||
|
|
||||||
switch (_boxType)
|
|
||||||
{
|
|
||||||
case CPBoxSeperator:
|
|
||||||
// NSBox does not include a horitontal flag for the seperator type. We have to determine
|
|
||||||
// the type of seperator to draw by the width and height of the frame.
|
|
||||||
if (CGRectGetWidth(bounds) === 5.0)
|
|
||||||
return [self _drawVerticalSeperatorInRect:bounds];
|
|
||||||
else if (CGRectGetHeight(bounds) === 5.0)
|
|
||||||
return [self _drawHorizontalSeperatorInRect:bounds];
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_borderType)
|
|
||||||
{
|
|
||||||
case CPBezelBorder:
|
|
||||||
[self _drawBezelBorderInRect:bounds];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
case CPLineBorder:
|
|
||||||
[self _drawLineBorderInRect:bounds];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_drawHorizontalSeperatorInRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, [self borderColor]);
|
|
||||||
CGContextSetLineWidth(context, 1.0);
|
|
||||||
|
|
||||||
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
|
|
||||||
CGContextAddLineToPoint(context, CGRectGetWidth(aRect), CGRectGetMinY(aRect));
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_drawVerticalSeperatorInRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, [self borderColor]);
|
|
||||||
CGContextSetLineWidth(context, 1.0);
|
|
||||||
|
|
||||||
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
|
|
||||||
CGContextAddLineToPoint(context, CGRectGetMinX(aRect), CGRectGetHeight(aRect));
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_drawBezelBorderInRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
sides = [CPMinYEdge, CPMaxXEdge, CPMaxYEdge, CPMinXEdge],
|
|
||||||
sideGray = 190.0 / 255.0,
|
|
||||||
grays = [142.0 / 255.0, sideGray, sideGray, sideGray],
|
|
||||||
borderWidth = _borderWidth;
|
|
||||||
|
|
||||||
while (borderWidth--)
|
|
||||||
aRect = CPDrawTiledRects(aRect, aRect, sides, grays);
|
|
||||||
|
|
||||||
CGContextSetFillColor(context, [self fillColor]);
|
|
||||||
CGContextFillRect(context, aRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_drawLineBorderInRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
aRect = CGRectInset(aRect, _borderWidth / 2.0, _borderWidth / 2.0);
|
|
||||||
|
|
||||||
CGContextSetFillColor(context, [self fillColor]);
|
|
||||||
CGContextSetStrokeColor(context, [self borderColor]);
|
|
||||||
|
|
||||||
CGContextSetLineWidth(context, _borderWidth);
|
|
||||||
CGContextFillRoundedRectangleInRect(context, aRect, _cornerRadius, YES, YES, YES, YES);
|
|
||||||
CGContextStrokeRoundedRectangleInRect(context, aRect, _cornerRadius, YES, YES, YES, YES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPBoxTypeKey = @"CPBoxTypeKey",
|
|
||||||
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
|
|
||||||
CPBoxBorderColorKey = @"CPBoxBorderColorKey",
|
|
||||||
CPBoxFillColorKey = @"CPBoxFillColorKey",
|
|
||||||
CPBoxCornerRadiusKey = @"CPBoxCornerRadiusKey",
|
|
||||||
CPBoxBorderWidthKey = @"CPBoxBorderWidthKey",
|
|
||||||
CPBoxContentMarginKey = @"CPBoxContentMarginKey";
|
|
||||||
|
|
||||||
@implementation CPBox (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_boxType = [aCoder decodeIntForKey:CPBoxTypeKey];
|
|
||||||
_borderType = [aCoder decodeIntForKey:CPBoxBorderTypeKey];
|
|
||||||
|
|
||||||
_borderColor = [aCoder decodeObjectForKey:CPBoxBorderColorKey];
|
|
||||||
_fillColor = [aCoder decodeObjectForKey:CPBoxFillColorKey];
|
|
||||||
|
|
||||||
_cornerRadius = [aCoder decodeFloatForKey:CPBoxCornerRadiusKey];
|
|
||||||
_borderWidth = [aCoder decodeFloatForKey:CPBoxBorderWidthKey];
|
|
||||||
|
|
||||||
_contentMargin = [aCoder decodeSizeForKey:CPBoxContentMarginKey];
|
|
||||||
|
|
||||||
_contentView = [self subviews][0];
|
|
||||||
|
|
||||||
[self setAutoresizesSubviews:YES];
|
|
||||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeInt:_boxType forKey:CPBoxTypeKey];
|
|
||||||
[aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_borderColor forKey:CPBoxBorderColorKey];
|
|
||||||
[aCoder encodeObject:_fillColor forKey:CPBoxFillColorKey];
|
|
||||||
|
|
||||||
[aCoder encodeFloat:_cornerRadius forKey:CPBoxCornerRadiusKey];
|
|
||||||
[aCoder encodeFloat:_borderWidth forKey:CPBoxBorderWidthKey];
|
|
||||||
|
|
||||||
[aCoder encodeSize:_contentMargin forKey:CPBoxContentMarginKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,955 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPBrowser.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Ross Boucher.
|
|
||||||
* Copyright 2010, 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/CPIndexSet.j>
|
|
||||||
|
|
||||||
@import "CPControl.j"
|
|
||||||
@import "CPImage.j"
|
|
||||||
@import "CPTableView.j"
|
|
||||||
@import "CPTextField.j"
|
|
||||||
@import "CPScrollView.j"
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
@class CPBrowser
|
|
||||||
*/
|
|
||||||
|
|
||||||
@implementation CPBrowser : CPControl
|
|
||||||
{
|
|
||||||
id _delegate;
|
|
||||||
CPString _pathSeparator;
|
|
||||||
|
|
||||||
CPView _contentView;
|
|
||||||
CPScrollView _horizontalScrollView;
|
|
||||||
CPView _prototypeView;
|
|
||||||
|
|
||||||
CPArray _tableViews;
|
|
||||||
CPArray _tableDelegates;
|
|
||||||
|
|
||||||
id _rootItem;
|
|
||||||
|
|
||||||
BOOL _delegateSupportsImages;
|
|
||||||
|
|
||||||
SEL _doubleAction @accessors(property=doubleAction);
|
|
||||||
|
|
||||||
BOOL _allowsMultipleSelection;
|
|
||||||
BOOL _allowsEmptySelection;
|
|
||||||
|
|
||||||
Class _tableViewClass @accessors(property=tableViewClass);
|
|
||||||
|
|
||||||
float _rowHeight;
|
|
||||||
float _imageWidth;
|
|
||||||
float _leafWidth;
|
|
||||||
float _minColumnWidth;
|
|
||||||
float _defaultColumnWidth @accessors(property=defaultColumnWidth);
|
|
||||||
|
|
||||||
CPArray _columnWidths;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPImage)branchImage
|
|
||||||
{
|
|
||||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]]
|
|
||||||
pathForResource:"browser-leaf.png"]
|
|
||||||
size:CGSizeMake(9,9)];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPImage)highlightedBranchImage
|
|
||||||
{
|
|
||||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]]
|
|
||||||
pathForResource:"browser-leaf-highlighted.png"]
|
|
||||||
size:CGSizeMake(9,9)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
{
|
|
||||||
_rowHeight = 23.0;
|
|
||||||
_defaultColumnWidth = 140.0;
|
|
||||||
_minColumnWidth = 80.0;
|
|
||||||
_imageWidth = 23.0;
|
|
||||||
_leafWidth = 13.0;
|
|
||||||
_columnWidths = [];
|
|
||||||
|
|
||||||
_pathSeparator = "/";
|
|
||||||
_tableViews = [];
|
|
||||||
_tableDelegates = [];
|
|
||||||
_allowsMultipleSelection = YES;
|
|
||||||
_allowsEmptySelection = YES;
|
|
||||||
_tableViewClass = [_CPBrowserTableView class];
|
|
||||||
|
|
||||||
_prototypeView = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[_prototypeView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
|
||||||
[_prototypeView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateSelectedDataView];
|
|
||||||
[_prototypeView setLineBreakMode:CPLineBreakByTruncatingTail];
|
|
||||||
|
|
||||||
_horizontalScrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
|
||||||
|
|
||||||
[_horizontalScrollView setHasVerticalScroller:NO];
|
|
||||||
[_horizontalScrollView setAutohidesScrollers:YES];
|
|
||||||
[_horizontalScrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
||||||
|
|
||||||
_contentView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 0, CGRectGetHeight([self bounds]))];
|
|
||||||
[_contentView setAutoresizingMask:CPViewHeightSizable];
|
|
||||||
|
|
||||||
[_horizontalScrollView setDocumentView:_contentView];
|
|
||||||
|
|
||||||
[self addSubview:_horizontalScrollView];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPrototypeView:(CPView)aPrototypeView
|
|
||||||
{
|
|
||||||
_prototypeView = [CPKeyedUnarchiver unarchiveObjectWithData:
|
|
||||||
[CPKeyedArchiver archivedDataWithRootObject:aPrototypeView]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)prototypeView
|
|
||||||
{
|
|
||||||
return [CPKeyedUnarchiver unarchiveObjectWithData:
|
|
||||||
[CPKeyedArchiver archivedDataWithRootObject:_prototypeView]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDelegate:(id)anObject
|
|
||||||
{
|
|
||||||
_delegate = anObject;
|
|
||||||
_delegateSupportsImages = [_delegate respondsToSelector:@selector(browser:imageValueForItem:)];
|
|
||||||
|
|
||||||
[self loadColumnZero];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)delegate
|
|
||||||
{
|
|
||||||
return _delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPTableView)tableViewInColumn:(unsigned)index
|
|
||||||
{
|
|
||||||
return _tableViews[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)columnOfTableView:(CPTableView)aTableView
|
|
||||||
{
|
|
||||||
return [_tableViews indexOfObject:aTableView];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)loadColumnZero
|
|
||||||
{
|
|
||||||
if ([_delegate respondsToSelector:@selector(rootItemForBrowser:)])
|
|
||||||
_rootItem = [_delegate rootItemForBrowser:self];
|
|
||||||
else
|
|
||||||
_rootItem = nil;
|
|
||||||
|
|
||||||
[self setLastColumn:-1];
|
|
||||||
[self addColumn];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setLastColumn:(int)columnIndex
|
|
||||||
{
|
|
||||||
if (columnIndex >= _tableViews.length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var oldValue = _tableViews.length - 1,
|
|
||||||
indexPlusOne = columnIndex + 1; // unloads all later columns.
|
|
||||||
|
|
||||||
[[_tableViews.slice(indexPlusOne) valueForKey:"enclosingScrollView"]
|
|
||||||
makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
|
||||||
|
|
||||||
_tableViews = _tableViews.slice(0, indexPlusOne);
|
|
||||||
_tableDelegates = _tableDelegates.slice(0, indexPlusOne);
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:didChangeLastColumn:toColumn:)])
|
|
||||||
[_delegate browser:self didChangeLastColumn:oldValue toColumn:columnIndex];
|
|
||||||
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)lastColumn
|
|
||||||
{
|
|
||||||
return _tableViews.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addColumn
|
|
||||||
{
|
|
||||||
var lastIndex = [self lastColumn],
|
|
||||||
lastColumn = _tableViews[lastIndex],
|
|
||||||
selectionIndexes = [lastColumn selectedRowIndexes];
|
|
||||||
|
|
||||||
if (lastIndex >= 0 && [selectionIndexes count] > 1)
|
|
||||||
[CPException raise:CPInvalidArgumentException
|
|
||||||
reason:"Can't add column, column "+lastIndex+" has invalid selection."];
|
|
||||||
|
|
||||||
var index = lastIndex + 1,
|
|
||||||
item = index === 0 ? _rootItem : [_tableDelegates[lastIndex] childAtIndex:[selectionIndexes firstIndex]];
|
|
||||||
|
|
||||||
if (index > 0 && item && [self isLeafItem:item])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var table = [[_tableViewClass alloc] initWithFrame:CGRectMakeZero() browser:self];
|
|
||||||
|
|
||||||
[table setHeaderView:nil];
|
|
||||||
[table setCornerView:nil];
|
|
||||||
[table setAllowsMultipleSelection:_allowsMultipleSelection];
|
|
||||||
[table setAllowsEmptySelection:_allowsEmptySelection];
|
|
||||||
[table registerForDraggedTypes:[self registeredDraggedTypes]];
|
|
||||||
|
|
||||||
[self _addTableColumnsToTableView:table forColumnIndex:index];
|
|
||||||
|
|
||||||
var delegate = [[_CPBrowserTableDelegate alloc] init];
|
|
||||||
|
|
||||||
[delegate _setDelegate:_delegate];
|
|
||||||
[delegate _setBrowser:self];
|
|
||||||
[delegate _setIndex:index];
|
|
||||||
[delegate _setItem:item];
|
|
||||||
|
|
||||||
_tableViews[index] = table;
|
|
||||||
_tableDelegates[index] = delegate;
|
|
||||||
|
|
||||||
[table setDelegate:delegate];
|
|
||||||
[table setDataSource:delegate];
|
|
||||||
[table setTarget:delegate];
|
|
||||||
[table setAction:@selector(_tableViewClicked:)];
|
|
||||||
[table setDoubleAction:@selector(_tableViewDoubleClicked:)];
|
|
||||||
[table setDraggingDestinationFeedbackStyle:CPTableViewDraggingDestinationFeedbackStyleRegular];
|
|
||||||
|
|
||||||
var scrollView = [[_CPBrowserScrollView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[scrollView _setBrowser:self];
|
|
||||||
[scrollView setDocumentView:table];
|
|
||||||
[scrollView setHasHorizontalScroller:NO];
|
|
||||||
[scrollView setAutoresizingMask:CPViewHeightSizable];
|
|
||||||
|
|
||||||
[_contentView addSubview:scrollView];
|
|
||||||
|
|
||||||
[self tile];
|
|
||||||
|
|
||||||
[self scrollColumnToVisible:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_addTableColumnsToTableView:(CPTableView)aTableView forColumnIndex:(unsigned)index
|
|
||||||
{
|
|
||||||
if (_delegateSupportsImages)
|
|
||||||
{
|
|
||||||
var column = [[CPTableColumn alloc] initWithIdentifier:@"Image"],
|
|
||||||
view = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
[view setImageScaling:CPScaleProportionally];
|
|
||||||
|
|
||||||
[column setDataView:view];
|
|
||||||
[column setResizingMask:CPTableColumnNoResizing];
|
|
||||||
|
|
||||||
[aTableView addTableColumn:column];
|
|
||||||
}
|
|
||||||
|
|
||||||
var column = [[CPTableColumn alloc] initWithIdentifier:@"Content"];
|
|
||||||
|
|
||||||
[column setDataView:_prototypeView];
|
|
||||||
[column setResizingMask:CPTableColumnNoResizing];
|
|
||||||
|
|
||||||
[aTableView addTableColumn:column];
|
|
||||||
|
|
||||||
var column = [[CPTableColumn alloc] initWithIdentifier:@"Leaf"],
|
|
||||||
view = [[_CPBrowserLeafView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
[view setBranchImage:[[self class] branchImage]];
|
|
||||||
[view setHighlightedBranchImage:[[self class] highlightedBranchImage]];
|
|
||||||
|
|
||||||
[column setDataView:view];
|
|
||||||
[column setResizingMask:CPTableColumnNoResizing];
|
|
||||||
|
|
||||||
[aTableView addTableColumn:column];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reloadColumn:(int)column
|
|
||||||
{
|
|
||||||
[[self tableViewInColumn:column] reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tile
|
|
||||||
{
|
|
||||||
var xOrigin = 0,
|
|
||||||
scrollerWidth = [CPScroller scrollerWidth],
|
|
||||||
height = CGRectGetHeight([_contentView bounds]);
|
|
||||||
|
|
||||||
for (var i = 0, count = _tableViews.length; i < count; i++)
|
|
||||||
{
|
|
||||||
var tableView = _tableViews[i],
|
|
||||||
scrollView = [tableView enclosingScrollView],
|
|
||||||
width = [self widthOfColumn:i],
|
|
||||||
tableHeight = CGRectGetHeight([tableView bounds]);
|
|
||||||
|
|
||||||
[[tableView tableColumnWithIdentifier:"Image"] setWidth:_imageWidth];
|
|
||||||
[[tableView tableColumnWithIdentifier:"Content"] setWidth:width - (_leafWidth + _delegateSupportsImages ? _imageWidth : 0) - scrollerWidth - scrollerWidth];
|
|
||||||
[[tableView tableColumnWithIdentifier:"Leaf"] setWidth:_leafWidth];
|
|
||||||
|
|
||||||
[tableView setRowHeight:_rowHeight];
|
|
||||||
[tableView setFrameSize:CGSizeMake(width - scrollerWidth, tableHeight)];
|
|
||||||
[scrollView setFrameOrigin:CGPointMake(xOrigin, 0)];
|
|
||||||
[scrollView setFrameSize:CGSizeMake(width, height)];
|
|
||||||
|
|
||||||
xOrigin += width;
|
|
||||||
}
|
|
||||||
|
|
||||||
[_contentView setFrameSize:CGSizeMake(xOrigin, height)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)rowAtPoint:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var column = [self columnAtPoint:aPoint];
|
|
||||||
if (column === -1)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
var tableView = _tableViews[column];
|
|
||||||
return [tableView rowAtPoint:[tableView convertPoint:aPoint fromView:self]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)columnAtPoint:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var adjustedPoint = [_contentView convertPoint:aPoint fromView:self];
|
|
||||||
|
|
||||||
for (var i = 0, count = _tableViews.length; i < count; i++)
|
|
||||||
{
|
|
||||||
var frame = [[_tableViews[i] enclosingScrollView] frame];
|
|
||||||
if (CGRectContainsPoint(frame, adjustedPoint))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectOfRow:(unsigned)aRow inColumn:(unsigned)aColumn
|
|
||||||
{
|
|
||||||
var tableView = _tableViews[aColumn],
|
|
||||||
rect = [tableView rectOfRow:aRow];
|
|
||||||
|
|
||||||
rect.origin = [self convertPoint:rect.origin fromView:tableView];
|
|
||||||
return rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ITEMS
|
|
||||||
|
|
||||||
- (id)itemAtRow:(int)row inColumn:(int)column
|
|
||||||
{
|
|
||||||
return [_tableDelegates[column] childAtIndex:row];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isLeafItem:(id)item
|
|
||||||
{
|
|
||||||
return [_delegate respondsToSelector:@selector(browser:isLeafItem:)] && [_delegate browser:self isLeafItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)parentForItemsInColumn:(int)column
|
|
||||||
{
|
|
||||||
return [_tableDelegates[column] _item];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPSet)selectedItems
|
|
||||||
{
|
|
||||||
var selectedColumn = [self selectedColumn],
|
|
||||||
selectedIndexes = [self selectedRowIndexesInColumn:selectedColumn],
|
|
||||||
set = [CPSet set],
|
|
||||||
index = [selectedIndexes firstIndex];
|
|
||||||
|
|
||||||
while (index !== CPNotFound)
|
|
||||||
{
|
|
||||||
[set addObject:[self itemAtRow:index inColumn:selectedColumn]];
|
|
||||||
index = [selectedIndexes indexGreaterThanIndex:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)selectedItem
|
|
||||||
{
|
|
||||||
var selectedColumn = [self selectedColumn],
|
|
||||||
selectedRow = [self selectedRowInColumn:selectedColumn];
|
|
||||||
|
|
||||||
return [self itemAtRow:selectedRow inColumn:selectedColumn];
|
|
||||||
}
|
|
||||||
|
|
||||||
// CLICK EVENTS
|
|
||||||
|
|
||||||
- (void)trackMouse:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_column:(unsigned)columnIndex clickedRow:(unsigned)rowIndex
|
|
||||||
{
|
|
||||||
[self setLastColumn:columnIndex];
|
|
||||||
|
|
||||||
if (rowIndex >= 0)
|
|
||||||
[self addColumn];
|
|
||||||
|
|
||||||
[self doClick:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)sendAction
|
|
||||||
{
|
|
||||||
[self sendAction:_action to:_target];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)doClick:(id)sender
|
|
||||||
{
|
|
||||||
[self sendAction:_action to:_target];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)doDoubleClick:(id)sender
|
|
||||||
{
|
|
||||||
[self sendAction:_doubleAction to:_target];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)keyDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
var column = [self selectedColumn];
|
|
||||||
if (column === -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[_tableViews[column] keyDown:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
// SIZING
|
|
||||||
|
|
||||||
- (float)columnContentWidthForColumnWidth:(float)aWidth
|
|
||||||
{
|
|
||||||
return aWidth - (_leafWidth + _delegateSupportsImages ? _imageWidth : 0) - [CPScroller scrollerWidth];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)columnWidthForColumnContentWidth:(float)aWidth
|
|
||||||
{
|
|
||||||
return aWidth + (_leafWidth + _delegateSupportsImages ? _imageWidth : 0) + [CPScroller scrollerWidth];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setImageWidth:(float)aWidth
|
|
||||||
{
|
|
||||||
_imageWidth = aWidth;
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)imageWidth
|
|
||||||
{
|
|
||||||
return _imageWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMinColumnWidth:(float)minWidth
|
|
||||||
{
|
|
||||||
_minColumnWidth = minWidth;
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)minColumnWidth
|
|
||||||
{
|
|
||||||
return _minColumnWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setWidth:(float)aWidth ofColumn:(unsigned)column
|
|
||||||
{
|
|
||||||
_columnWidths[column] = aWidth;
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:didResizeColumn:)])
|
|
||||||
[_delegate browser:self didResizeColumn:column];
|
|
||||||
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)widthOfColumn:(unsigned)column
|
|
||||||
{
|
|
||||||
var width = _columnWidths[column];
|
|
||||||
|
|
||||||
if (width == null)
|
|
||||||
width = _defaultColumnWidth;
|
|
||||||
|
|
||||||
return MAX([CPScroller scrollerWidth], MAX(_minColumnWidth, width));
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setRowHeight:(float)aHeight
|
|
||||||
{
|
|
||||||
_rowHeight = aHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)rowHeight
|
|
||||||
{
|
|
||||||
return _rowHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SCROLLERS
|
|
||||||
|
|
||||||
- (void)scrollColumnToVisible:(unsigned)columnIndex
|
|
||||||
{
|
|
||||||
[_contentView scrollRectToVisible:[[[self tableViewInColumn:columnIndex] enclosingScrollView] frame]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollRowToVisible:(unsigned)rowIndex inColumn:(unsigned)columnIndex
|
|
||||||
{
|
|
||||||
[self scrollColumnToVisible:columnIndex];
|
|
||||||
[[self tableViewInColumn:columnIndex] scrollRowToVisible:rowIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)autohidesScroller
|
|
||||||
{
|
|
||||||
return [_horizontalScrollView autohidesScrollers];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAutohidesScroller:(BOOL)shouldHide
|
|
||||||
{
|
|
||||||
[_horizontalScrollView setAutohidesScrollers:shouldHide];
|
|
||||||
}
|
|
||||||
|
|
||||||
// SELECTION
|
|
||||||
|
|
||||||
- (unsigned)selectedRowInColumn:(unsigned)columnIndex
|
|
||||||
{
|
|
||||||
if (columnIndex > [self lastColumn] || columnIndex < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return [_tableViews[columnIndex] selectedRow];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)selectedColumn
|
|
||||||
{
|
|
||||||
var column = [self lastColumn],
|
|
||||||
row = [self selectedRowInColumn:column];
|
|
||||||
|
|
||||||
if (row >= 0)
|
|
||||||
return column;
|
|
||||||
else
|
|
||||||
return column - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)selectRow:(unsigned)row inColumn:(unsigned)column
|
|
||||||
{
|
|
||||||
var selectedIndexes = row === -1 ? [CPIndexSet indexSet] : [CPIndexSet indexSetWithIndex:row];
|
|
||||||
[self selectRowIndexes:selectedIndexes inColumn:column];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)allowsMultipleSelection
|
|
||||||
{
|
|
||||||
return _allowsMultipleSelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAllowsMultipleSelection:(BOOL)shouldAllow
|
|
||||||
{
|
|
||||||
if (_allowsMultipleSelection === shouldAllow)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_allowsMultipleSelection = shouldAllow;
|
|
||||||
[_tableViews makeObjectsPerformSelector:@selector(setAllowsMultipleSelection:) withObject:shouldAllow];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)allowsEmptySelection
|
|
||||||
{
|
|
||||||
return _allowsEmptySelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAllowsEmptySelection:(BOOL)shouldAllow
|
|
||||||
{
|
|
||||||
if (_allowsEmptySelection === shouldAllow)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_allowsEmptySelection = shouldAllow;
|
|
||||||
[_tableViews makeObjectsPerformSelector:@selector(setAllowsEmptySelection:) withObject:shouldAllow];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPIndexSet)selectedRowIndexesInColumn:(unsigned)column
|
|
||||||
{
|
|
||||||
if (column < 0 || column > [self lastColumn] +1)
|
|
||||||
return [CPIndexSet indexSet];
|
|
||||||
|
|
||||||
return [[self tableViewInColumn:column] selectedRowIndexes];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)selectRowIndexes:(CPIndexSet)indexSet inColumn:(unsigned)column
|
|
||||||
{
|
|
||||||
if (column < 0 || column > [self lastColumn] + 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:selectionIndexesForProposedSelection:inColumn:)])
|
|
||||||
indexSet = [_delegate browser:self selectionIndexesForProposedSelection:indexSet inColumn:column];
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:shouldSelectRowIndexes:inColumn:)] &&
|
|
||||||
![_delegate browser:self shouldSelectRowIndexes:indexSet inColumn:column])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(browserSelectionIsChanging:)])
|
|
||||||
[_delegate browserSelectionIsChanging:self];
|
|
||||||
|
|
||||||
if (column > [self lastColumn])
|
|
||||||
[self addColumn];
|
|
||||||
|
|
||||||
[self setLastColumn:column];
|
|
||||||
|
|
||||||
[[self tableViewInColumn:column] selectRowIndexes:indexSet byExtendingSelection:NO];
|
|
||||||
|
|
||||||
[self scrollColumnToVisible:column];
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(browserSelectionDidChange:)])
|
|
||||||
[_delegate browserSelectionDidChange:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setBackgroundColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
[super setBackgroundColor:aColor];
|
|
||||||
[_contentView setBackgroundColor:aColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)acceptsFirstResponder
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DRAG AND DROP
|
|
||||||
|
|
||||||
- (void)registerForDraggedTypes:(CPArray)types
|
|
||||||
{
|
|
||||||
[super registerForDraggedTypes:types];
|
|
||||||
[_tableViews makeObjectsPerformSelector:@selector(registerForDraggedTypes:) withObject:types];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent
|
|
||||||
{
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:canDragRowsWithIndexes:inColumn:withEvent:)])
|
|
||||||
return [_delegate browser:self canDragRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPImage)draggingImageForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
|
|
||||||
{
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:draggingImageForRowsWithIndexes:inColumn:withEvent:offset:)])
|
|
||||||
return [_delegate browser:self draggingImageForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset];
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)draggingViewForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
|
|
||||||
{
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:draggingViewForRowsWithIndexes:inColumn:withEvent:offset:)])
|
|
||||||
return [_delegate browser:self draggingViewForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset];
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var _CPBrowserResizeControlBackgroundImage = nil;
|
|
||||||
|
|
||||||
@implementation _CPBrowserResizeControl : CPView
|
|
||||||
{
|
|
||||||
CGPoint _mouseDownX;
|
|
||||||
CPBrowser _browser;
|
|
||||||
unsigned _index;
|
|
||||||
unsigned _width;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPImage)backgroundImage
|
|
||||||
{
|
|
||||||
if (!_CPBrowserResizeControlBackgroundImage)
|
|
||||||
{
|
|
||||||
var path = [[CPBundle bundleForClass:[self class]] pathForResource:"browser-resize-control.png"];
|
|
||||||
_CPBrowserResizeControlBackgroundImage = [[CPImage alloc] initWithContentsOfFile:path
|
|
||||||
size:CGSizeMake(15, 14)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return _CPBrowserResizeControlBackgroundImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
[self setBackgroundColor:[CPColor colorWithPatternImage:[[self class] backgroundImage]]];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
_mouseDownX = [anEvent locationInWindow].x;
|
|
||||||
_browser = [[self superview] _browser];
|
|
||||||
_index = [_browser columnOfTableView:[[self superview] documentView]];
|
|
||||||
_width = [_browser widthOfColumn:_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDragged:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
var deltaX = [anEvent locationInWindow].x - _mouseDownX;
|
|
||||||
[_browser setWidth:_width + deltaX ofColumn:_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseUp:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPBrowserScrollView : CPScrollView
|
|
||||||
{
|
|
||||||
_CPBrowserResizeControl _resizeControl;
|
|
||||||
CPBrowser _browser @accessors;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
{
|
|
||||||
_resizeControl = [[_CPBrowserResizeControl alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[self addSubview:_resizeControl];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reflectScrolledClipView:(CPClipView)aClipView
|
|
||||||
{
|
|
||||||
[super reflectScrolledClipView:aClipView];
|
|
||||||
|
|
||||||
var frame = [_verticalScroller frame];
|
|
||||||
frame.size.height = CGRectGetHeight([self bounds]) - 14.0 - frame.origin.y;
|
|
||||||
[_verticalScroller setFrameSize:frame.size];
|
|
||||||
|
|
||||||
var resizeFrame = CGRectMake(CGRectGetMinX(frame), CGRectGetMaxY(frame), [CPScroller scrollerWidth], 14.0);
|
|
||||||
[_resizeControl setFrame:resizeFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPBrowserTableView : CPTableView
|
|
||||||
{
|
|
||||||
CPBrowser _browser;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame browser:(CPBrowser)aBrowser
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
_browser = aBrowser;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)acceptsFirstResponder
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
[super mouseDown:anEvent];
|
|
||||||
[[self window] makeFirstResponder:_browser];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)browserView
|
|
||||||
{
|
|
||||||
return _browser;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint
|
|
||||||
{
|
|
||||||
return [_browser canDragRowsWithIndexes:rowIndexes inColumn:[_browser columnOfTableView:self] withEvent:[CPApp currentEvent]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPointPointer)dragImageOffset
|
|
||||||
{
|
|
||||||
return [_browser draggingImageForRowsWithIndexes:dragRows inColumn:[_browser columnOfTableView:self] withEvent:dragEvent offset:dragImageOffset] ||
|
|
||||||
[super dragImageForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragImageOffset];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)dragViewForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPoint)dragViewOffset
|
|
||||||
{
|
|
||||||
var count = theTableColumns.length;
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
if ([theTableColumns[count] identifier] === "Leaf")
|
|
||||||
[theTableColumns removeObject:theTableColumns[count]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [_browser draggingViewForRowsWithIndexes:dragRows inColumn:[_browser columnOfTableView:self] withEvent:dragEvent offset:dragViewOffset] ||
|
|
||||||
[super dragViewForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragViewOffset];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveUp:(id)sender
|
|
||||||
{
|
|
||||||
[super moveUp:sender];
|
|
||||||
[_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveDown:(id)sender
|
|
||||||
{
|
|
||||||
[super moveDown:sender];
|
|
||||||
[_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveLeft:(id)sender
|
|
||||||
{
|
|
||||||
var previousColumn = [_browser selectedColumn] - 1,
|
|
||||||
selectedRow = [_browser selectedRowInColumn:previousColumn];
|
|
||||||
|
|
||||||
[_browser selectRow:selectedRow inColumn:previousColumn];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveRight:(id)sender
|
|
||||||
{
|
|
||||||
[_browser selectRow:0 inColumn:[_browser selectedColumn] + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
|
|
||||||
@implementation _CPBrowserTableDelegate : CPObject
|
|
||||||
{
|
|
||||||
CPBrowser _browser @accessors;
|
|
||||||
unsigned _index @accessors;
|
|
||||||
id _delegate @accessors;
|
|
||||||
id _item @accessors;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)numberOfRowsInTableView:(CPTableView)aTableView
|
|
||||||
{
|
|
||||||
return [_delegate browser:_browser numberOfChildrenOfItem:_item];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)column row:(unsigned)row
|
|
||||||
{
|
|
||||||
if ([column identifier] === "Image")
|
|
||||||
return [_delegate browser:_browser imageValueForItem:[self childAtIndex:row]];
|
|
||||||
else if ([column identifier] === "Leaf")
|
|
||||||
return ![_browser isLeafItem:[self childAtIndex:row]];
|
|
||||||
else
|
|
||||||
return [_delegate browser:_browser objectValueForItem:[self childAtIndex:row]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_tableViewDoubleClicked:(CPTableView)aTableView
|
|
||||||
{
|
|
||||||
[_browser doDoubleClick:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_tableViewClicked:(CPTableView)aTableView
|
|
||||||
{
|
|
||||||
var selectedIndexes = [aTableView selectedRowIndexes];
|
|
||||||
[_browser _column:_index clickedRow:[selectedIndexes count] === 1 ? [selectedIndexes firstIndex] : -1];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)childAtIndex:(unsigned)index
|
|
||||||
{
|
|
||||||
return [_delegate browser:_browser child:index ofItem:_item];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tableView:(CPTableView)aTableView acceptDrop:(id)info row:(int)row dropOperation:(CPTableViewDropOperation)operation
|
|
||||||
{
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:acceptDrop:atRow:column:dropOperation:)])
|
|
||||||
return [_delegate browser:_browser acceptDrop:info atRow:row column:_index dropOperation:operation];
|
|
||||||
else
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPDragOperation)tableView:(CPTableView)aTableView validateDrop:(id)info proposedRow:(int)row proposedDropOperation:(CPTableViewDropOperation)operation
|
|
||||||
{
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:validateDrop:proposedRow:column:dropOperation:)])
|
|
||||||
return [_delegate browser:_browser validateDrop:info proposedRow:row column:_index dropOperation:operation];
|
|
||||||
else
|
|
||||||
return CPDragOperationNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tableView:(CPTableView)aTableView writeRowsWithIndexes:(CPIndexSet)rowIndexes toPasteboard:(CPPasteboard)pboard
|
|
||||||
{
|
|
||||||
if ([_delegate respondsToSelector:@selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:)])
|
|
||||||
return [_delegate browser:_browser writeRowsWithIndexes:rowIndexes inColumn:_index toPasteboard:pboard];
|
|
||||||
else
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)respondsToSelector:(SEL)aSelector
|
|
||||||
{
|
|
||||||
if (aSelector === @selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:))
|
|
||||||
return [_delegate respondsToSelector:@selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:)];
|
|
||||||
else
|
|
||||||
return [super respondsToSelector:aSelector];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPBrowserLeafView : CPView
|
|
||||||
{
|
|
||||||
BOOL _isLeaf @accessors(readonly, property=isLeaf);
|
|
||||||
CPImage _branchImage @accessors(property=branchImage);
|
|
||||||
CPImage _highlightedBranchImage @accessors(property=highlightedBranchImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)objectValue
|
|
||||||
{
|
|
||||||
return _isLeaf;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setObjectValue:(id)aValue
|
|
||||||
{
|
|
||||||
_isLeaf = !!aValue;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
|
||||||
{
|
|
||||||
if (aName === "image-view")
|
|
||||||
return CGRectInset([self bounds], 1, 1);
|
|
||||||
|
|
||||||
return [super rectForEphemeralSubviewNamed:aName];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
|
||||||
{
|
|
||||||
if (aName === "image-view")
|
|
||||||
return [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
return [super createEphemeralSubviewNamed:aName];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
var imageView = [self layoutEphemeralSubviewNamed:@"image-view"
|
|
||||||
positioned:CPWindowAbove
|
|
||||||
relativeToEphemeralSubviewNamed:nil],
|
|
||||||
isHighlighted = [self themeState] & CPThemeStateSelectedDataView;
|
|
||||||
|
|
||||||
[imageView setImage: _isLeaf ? (isHighlighted ? _highlightedBranchImage : _branchImage) : nil];
|
|
||||||
[imageView setImageScaling:CPScaleNone];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeBool:_isLeaf forKey:"_CPBrowserLeafViewIsLeafKey"];
|
|
||||||
[aCoder encodeObject:_branchImage forKey:"_CPBrowserLeafViewBranchImageKey"];
|
|
||||||
[aCoder encodeObject:_highlightedBranchImage forKey:"_CPBrowserLeafViewHighlightedBranchImageKey"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if (self = [super initWithCoder:aCoder])
|
|
||||||
{
|
|
||||||
_isLeaf = [aCoder decodeBoolForKey:"_CPBrowserLeafViewIsLeafKey"];
|
|
||||||
_branchImage = [aCoder decodeObjectForKey:"_CPBrowserLeafViewBranchImageKey"];
|
|
||||||
_highlightedBranchImage = [aCoder decodeObjectForKey:"_CPBrowserLeafViewHighlightedBranchImageKey"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+103
-184
@@ -24,8 +24,31 @@
|
|||||||
@import "CGGeometry.j"
|
@import "CGGeometry.j"
|
||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPStringDrawing.j"
|
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
|
||||||
|
CPScaleProportionally = 0;
|
||||||
|
CPScaleToFit = 1;
|
||||||
|
CPScaleNone = 2;
|
||||||
|
|
||||||
|
|
||||||
|
/* @group CPCellImagePosition */
|
||||||
|
|
||||||
|
CPNoImage = 0;
|
||||||
|
CPImageOnly = 1;
|
||||||
|
CPImageLeft = 2;
|
||||||
|
CPImageRight = 3;
|
||||||
|
CPImageBelow = 4;
|
||||||
|
CPImageAbove = 5;
|
||||||
|
CPImageOverlaps = 6;
|
||||||
|
|
||||||
|
|
||||||
|
/* @group CPButtonState */
|
||||||
|
|
||||||
|
CPOnState = 1;
|
||||||
|
CPOffState = 0;
|
||||||
|
CPMixedState = -1;
|
||||||
|
|
||||||
/* @group CPBezelStyle */
|
/* @group CPBezelStyle */
|
||||||
|
|
||||||
@@ -72,10 +95,7 @@ CPChangeBackgroundCellMask = CPBackgroundButtonMask;
|
|||||||
|
|
||||||
CPButtonStateMixed = CPThemeState("mixed");
|
CPButtonStateMixed = CPThemeState("mixed");
|
||||||
|
|
||||||
CPButtonDefaultHeight = 24.0;
|
/*!
|
||||||
CPButtonImageOffset = 3.0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPButton
|
@class CPButton
|
||||||
|
|
||||||
@@ -86,9 +106,12 @@ CPButtonImageOffset = 3.0;
|
|||||||
@implementation CPButton : CPControl
|
@implementation CPButton : CPControl
|
||||||
{
|
{
|
||||||
BOOL _allowsMixedState;
|
BOOL _allowsMixedState;
|
||||||
|
|
||||||
CPString _title;
|
CPString _title;
|
||||||
CPString _alternateTitle;
|
CPString _alternateTitle;
|
||||||
|
|
||||||
|
CPImage _image;
|
||||||
|
CPImage _alternateImage;
|
||||||
|
|
||||||
CPInteger _showsStateBy;
|
CPInteger _showsStateBy;
|
||||||
CPInteger _highlightsBy;
|
CPInteger _highlightsBy;
|
||||||
@@ -97,9 +120,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
// NS-style Display Properties
|
// NS-style Display Properties
|
||||||
CPBezelStyle _bezelStyle;
|
CPBezelStyle _bezelStyle;
|
||||||
CPControlSize _controlSize;
|
CPControlSize _controlSize;
|
||||||
|
|
||||||
CPString _keyEquivalent;
|
|
||||||
unsigned _keyEquivalentModifierMask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)buttonWithTitle:(CPString)aTitle
|
+ (id)buttonWithTitle:(CPString)aTitle
|
||||||
@@ -118,21 +138,21 @@ CPButtonImageOffset = 3.0;
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)themeClass
|
||||||
{
|
{
|
||||||
return @"button";
|
return @"button";
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], 0.0, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null]]
|
return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMakeZero(), nil]
|
||||||
forKeys:[@"image", @"image-offset", @"bezel-inset", @"content-inset", @"bezel-color"]];
|
forKeys:[@"bezel-inset", @"content-inset", @"bezel-color"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
// Should we instead override the defaults?
|
// Should we instead override the defaults?
|
||||||
@@ -140,22 +160,19 @@ CPButtonImageOffset = 3.0;
|
|||||||
[self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
|
[self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
|
||||||
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
||||||
[self setValue:CPScaleNone forThemeAttribute:@"image-scaling"];
|
[self setValue:CPScaleNone forThemeAttribute:@"image-scaling"];
|
||||||
|
|
||||||
_controlSize = CPRegularControlSize;
|
_controlSize = CPRegularControlSize;
|
||||||
|
|
||||||
_keyEquivalent = @"";
|
|
||||||
_keyEquivalentModifierMask = 0;
|
|
||||||
|
|
||||||
// [self setBezelStyle:CPRoundRectBezelStyle];
|
// [self setBezelStyle:CPRoundRectBezelStyle];
|
||||||
[self setBordered:YES];
|
[self setBordered:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting the state
|
// Setting the state
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the button has a 'mixed' state in addition to on and off.
|
Returns <code>YES</code> if the button has a 'mixed' state in addition to on and off.
|
||||||
*/
|
*/
|
||||||
- (BOOL)allowsMixedState
|
- (BOOL)allowsMixedState
|
||||||
{
|
{
|
||||||
@@ -175,8 +192,8 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
_allowsMixedState = aFlag;
|
_allowsMixedState = aFlag;
|
||||||
|
|
||||||
if (!_allowsMixedState && [self state] === CPMixedState)
|
if (!_allowsMixedState)
|
||||||
[self setState:CPOnState];
|
[self unsetThemeState:CPButtonStateMixed];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setObjectValue:(id)anObjectValue
|
- (void)setObjectValue:(id)anObjectValue
|
||||||
@@ -231,9 +248,9 @@ CPButtonImageOffset = 3.0;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the button's state to \c aState.
|
Sets the button's state to <code>aState</code>.
|
||||||
@param aState Possible states are any of the CPButton globals:
|
@param aState Possible states are any of the CPButton globals:
|
||||||
\c CPOffState, \c CPOnState, \c CPMixedState
|
<code>CPOffState, CPOnState, CPMixedState</code>
|
||||||
*/
|
*/
|
||||||
- (void)setState:(CPInteger)aState
|
- (void)setState:(CPInteger)aState
|
||||||
{
|
{
|
||||||
@@ -252,9 +269,9 @@ CPButtonImageOffset = 3.0;
|
|||||||
{
|
{
|
||||||
if (_title === aTitle)
|
if (_title === aTitle)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_title = aTitle;
|
_title = aTitle;
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
@@ -268,7 +285,7 @@ CPButtonImageOffset = 3.0;
|
|||||||
{
|
{
|
||||||
if (_alternateTitle === aTitle)
|
if (_alternateTitle === aTitle)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_alternateTitle = aTitle;
|
_alternateTitle = aTitle;
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
@@ -282,12 +299,18 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
- (void)setImage:(CPImage)anImage
|
- (void)setImage:(CPImage)anImage
|
||||||
{
|
{
|
||||||
[self setValue:anImage forThemeAttribute:@"image"];
|
if (_image === anImage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_image = anImage;
|
||||||
|
|
||||||
|
[self setNeedsLayout];
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPImage)image
|
- (CPImage)image
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"image" inState:CPThemeStateNormal];
|
return _image;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -296,7 +319,13 @@ CPButtonImageOffset = 3.0;
|
|||||||
*/
|
*/
|
||||||
- (void)setAlternateImage:(CPImage)anImage
|
- (void)setAlternateImage:(CPImage)anImage
|
||||||
{
|
{
|
||||||
[self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
|
if (_alternateImage === anImage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_alternateImage = anImage;
|
||||||
|
|
||||||
|
[self setNeedsLayout];
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -304,17 +333,7 @@ CPButtonImageOffset = 3.0;
|
|||||||
*/
|
*/
|
||||||
- (CPImage)alternateImage
|
- (CPImage)alternateImage
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"image" inState:CPThemeStateHighlighted];
|
return _alternateImage;
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setImageOffset:(float)theImageOffset
|
|
||||||
{
|
|
||||||
[self setValue:theImageOffset forThemeAttribute:@"image-offset"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)imageOffset
|
|
||||||
{
|
|
||||||
return [self valueForThemeAttribute:@"image-offset"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setShowsStateBy:(CPInteger)aMask
|
- (void)setShowsStateBy:(CPInteger)aMask
|
||||||
@@ -375,13 +394,13 @@ CPButtonImageOffset = 3.0;
|
|||||||
[self setShowsStateBy:CPContentsCellMask];
|
[self setShowsStateBy:CPContentsCellMask];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPSwitchButton: [CPException raise:CPInvalidArgumentException
|
case CPSwitchButton: [CPException raise:CPInvalidArgumentException
|
||||||
reason:"The CPSwitchButton type is not supported in Cappuccino, use the CPCheckBox class instead."];
|
reason:"The CPSwitchButton type is not supported in Cappuccino, use the CPCheckBox class instead."];
|
||||||
|
|
||||||
case CPRadioButton: [CPException raise:CPInvalidArgumentException
|
case CPRadioButton: [CPException raise:CPInvalidArgumentException
|
||||||
reason:"The CPRadioButton type is not supported in Cappuccino, use the CPRadio class instead."];
|
reason:"The CPRadioButton type is not supported in Cappuccino, use the CPRadio class instead."];
|
||||||
|
|
||||||
default: [CPException raise:CPInvalidArgumentException
|
default: [CPException raise:CPInvalidArgumentException
|
||||||
reason:"Unknown button type."];
|
reason:"Unknown button type."];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +416,7 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
_imageDimsWhenDisabled = imageShouldDimWhenDisabled;
|
_imageDimsWhenDisabled = imageShouldDimWhenDisabled;
|
||||||
|
|
||||||
if ([self hasThemeState:CPThemeStateDisabled])
|
if (_imageDimsWhenDisabled)
|
||||||
{
|
{
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
@@ -412,7 +431,7 @@ CPButtonImageOffset = 3.0;
|
|||||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
[self highlight:YES];
|
[self highlight:YES];
|
||||||
|
|
||||||
return [super startTrackingAt:aPoint];
|
return [super startTrackingAt:aPoint];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,16 +452,15 @@ CPButtonImageOffset = 3.0;
|
|||||||
if (_CGInsetIsEmpty(contentInset))
|
if (_CGInsetIsEmpty(contentInset))
|
||||||
return bounds;
|
return bounds;
|
||||||
|
|
||||||
bounds = _CGRectMakeCopy(bounds);
|
|
||||||
bounds.origin.x += contentInset.left;
|
bounds.origin.x += contentInset.left;
|
||||||
bounds.origin.y += contentInset.top;
|
bounds.origin.y += contentInset.top;
|
||||||
bounds.size.width -= contentInset.left + contentInset.right;
|
bounds.size.width -= contentInset.left + contentInset.right;
|
||||||
bounds.size.height -= contentInset.top + contentInset.bottom;
|
bounds.size.height -= contentInset.top + contentInset.bottom;
|
||||||
|
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)bezelRectForBounds:(CGRect)bounds
|
- (CGRect)bezelRectForBounds:(CFRect)bounds
|
||||||
{
|
{
|
||||||
if (![self isBordered])
|
if (![self isBordered])
|
||||||
return _CGRectMakeZero();
|
return _CGRectMakeZero();
|
||||||
@@ -452,7 +470,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
if (_CGInsetIsEmpty(bezelInset))
|
if (_CGInsetIsEmpty(bezelInset))
|
||||||
return bounds;
|
return bounds;
|
||||||
|
|
||||||
bounds = _CGRectMakeCopy(bounds);
|
|
||||||
bounds.origin.x += bezelInset.left;
|
bounds.origin.x += bezelInset.left;
|
||||||
bounds.origin.y += bezelInset.top;
|
bounds.origin.y += bezelInset.top;
|
||||||
bounds.size.width -= bezelInset.left + bezelInset.right;
|
bounds.size.width -= bezelInset.left + bezelInset.right;
|
||||||
@@ -466,20 +483,8 @@ CPButtonImageOffset = 3.0;
|
|||||||
*/
|
*/
|
||||||
- (void)sizeToFit
|
- (void)sizeToFit
|
||||||
{
|
{
|
||||||
[self layoutSubviews];
|
var size = [([self title] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]],
|
||||||
|
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||||
var size,
|
|
||||||
contentView = [self ephemeralSubviewNamed:@"content-view"];
|
|
||||||
|
|
||||||
if (contentView)
|
|
||||||
{
|
|
||||||
[contentView sizeToFit];
|
|
||||||
size = [contentView frameSize];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
size = [([self title] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]];
|
|
||||||
|
|
||||||
var contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
minSize = [self currentValueForThemeAttribute:@"min-size"],
|
minSize = [self currentValueForThemeAttribute:@"min-size"],
|
||||||
maxSize = [self currentValueForThemeAttribute:@"max-size"];
|
maxSize = [self currentValueForThemeAttribute:@"max-size"];
|
||||||
|
|
||||||
@@ -499,10 +504,10 @@ CPButtonImageOffset = 3.0;
|
|||||||
{
|
{
|
||||||
if (aName === "bezel-view")
|
if (aName === "bezel-view")
|
||||||
return [self bezelRectForBounds:[self bounds]];
|
return [self bezelRectForBounds:[self bounds]];
|
||||||
|
|
||||||
else if (aName === "content-view")
|
else if (aName === "content-view")
|
||||||
return [self contentRectForBounds:[self bounds]];
|
return [self contentRectForBounds:[self bounds]];
|
||||||
|
|
||||||
return [super rectForEphemeralSubviewNamed:aName];
|
return [super rectForEphemeralSubviewNamed:aName];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,11 +518,13 @@ CPButtonImageOffset = 3.0;
|
|||||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
[view setHitTests:NO];
|
[view setHitTests:NO];
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
|
return [super createEphemeralSubviewNamed:aName];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
@@ -525,8 +532,9 @@ CPButtonImageOffset = 3.0;
|
|||||||
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
||||||
positioned:CPWindowBelow
|
positioned:CPWindowBelow
|
||||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
relativeToEphemeralSubviewNamed:@"content-view"];
|
||||||
|
|
||||||
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
if (bezelView)
|
||||||
|
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||||
|
|
||||||
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
||||||
positioned:CPWindowAbove
|
positioned:CPWindowAbove
|
||||||
@@ -535,8 +543,7 @@ CPButtonImageOffset = 3.0;
|
|||||||
if (contentView)
|
if (contentView)
|
||||||
{
|
{
|
||||||
[contentView setText:([self hasThemeState:CPThemeStateHighlighted] && _alternateTitle) ? _alternateTitle : _title];
|
[contentView setText:([self hasThemeState:CPThemeStateHighlighted] && _alternateTitle) ? _alternateTitle : _title];
|
||||||
[contentView setImage:[self currentValueForThemeAttribute:@"image"]];
|
[contentView setImage:([self hasThemeState:CPThemeStateHighlighted] && _alternateImage) ? _alternateImage : _image];
|
||||||
[contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]];
|
|
||||||
|
|
||||||
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
|
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
|
||||||
[contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
[contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
||||||
@@ -547,10 +554,17 @@ CPButtonImageOffset = 3.0;
|
|||||||
[contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
|
[contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
|
||||||
[contentView setImagePosition:[self currentValueForThemeAttribute:@"image-position"]];
|
[contentView setImagePosition:[self currentValueForThemeAttribute:@"image-position"]];
|
||||||
[contentView setImageScaling:[self currentValueForThemeAttribute:@"image-scaling"]];
|
[contentView setImageScaling:[self currentValueForThemeAttribute:@"image-scaling"]];
|
||||||
[contentView setDimsImage:[self hasThemeState:CPThemeStateDisabled] && _imageDimsWhenDisabled];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setDefaultButton:(BOOL)shouldBeDefaultButton
|
||||||
|
{
|
||||||
|
if (shouldBeDefaultButton)
|
||||||
|
[self setThemeState:CPThemeStateDefault];
|
||||||
|
else
|
||||||
|
[self unsetThemeState:CPThemeStateDefault];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setBordered:(BOOL)shouldBeBordered
|
- (void)setBordered:(BOOL)shouldBeBordered
|
||||||
{
|
{
|
||||||
if (shouldBeBordered)
|
if (shouldBeBordered)
|
||||||
@@ -564,79 +578,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
return [self hasThemeState:CPThemeStateBordered];
|
return [self hasThemeState:CPThemeStateBordered];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the keyboard shortcut for this button. For special keys see
|
|
||||||
CPEvent.j CP...FunctionKey and CPText.j CP...Character.
|
|
||||||
|
|
||||||
@param aString the keyboard shortcut as a string
|
|
||||||
*/
|
|
||||||
- (void)setKeyEquivalent:(CPString)aString
|
|
||||||
{
|
|
||||||
_keyEquivalent = aString || @"";
|
|
||||||
|
|
||||||
// Check if the key equivalent is the enter key
|
|
||||||
// Treat \r and \n as the same key equivalent. See issue #710.
|
|
||||||
if (aString === CPNewlineCharacter || aString === CPCarriageReturnCharacter)
|
|
||||||
[self setThemeState:CPThemeStateDefault];
|
|
||||||
else
|
|
||||||
[self unsetThemeState:CPThemeStateDefault];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillMoveToWindow:(CPWindow)aWindow
|
|
||||||
{
|
|
||||||
var selfWindow = [self window];
|
|
||||||
|
|
||||||
if (selfWindow === aWindow || aWindow === nil)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ([selfWindow defaultButton] === self)
|
|
||||||
[selfWindow setDefaultButton:nil];
|
|
||||||
|
|
||||||
if ([self keyEquivalent] === CPNewlineCharacter || [self keyEquivalent] === CPCarriageReturnCharacter)
|
|
||||||
[aWindow setDefaultButton:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the keyboard shortcut for this button.
|
|
||||||
*/
|
|
||||||
- (CPString)keyEquivalent
|
|
||||||
{
|
|
||||||
return _keyEquivalent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the mask used with this button's key equivalent.
|
|
||||||
*/
|
|
||||||
- (void)setKeyEquivalentModifierMask:(unsigned)aMask
|
|
||||||
{
|
|
||||||
_keyEquivalentModifierMask = aMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the mask to be used with this button's key equivalent.
|
|
||||||
*/
|
|
||||||
- (unsigned)keyEquivalentModifierMask
|
|
||||||
{
|
|
||||||
return _keyEquivalentModifierMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Checks the button's key equivalent against that in the event, and if they
|
|
||||||
match simulates a button click.
|
|
||||||
*/
|
|
||||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
// Don't handle the key equivalent for the default window because the window will handle it for us
|
|
||||||
if ([[self window] defaultButton] === self)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
if (![anEvent _triggersKeyEquivalent:[self keyEquivalent] withModifierMask:[self keyEquivalentModifierMask]])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
[self performClick:nil];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPButton (NS)
|
@implementation CPButton (NS)
|
||||||
@@ -652,51 +593,36 @@ CPButtonImageOffset = 3.0;
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
var CPButtonImageKey = @"CPButtonImageKey",
|
var CPButtonImageKey = @"CPButtonImageKey",
|
||||||
CPButtonAlternateImageKey = @"CPButtonAlternateImageKey",
|
CPButtonAlternateImageKey = @"CPButtonAlternateImageKey",
|
||||||
CPButtonTitleKey = @"CPButtonTitleKey",
|
CPButtonTitleKey = @"CPButtonTitleKey",
|
||||||
CPButtonAlternateTitleKey = @"CPButtonAlternateTitleKey",
|
CPButtonAlternateTitleKey = @"CPButtonAlternateTitleKey",
|
||||||
CPButtonIsBorderedKey = @"CPButtonIsBorderedKey",
|
CPButtonIsBorderedKey = @"CPButtonIsBorderedKey";
|
||||||
CPButtonAllowsMixedStateKey = @"CPButtonAllowsMixedStateKey",
|
|
||||||
CPButtonImageDimsWhenDisabledKey = @"CPButtonImageDimsWhenDisabledKey",
|
|
||||||
CPButtonImagePositionKey = @"CPButtonImagePositionKey",
|
|
||||||
CPButtonKeyEquivalentKey = @"CPButtonKeyEquivalentKey",
|
|
||||||
CPButtonKeyEquivalentMaskKey = @"CPButtonKeyEquivalentMaskKey";
|
|
||||||
|
|
||||||
@implementation CPButton (CPCoding)
|
@implementation CPButton (CPCoding)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Initializes the button by unarchiving data from \c aCoder.
|
Initializes the button by unarchiving data from <code>aCoder</code>.
|
||||||
@param aCoder the coder containing the archived CPButton.
|
@param aCoder the coder containing the archived CPButton.
|
||||||
*/
|
*/
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super initWithCoder:aCoder];
|
self = [super initWithCoder:aCoder];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_controlSize = CPRegularControlSize;
|
_controlSize = CPRegularControlSize;
|
||||||
|
|
||||||
_title = [aCoder decodeObjectForKey:CPButtonTitleKey];
|
[self setImage:[aCoder decodeObjectForKey:CPButtonImageKey]];
|
||||||
_alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey];
|
[self setAlternateImage:[aCoder decodeObjectForKey:CPButtonAlternateImageKey]];
|
||||||
|
|
||||||
if ([aCoder containsValueForKey:CPButtonAllowsMixedStateKey])
|
[self setTitle:[aCoder decodeObjectForKey:CPButtonTitleKey]];
|
||||||
_allowsMixedState = [aCoder decodeBoolForKey:CPButtonAllowsMixedStateKey];
|
[self setAlternateTitle:[aCoder decodeObjectForKey:CPButtonAlternateTitleKey]];
|
||||||
|
|
||||||
[self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]];
|
|
||||||
|
|
||||||
if ([aCoder containsValueForKey:CPButtonImagePositionKey])
|
|
||||||
[self setImagePosition:[aCoder decodeIntForKey:CPButtonImagePositionKey]];
|
|
||||||
|
|
||||||
if ([aCoder containsValueForKey:CPButtonKeyEquivalentKey])
|
|
||||||
[self setKeyEquivalent:CFData.decodeBase64ToUtf16String([aCoder decodeObjectForKey:CPButtonKeyEquivalentKey])];
|
|
||||||
|
|
||||||
_keyEquivalentModifierMask = [aCoder decodeIntForKey:CPButtonKeyEquivalentMaskKey];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,18 +634,11 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
|||||||
{
|
{
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
|
[aCoder encodeObject:_image forKey:CPButtonImageKey];
|
||||||
|
[aCoder encodeObject:_alternateImage forKey:CPButtonAlternateImageKey];
|
||||||
|
|
||||||
[aCoder encodeObject:_title forKey:CPButtonTitleKey];
|
[aCoder encodeObject:_title forKey:CPButtonTitleKey];
|
||||||
[aCoder encodeObject:_alternateTitle forKey:CPButtonAlternateTitleKey];
|
[aCoder encodeObject:_alternateTitle forKey:CPButtonAlternateTitleKey];
|
||||||
|
|
||||||
[aCoder encodeBool:_allowsMixedState forKey:CPButtonAllowsMixedStateKey];
|
|
||||||
|
|
||||||
[aCoder encodeBool:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey];
|
|
||||||
[aCoder encodeInt:[self imagePosition] forKey:CPButtonImagePositionKey];
|
|
||||||
|
|
||||||
if (_keyEquivalent)
|
|
||||||
[aCoder encodeObject:CFData.encodeBase64Utf16String(_keyEquivalent) forKey:CPButtonKeyEquivalentKey];
|
|
||||||
|
|
||||||
[aCoder encodeInt:_keyEquivalentModifierMask forKey:CPButtonKeyEquivalentMaskKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+28
-244
@@ -1,277 +1,61 @@
|
|||||||
|
|
||||||
@import "CPView.j"
|
@import <AppKit/CPView.j>
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
|
||||||
@implementation CPButtonBar : CPView
|
@implementation CPButtonBar : CPControl
|
||||||
{
|
{
|
||||||
BOOL _hasResizeControl;
|
|
||||||
BOOL _resizeControlIsLeftAligned;
|
|
||||||
CPArray _buttons;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)plusButton
|
+ (CPString)themeClass
|
||||||
{
|
|
||||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
|
||||||
image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"plus_button.png"] size:CGSizeMake(11, 12)];
|
|
||||||
|
|
||||||
[button setBordered:NO];
|
|
||||||
[button setImage:image];
|
|
||||||
[button setImagePosition:CPImageOnly];
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)minusButton
|
|
||||||
{
|
|
||||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
|
||||||
image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"minus_button.png"] size:CGSizeMake(11, 4)];
|
|
||||||
|
|
||||||
[button setBordered:NO];
|
|
||||||
[button setImage:image];
|
|
||||||
[button setImagePosition:CPImageOnly];
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)actionPopupButton
|
|
||||||
{
|
|
||||||
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
|
||||||
image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"action_button.png"] size:CGSizeMake(22, 14)];
|
|
||||||
|
|
||||||
[button addItemWithTitle:nil];
|
|
||||||
[[button lastItem] setImage:image];
|
|
||||||
[button setImagePosition:CPImageOnly];
|
|
||||||
[button setValue:CGInsetMake(0, 0, 0, 0) forThemeAttribute:"content-inset"];
|
|
||||||
|
|
||||||
[button setPullsDown:YES];
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
{
|
||||||
return @"button-bar";
|
return @"button-bar";
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return [CPDictionary dictionaryWithObjects:[CGInsetMake(0.0, 0.0, 0.0, 0.0), CGSizeMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null]]
|
return [CPDictionary dictionaryWithObjects:[nil]
|
||||||
forKeys:[@"resize-control-inset", @"resize-control-size", @"resize-control-color", @"bezel-color", @"button-bezel-color", @"button-text-color"]];
|
forKeys:[@"bezel-color"]];
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_buttons = [];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromCib
|
|
||||||
{
|
|
||||||
var view = [self superview],
|
|
||||||
subview = self;
|
|
||||||
|
|
||||||
while (view)
|
|
||||||
{
|
|
||||||
if ([view isKindOfClass:[CPSplitView class]])
|
|
||||||
{
|
|
||||||
var viewIndex = [[view subviews] indexOfObject:subview];
|
|
||||||
[view setButtonBar:self forDividerAtIndex:viewIndex];
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
subview = view;
|
|
||||||
view = [view superview];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setButtons:(CPArray)buttons
|
|
||||||
{
|
|
||||||
_buttons = [CPArray arrayWithArray:buttons];
|
|
||||||
|
|
||||||
for (var i = 0, count = [_buttons count]; i < count; i++)
|
|
||||||
[_buttons[i] setBordered:YES];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)buttons
|
|
||||||
{
|
|
||||||
return [CPArray arrayWithArray:_buttons];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setHasResizeControl:(BOOL)shouldHaveResizeControl
|
|
||||||
{
|
|
||||||
if (_hasResizeControl === shouldHaveResizeControl)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_hasResizeControl = !!shouldHaveResizeControl;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)hasResizeControl
|
|
||||||
{
|
|
||||||
return _hasResizeControl;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setResizeControlIsLeftAligned:(BOOL)shouldBeLeftAligned
|
|
||||||
{
|
|
||||||
if (_resizeControlIsLeftAligned === shouldBeLeftAligned)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_resizeControlIsLeftAligned = !!shouldBeLeftAligned;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)resizeControlIsLeftAligned
|
|
||||||
{
|
|
||||||
return _resizeControlIsLeftAligned;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)resizeControlFrame
|
|
||||||
{
|
|
||||||
var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
|
|
||||||
size = [self currentValueForThemeAttribute:@"resize-control-size"],
|
|
||||||
currentSize = [self bounds],
|
|
||||||
leftOrigin = _resizeControlIsLeftAligned ? 0 : currentSize.size.width - size.width - inset.right - inset.left;
|
|
||||||
|
|
||||||
return CGRectMake(leftOrigin, 0, size.width + inset.left + inset.right, size.height + inset.top + inset.bottom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||||
{
|
{
|
||||||
if (aName === "resize-control-view")
|
if (aName === "bezel-view")
|
||||||
{
|
return [self bounds];
|
||||||
var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
|
|
||||||
size = [self currentValueForThemeAttribute:@"resize-control-size"],
|
|
||||||
currentSize = [self bounds];
|
|
||||||
|
|
||||||
if (_resizeControlIsLeftAligned)
|
|
||||||
return CGRectMake(inset.left, inset.top, size.width, size.height);
|
|
||||||
else
|
|
||||||
return CGRectMake(currentSize.size.width - size.width - inset.right, inset.top, size.width, size.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [super rectForEphemeralSubviewNamed:aName];
|
return [super rectForEphemeralSubviewNamed:aName];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||||
{
|
{
|
||||||
if (aName === "resize-control-view")
|
if (aName === "bezel-view")
|
||||||
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
{
|
||||||
|
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
|
[view setHitTests:NO];
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
return [super createEphemeralSubviewNamed:aName];
|
return [super createEphemeralSubviewNamed:aName];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
{
|
{
|
||||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
||||||
|
positioned:CPWindowBelow
|
||||||
var normalColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateNormal],
|
relativeToEphemeralSubviewNamed:@""];
|
||||||
highlightedColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateHighlighted],
|
|
||||||
disabledColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateDisabled],
|
if (bezelView)
|
||||||
textColor = [self valueForThemeAttribute:@"button-text-color" inState:CPThemeStateNormal];
|
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||||
|
|
||||||
var buttonsNotHidden = [CPArray arrayWithArray:_buttons],
|
|
||||||
count = [buttonsNotHidden count];
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
if ([buttonsNotHidden[count] isHidden])
|
|
||||||
[buttonsNotHidden removeObject:buttonsNotHidden[count]];
|
|
||||||
|
|
||||||
var currentButtonOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX([self bounds]) + 1 : -1,
|
|
||||||
bounds = [self bounds],
|
|
||||||
height = CGRectGetHeight(bounds) - 1,
|
|
||||||
frameWidth = CGRectGetWidth(bounds),
|
|
||||||
resizeRect = _hasResizeControl ? [self rectForEphemeralSubviewNamed:"resize-control-view"] : CGRectMakeZero(),
|
|
||||||
resizeWidth = CGRectGetWidth(resizeRect),
|
|
||||||
availableWidth = frameWidth - resizeWidth - 1;
|
|
||||||
|
|
||||||
for (var i = 0, count = [buttonsNotHidden count]; i < count; i++)
|
|
||||||
{
|
|
||||||
var button = buttonsNotHidden[i],
|
|
||||||
width = CGRectGetWidth([button frame]);
|
|
||||||
|
|
||||||
if (availableWidth > width)
|
|
||||||
availableWidth -= width;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (_resizeControlIsLeftAligned)
|
|
||||||
{
|
|
||||||
[button setFrame:CGRectMake(currentButtonOffset - width, 1, width, height)];
|
|
||||||
currentButtonOffset -= width - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[button setFrame:CGRectMake(currentButtonOffset, 1, width, height)];
|
|
||||||
currentButtonOffset += width - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal | CPThemeStateBordered];
|
|
||||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted | CPThemeStateBordered];
|
|
||||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled | CPThemeStateBordered];
|
|
||||||
[button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
|
|
||||||
|
|
||||||
// FIXME shouldn't need this
|
|
||||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
|
|
||||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
|
|
||||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
|
|
||||||
|
|
||||||
[self addSubview:button];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_hasResizeControl)
|
|
||||||
{
|
|
||||||
var resizeControlView = [self layoutEphemeralSubviewNamed:@"resize-control-view"
|
|
||||||
positioned:CPWindowAbove
|
|
||||||
relativeToEphemeralSubviewNamed:nil];
|
|
||||||
|
|
||||||
[resizeControlView setAutoresizingMask: _resizeControlIsLeftAligned ? CPViewMaxXMargin : CPViewMinXMargin];
|
|
||||||
[resizeControlView setBackgroundColor:[self currentValueForThemeAttribute:@"resize-control-color"]];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFrameSize:(CGSize)aSize
|
- (void)addSubview:(CPView)aSubview
|
||||||
{
|
{
|
||||||
[super setFrameSize:aSize];
|
[super addSubview:aSubview];
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
[aSubview setAutoresizingMask:CPViewMinXMargin];
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
|
|
||||||
CPButtonBarResizeControlIsLeftAlignedKey = @"CPButtonBarResizeControlIsLeftAlignedKey",
|
|
||||||
CPButtonBarButtonsKey = @"CPButtonBarButtonsKey";
|
|
||||||
|
|
||||||
@implementation CPButtonBar (CPCoding)
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeBool:_hasResizeControl forKey:CPButtonBarHasResizeControlKey];
|
|
||||||
[aCoder encodeBool:_resizeControlIsLeftAligned forKey:CPButtonBarResizeControlIsLeftAlignedKey];
|
|
||||||
[aCoder encodeObject:_buttons forKey:CPButtonBarButtonsKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if (self = [super initWithCoder:aCoder])
|
|
||||||
{
|
|
||||||
_buttons = [aCoder decodeObjectForKey:CPButtonBarButtonsKey] || [];
|
|
||||||
_hasResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasResizeControlKey];
|
|
||||||
_resizeControlIsLeftAligned = [aCoder decodeBoolForKey:CPButtonBarResizeControlIsLeftAlignedKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+2
-27
@@ -23,8 +23,6 @@
|
|||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
|
|
||||||
|
|
||||||
CPCheckBoxImageOffset = 4.0;
|
|
||||||
|
|
||||||
@implementation CPCheckBox : CPButton
|
@implementation CPCheckBox : CPButton
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -39,7 +37,7 @@ CPCheckBoxImageOffset = 4.0;
|
|||||||
return [self buttonWithTitle:aTitle];
|
return [self buttonWithTitle:aTitle];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)themeClass
|
||||||
{
|
{
|
||||||
return @"check-box";
|
return @"check-box";
|
||||||
}
|
}
|
||||||
@@ -57,33 +55,10 @@ CPCheckBoxImageOffset = 4.0;
|
|||||||
[self setImagePosition:CPImageLeft];
|
[self setImagePosition:CPImageLeft];
|
||||||
[self setAlignment:CPLeftTextAlignment];
|
[self setAlignment:CPLeftTextAlignment];
|
||||||
|
|
||||||
[self setBordered:NO];
|
[self setBordered:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)takeStateFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
var count = objects.length,
|
|
||||||
value = [objects[0] valueForKeyPath:aKeyPath] ? CPOnState : CPOffState;
|
|
||||||
|
|
||||||
[self setAllowsMixedState:NO];
|
|
||||||
[self setState:value];
|
|
||||||
|
|
||||||
while (count-- > 1)
|
|
||||||
{
|
|
||||||
if (value !== ([objects[count] valueForKeyPath:aKeyPath] ? CPOnState : CPOffState))
|
|
||||||
{
|
|
||||||
[self setAllowsMixedState:YES];
|
|
||||||
[self setState:CPMixedState];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
[self takeStateFromKeyPath:aKeyPath ofObjects:objects];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+40
-68
@@ -22,12 +22,14 @@
|
|||||||
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
/*!
|
|
||||||
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPClipView
|
@class CPClipView
|
||||||
|
|
||||||
CPClipView allows you to define a clip rect and display only that portion of its containing view.
|
CPClipView allows you to define a clip rect and display only that portion of its containing view.
|
||||||
It is used to hold the document view in a CPScrollView.
|
It is used to hold the document view in a CPScrollView.
|
||||||
*/
|
*/
|
||||||
@implementation CPClipView : CPView
|
@implementation CPClipView : CPView
|
||||||
@@ -36,8 +38,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the document view to be \c aView.
|
Sets the document view to be <code>aView</code>.
|
||||||
@param aView the new document view. It's frame origin will be changed to \c (0,0) after calling this method.
|
@param aView the new document view. It's frame origin will be changed to <code>(0,0)</code> after calling this method.
|
||||||
*/
|
*/
|
||||||
- (void)setDocumentView:(CPView)aView
|
- (void)setDocumentView:(CPView)aView
|
||||||
{
|
{
|
||||||
@@ -45,7 +47,7 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||||
|
|
||||||
if (_documentView)
|
if (_documentView)
|
||||||
{
|
{
|
||||||
[defaultCenter
|
[defaultCenter
|
||||||
@@ -57,29 +59,29 @@
|
|||||||
removeObserver:self
|
removeObserver:self
|
||||||
name:CPViewBoundsDidChangeNotification
|
name:CPViewBoundsDidChangeNotification
|
||||||
object:_documentView];
|
object:_documentView];
|
||||||
|
|
||||||
[_documentView removeFromSuperview];
|
[_documentView removeFromSuperview];
|
||||||
}
|
}
|
||||||
|
|
||||||
_documentView = aView;
|
_documentView = aView;
|
||||||
|
|
||||||
if (_documentView)
|
if (_documentView)
|
||||||
{
|
{
|
||||||
[self addSubview:_documentView];
|
[self addSubview:_documentView];
|
||||||
|
|
||||||
|
[_documentView setPostsFrameChangedNotifications:YES];
|
||||||
|
[_documentView setPostsBoundsChangedNotifications:YES];
|
||||||
|
|
||||||
[_documentView setPostsFrameChangedNotifications:YES];
|
[defaultCenter
|
||||||
[_documentView setPostsBoundsChangedNotifications:YES];
|
|
||||||
|
|
||||||
[defaultCenter
|
|
||||||
addObserver:self
|
addObserver:self
|
||||||
selector:@selector(viewFrameChanged:)
|
selector:@selector(viewFrameChanged:)
|
||||||
name:CPViewFrameDidChangeNotification
|
name:CPViewFrameDidChangeNotification
|
||||||
object:_documentView];
|
object:_documentView];
|
||||||
|
|
||||||
[defaultCenter
|
[defaultCenter
|
||||||
addObserver:self
|
addObserver:self
|
||||||
selector:@selector(viewBoundsChanged:)
|
selector:@selector(viewBoundsChanged:)
|
||||||
name:CPViewBoundsDidChangeNotification
|
name:CPViewBoundsDidChangeNotification
|
||||||
object:_documentView];
|
object:_documentView];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +95,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a new point that may be adjusted from \c aPoint
|
Returns a new point that may be adjusted from <code>aPoint</code>
|
||||||
to make sure it lies within the document view.
|
to make sure it lies within the document view.
|
||||||
@param aPoint
|
@param aPoint
|
||||||
@return the adjusted point
|
@return the adjusted point
|
||||||
@@ -104,7 +106,7 @@
|
|||||||
return _CGPointMakeZero();
|
return _CGPointMakeZero();
|
||||||
|
|
||||||
var documentFrame = [_documentView frame];
|
var documentFrame = [_documentView frame];
|
||||||
|
|
||||||
aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(_bounds), 0.0)));
|
aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(_bounds), 0.0)));
|
||||||
aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(_CGRectGetHeight(documentFrame) - _CGRectGetHeight(_bounds), 0.0)));
|
aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(_CGRectGetHeight(documentFrame) - _CGRectGetHeight(_bounds), 0.0)));
|
||||||
|
|
||||||
@@ -115,22 +117,18 @@
|
|||||||
{
|
{
|
||||||
if (_CGPointEqualToPoint(_bounds.origin, aPoint))
|
if (_CGPointEqualToPoint(_bounds.origin, aPoint))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[super setBoundsOrigin:aPoint];
|
[super setBoundsOrigin:aPoint];
|
||||||
|
|
||||||
var superview = [self superview],
|
var superview = [self superview];
|
||||||
|
|
||||||
// This is hack to avoid having to import CPScrollView.
|
if([superview isKindOfClass:[CPScrollView class]])
|
||||||
// FIXME: Should CPScrollView be finding out about this on its own somehow?
|
|
||||||
scrollViewClass = objj_getClass("CPScrollView");
|
|
||||||
|
|
||||||
if ([superview isKindOfClass:scrollViewClass])
|
|
||||||
[superview reflectScrolledClipView:self];
|
[superview reflectScrolledClipView:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Scrolls the clip view to the specified point. The method
|
Scrolls the clip view to the specified point. The method
|
||||||
sets its bounds origin to \c aPoint.
|
sets its bounds origin to <code>aPoint</code>.
|
||||||
*/
|
*/
|
||||||
- (void)scrollToPoint:(CGPoint)aPoint
|
- (void)scrollToPoint:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
@@ -164,63 +162,37 @@
|
|||||||
- (void)_constrainScrollPoint
|
- (void)_constrainScrollPoint
|
||||||
{
|
{
|
||||||
var oldScrollPoint = [self bounds].origin;
|
var oldScrollPoint = [self bounds].origin;
|
||||||
|
|
||||||
// Call scrollToPoint: because the current scroll point may no longer make
|
// Call scrollToPoint: because the current scroll point may no longer make
|
||||||
// sense given the new frame of the document view.
|
// sense given the new frame of the document view.
|
||||||
[self scrollToPoint:oldScrollPoint];
|
[self scrollToPoint:oldScrollPoint];
|
||||||
|
|
||||||
// scrollToPoint: takes care of reflectScrollClipView: for us, so bail if
|
// scrollToPoint: takes care of reflectScrollClipView: for us, so bail if
|
||||||
// the scroll points are not equal (meaning scrollToPoint: didn't early bail).
|
// the scroll points are not equal (meaning scrollToPoint: didn't early bail).
|
||||||
if (!CGPointEqualToPoint(oldScrollPoint, [self bounds].origin))
|
if (!CGPointEqualToPoint(oldScrollPoint, [self bounds].origin))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// ... and we're in a scroll view of course.
|
// ... and we're in a scroll view of course.
|
||||||
var superview = [self superview],
|
var superview = [self superview];
|
||||||
|
|
||||||
// This is hack to avoid having to import CPScrollView.
|
if ([superview isKindOfClass:[CPScrollView class]])
|
||||||
// FIXME: Should CPScrollView be finding out about this on its own somehow?
|
|
||||||
scrollViewClass = objj_getClass("CPScrollView");
|
|
||||||
|
|
||||||
if ([superview isKindOfClass:scrollViewClass])
|
|
||||||
[superview reflectScrolledClipView:self];
|
[superview reflectScrolledClipView:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)autoscroll:(CPEvent)anEvent
|
- (BOOL)autoscroll:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var bounds = [self bounds],
|
var bounds = [self bounds],
|
||||||
eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
superview = [self superview],
|
|
||||||
deltaX = 0,
|
|
||||||
deltaY = 0;
|
|
||||||
|
|
||||||
if (CGRectContainsPoint(bounds, eventLocation))
|
if (CPRectContainsPoint(bounds, eventLocation))
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (![superview isKindOfClass:[CPScrollView class]] || [superview hasVerticalScroller])
|
var newRect = CGRectMakeZero();
|
||||||
{
|
|
||||||
if (eventLocation.y < CGRectGetMinY(bounds))
|
|
||||||
deltaY = CGRectGetMinY(bounds) - eventLocation.y;
|
|
||||||
else if (eventLocation.y > CGRectGetMaxY(bounds))
|
|
||||||
deltaY = CGRectGetMaxY(bounds) - eventLocation.y;
|
|
||||||
if (deltaY < -bounds.size.height)
|
|
||||||
deltaY = -bounds.size.height;
|
|
||||||
if (deltaY > bounds.size.height)
|
|
||||||
deltaY = bounds.size.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (![superview isKindOfClass:[CPScrollView class]] || [superview hasHorizontalScroller])
|
newRect.origin = eventLocation;
|
||||||
{
|
newRect.size = CPSizeMake(10, 10);
|
||||||
if (eventLocation.x < CGRectGetMinX(bounds))
|
|
||||||
deltaX = CGRectGetMinX(bounds) - eventLocation.x;
|
|
||||||
else if (eventLocation.x > CGRectGetMaxX(bounds))
|
|
||||||
deltaX = CGRectGetMaxX(bounds) - eventLocation.x;
|
|
||||||
if (deltaX < -bounds.size.width)
|
|
||||||
deltaX = -bounds.size.width;
|
|
||||||
if (deltaX > bounds.size.width)
|
|
||||||
deltaX = bounds.size.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [self scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
|
return [_documentView scrollRectToVisible:newRect];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -234,14 +206,14 @@ var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
|
|||||||
{
|
{
|
||||||
if (self = [super initWithCoder:aCoder])
|
if (self = [super initWithCoder:aCoder])
|
||||||
[self setDocumentView:[aCoder decodeObjectForKey:CPClipViewDocumentViewKey]];
|
[self setDocumentView:[aCoder decodeObjectForKey:CPClipViewDocumentViewKey]];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
[aCoder encodeObject:_documentView forKey:CPClipViewDocumentViewKey];
|
[aCoder encodeObject:_documentView forKey:CPClipViewDocumentViewKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+236
-389
@@ -26,20 +26,19 @@
|
|||||||
@import <Foundation/CPKeyedArchiver.j>
|
@import <Foundation/CPKeyedArchiver.j>
|
||||||
@import <Foundation/CPKeyedUnarchiver.j>
|
@import <Foundation/CPKeyedUnarchiver.j>
|
||||||
|
|
||||||
@import "CPView.j"
|
@import <AppKit/CPView.j>
|
||||||
@import "CPCollectionViewItem.j"
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPCollectionView
|
@class CPCollectionView
|
||||||
|
|
||||||
This class displays an array as a grid of objects, where each object is represented by a view.
|
This class displays an array as a grid of objects, where each object is represented by a view.
|
||||||
The view is controlled by creating a CPCollectionViewItem and specifying its view, then
|
The view is controlled by creating a CPCollectionViewItem and specifying its view, then
|
||||||
setting that item as the collection view prototype.
|
setting that item as the collection view prototype.
|
||||||
|
|
||||||
@par Delegate Methods
|
@par Delegate Methods
|
||||||
|
|
||||||
@delegate -(void)collectionViewDidChangeSelection:(CPCollectionView)collectionView;
|
@delegate -(void)collectionViewDidChangeSelection:(CPCollectionView)collectionView;
|
||||||
Called when the selection in the collection view has changed.
|
Called when the selection in the collection view has changed.
|
||||||
@param collectionView the collection view who's selection changed
|
@param collectionView the collection view who's selection changed
|
||||||
@@ -62,84 +61,76 @@
|
|||||||
@param indices the indices to obtain drag types
|
@param indices the indices to obtain drag types
|
||||||
@return an array of drag types (CPString)
|
@return an array of drag types (CPString)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@implementation CPCollectionView : CPView
|
@implementation CPCollectionView : CPView
|
||||||
{
|
{
|
||||||
CPArray _content;
|
CPArray _content;
|
||||||
CPArray _items;
|
CPArray _items;
|
||||||
|
|
||||||
CPData _itemData;
|
CPData _itemData;
|
||||||
CPCollectionViewItem _itemPrototype;
|
CPCollectionViewItem _itemPrototype;
|
||||||
CPCollectionViewItem _itemForDragging;
|
CPCollectionViewItem _itemForDragging;
|
||||||
CPMutableArray _cachedItems;
|
CPMutableArray _cachedItems;
|
||||||
|
|
||||||
unsigned _maxNumberOfRows;
|
unsigned _maxNumberOfRows;
|
||||||
unsigned _maxNumberOfColumns;
|
unsigned _maxNumberOfColumns;
|
||||||
|
|
||||||
CGSize _minItemSize;
|
CGSize _minItemSize;
|
||||||
CGSize _maxItemSize;
|
CGSize _maxItemSize;
|
||||||
|
|
||||||
CPArray _backgroundColors;
|
|
||||||
|
|
||||||
float _tileWidth;
|
float _tileWidth;
|
||||||
|
|
||||||
BOOL _isSelectable;
|
BOOL _isSelectable;
|
||||||
BOOL _allowsMultipleSelection;
|
BOOL _allowsMultipleSelection;
|
||||||
BOOL _allowsEmptySelection;
|
BOOL _allowsEmptySelection;
|
||||||
CPIndexSet _selectionIndexes;
|
CPIndexSet _selectionIndexes;
|
||||||
|
|
||||||
CGSize _itemSize;
|
CGSize _itemSize;
|
||||||
|
|
||||||
float _horizontalMargin;
|
float _horizontalMargin;
|
||||||
float _verticalMargin;
|
float _verticalMargin;
|
||||||
|
|
||||||
unsigned _numberOfRows;
|
unsigned _numberOfRows;
|
||||||
unsigned _numberOfColumns;
|
unsigned _numberOfColumns;
|
||||||
|
|
||||||
id _delegate;
|
id _delegate;
|
||||||
|
|
||||||
CPEvent _mouseDownEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_items = [];
|
_items = [];
|
||||||
_content = [];
|
_content = [];
|
||||||
|
|
||||||
_cachedItems = [];
|
_cachedItems = [];
|
||||||
|
|
||||||
_itemSize = CGSizeMakeZero();
|
_itemSize = CGSizeMakeZero();
|
||||||
_minItemSize = CGSizeMakeZero();
|
_minItemSize = CGSizeMakeZero();
|
||||||
_maxItemSize = CGSizeMakeZero();
|
_maxItemSize = CGSizeMakeZero();
|
||||||
|
|
||||||
[self setBackgroundColors:nil];
|
|
||||||
|
|
||||||
_verticalMargin = 5.0;
|
_verticalMargin = 5.0;
|
||||||
_tileWidth = -1.0;
|
_tileWidth = -1.0;
|
||||||
|
|
||||||
_selectionIndexes = [CPIndexSet indexSet];
|
_selectionIndexes = [CPIndexSet indexSet];
|
||||||
_allowsEmptySelection = YES;
|
_allowsEmptySelection = YES;
|
||||||
_isSelectable = YES;
|
_isSelectable = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the item prototype to \c anItem
|
Sets the item prototype to <code>anItem</code>
|
||||||
@param anItem the new item prototype
|
@param anItem the new item prototype
|
||||||
*/
|
*/
|
||||||
- (void)setItemPrototype:(CPCollectionViewItem)anItem
|
- (void)setItemPrototype:(CPCollectionViewItem)anItem
|
||||||
{
|
{
|
||||||
_cachedItems = [];
|
_itemData = [CPKeyedArchiver archivedDataWithRootObject:anItem];
|
||||||
_itemData = nil;
|
_itemForDragging = anItem//[CPKeyedUnarchiver unarchiveObjectWithData:_itemData];
|
||||||
_itemForDragging = nil;
|
|
||||||
_itemPrototype = anItem;
|
_itemPrototype = anItem;
|
||||||
|
|
||||||
[self reloadContent];
|
[self reloadContent];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,24 +143,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a collection view item for \c anObject.
|
Returns a collection view item for <code>anObject</code>.
|
||||||
@param anObject the object to be represented.
|
@param anObject the object to be represented.
|
||||||
*/
|
*/
|
||||||
- (CPCollectionViewItem)newItemForRepresentedObject:(id)anObject
|
- (CPCollectionViewItem)newItemForRepresentedObject:(id)anObject
|
||||||
{
|
{
|
||||||
var item = nil;
|
var item = nil;
|
||||||
|
|
||||||
if (_cachedItems.length)
|
if (_cachedItems.length)
|
||||||
item = _cachedItems.pop();
|
item = _cachedItems.pop();
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
if (!_itemData)
|
|
||||||
if (_itemPrototype)
|
|
||||||
_itemData = [CPKeyedArchiver archivedDataWithRootObject:_itemPrototype];
|
|
||||||
|
|
||||||
item = [CPKeyedUnarchiver unarchiveObjectWithData:_itemData];
|
item = [CPKeyedUnarchiver unarchiveObjectWithData:_itemData];
|
||||||
}
|
|
||||||
|
|
||||||
[item setRepresentedObject:anObject];
|
[item setRepresentedObject:anObject];
|
||||||
[[item view] setFrameSize:_itemSize];
|
[[item view] setFrameSize:_itemSize];
|
||||||
@@ -179,7 +163,7 @@
|
|||||||
|
|
||||||
// Working with the Responder Chain
|
// Working with the Responder Chain
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES by default.
|
Returns <code>YES</code> by default.
|
||||||
*/
|
*/
|
||||||
- (BOOL)acceptsFirstResponder
|
- (BOOL)acceptsFirstResponder
|
||||||
{
|
{
|
||||||
@@ -191,13 +175,13 @@
|
|||||||
*/
|
*/
|
||||||
- (BOOL)isFirstResponder
|
- (BOOL)isFirstResponder
|
||||||
{
|
{
|
||||||
return [[self window] firstResponder] === self;
|
return [[self window] firstResponder] == self;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting the Content
|
// Setting the Content
|
||||||
/*!
|
/*!
|
||||||
Sets the content of the collection view to the content in \c anArray.
|
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 \c -setRepresentedObject: method.
|
This array can be of any type, and each element will be passed to the <code>setRepresentedObject:</code> method.
|
||||||
It's the responsibility of your custom collection view item to interpret the object.
|
It's the responsibility of your custom collection view item to interpret the object.
|
||||||
@param anArray the content array
|
@param anArray the content array
|
||||||
*/
|
*/
|
||||||
@@ -205,9 +189,9 @@
|
|||||||
{
|
{
|
||||||
if (_content == anArray)
|
if (_content == anArray)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_content = anArray;
|
_content = anArray;
|
||||||
|
|
||||||
[self reloadContent];
|
[self reloadContent];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,36 +214,36 @@
|
|||||||
// Setting the Selection Mode
|
// Setting the Selection Mode
|
||||||
/*!
|
/*!
|
||||||
Sets whether the user is allowed to select items
|
Sets whether the user is allowed to select items
|
||||||
@param isSelectable \c YES allows the user to select items.
|
@param isSelectable <code>YES</code> allows the user to select items.
|
||||||
*/
|
*/
|
||||||
- (void)setSelectable:(BOOL)isSelectable
|
- (void)setSelectable:(BOOL)isSelectable
|
||||||
{
|
{
|
||||||
if (_isSelectable == isSelectable)
|
if (_isSelectable == isSelectable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_isSelectable = isSelectable;
|
_isSelectable = isSelectable;
|
||||||
|
|
||||||
if (!_isSelectable)
|
if (!_isSelectable)
|
||||||
{
|
{
|
||||||
var index = CPNotFound;
|
var index = CPNotFound;
|
||||||
|
|
||||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
||||||
[_items[index] setSelected:NO];
|
[_items[index] setSelected:NO];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the collection view is
|
Returns <code>YES</code> if the collection view is
|
||||||
selectable, and \c NO otherwise.
|
selected, and <code>NO</code> otherwise.
|
||||||
*/
|
*/
|
||||||
- (BOOL)isSelectable
|
- (BOOL)isSelected
|
||||||
{
|
{
|
||||||
return _isSelectable;
|
return _isSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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.
|
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 \c YES allows the user to select multiple items
|
@param shouldAllowMultipleSelection <code>YES</code> allows the user to select multiple items
|
||||||
*/
|
*/
|
||||||
- (void)setAllowsEmptySelection:(BOOL)shouldAllowEmptySelection
|
- (void)setAllowsEmptySelection:(BOOL)shouldAllowEmptySelection
|
||||||
{
|
{
|
||||||
@@ -267,7 +251,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the user can select no items, \c NO otherwise.
|
Returns <code>YES</code> if the user can select no items, <code>NO</code> otherwise.
|
||||||
*/
|
*/
|
||||||
- (BOOL)allowsEmptySelection
|
- (BOOL)allowsEmptySelection
|
||||||
{
|
{
|
||||||
@@ -276,7 +260,7 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets whether the user can select multiple items.
|
Sets whether the user can select multiple items.
|
||||||
@param shouldAllowMultipleSelection \c YES allows the user to select multiple items
|
@param shouldAllowMultipleSelection <code>YES</code> allows the user to select multiple items
|
||||||
*/
|
*/
|
||||||
- (void)setAllowsMultipleSelection:(BOOL)shouldAllowMultipleSelection
|
- (void)setAllowsMultipleSelection:(BOOL)shouldAllowMultipleSelection
|
||||||
{
|
{
|
||||||
@@ -284,7 +268,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the user can select multiple items, \c NO otherwise.
|
Returns <code>YES</code> if the user can select multiple items, <code>NO</code> otherwise.
|
||||||
*/
|
*/
|
||||||
- (BOOL)allowsMultipleSelection
|
- (BOOL)allowsMultipleSelection
|
||||||
{
|
{
|
||||||
@@ -297,25 +281,23 @@
|
|||||||
*/
|
*/
|
||||||
- (void)setSelectionIndexes:(CPIndexSet)anIndexSet
|
- (void)setSelectionIndexes:(CPIndexSet)anIndexSet
|
||||||
{
|
{
|
||||||
if ([_selectionIndexes isEqual:anIndexSet] || !_isSelectable)
|
if (_selectionIndexes == anIndexSet || !_isSelectable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var index = CPNotFound;
|
var index = CPNotFound;
|
||||||
|
|
||||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
||||||
[_items[index] setSelected:NO];
|
[_items[index] setSelected:NO];
|
||||||
|
|
||||||
_selectionIndexes = anIndexSet;
|
_selectionIndexes = anIndexSet;
|
||||||
|
|
||||||
var index = CPNotFound;
|
var index = CPNotFound;
|
||||||
|
|
||||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
||||||
[_items[index] setSelected:YES];
|
[_items[index] setSelected:YES];
|
||||||
|
|
||||||
[[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)])
|
if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)])
|
||||||
[_delegate collectionViewDidChangeSelection:self];
|
[_delegate collectionViewDidChangeSelection:self]
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -323,43 +305,37 @@
|
|||||||
*/
|
*/
|
||||||
- (CPIndexSet)selectionIndexes
|
- (CPIndexSet)selectionIndexes
|
||||||
{
|
{
|
||||||
return [_selectionIndexes copy];
|
return _selectionIndexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (void)reloadContent
|
- (void)reloadContent
|
||||||
{
|
{
|
||||||
// Remove current views
|
// Remove current views
|
||||||
var count = _items.length;
|
var count = _items.length;
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
{
|
{
|
||||||
[[_items[count] view] removeFromSuperview];
|
[[_items[count] view] removeFromSuperview];
|
||||||
[_items[count] setSelected:NO];
|
|
||||||
|
|
||||||
_cachedItems.push(_items[count]);
|
_cachedItems.push(_items[count]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_items = [];
|
_items = [];
|
||||||
|
|
||||||
if (!_itemPrototype || !_content)
|
if (!_itemData || !_content)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
|
||||||
count = _content.length;
|
count = _content.length;
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
{
|
{
|
||||||
_items.push([self newItemForRepresentedObject:_content[index]]);
|
_items.push([self newItemForRepresentedObject:_content[index]]);
|
||||||
|
|
||||||
[self addSubview:[_items[index] view]];
|
[self addSubview:[_items[index] view]];
|
||||||
}
|
}
|
||||||
|
|
||||||
index = CPNotFound;
|
|
||||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
|
||||||
[_items[index] setSelected:YES];
|
|
||||||
|
|
||||||
[self tile];
|
[self tile];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,49 +343,49 @@
|
|||||||
- (void)tile
|
- (void)tile
|
||||||
{
|
{
|
||||||
var width = CGRectGetWidth([self bounds]);
|
var width = CGRectGetWidth([self bounds]);
|
||||||
|
|
||||||
if (![_content count] || width == _tileWidth)
|
if (![_content count] || width == _tileWidth)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We try to fit as many views per row as possible. Any remaining space is then
|
// We try to fit as many views per row as possible. Any remaining space is then
|
||||||
// either proportioned out to the views (if their minSize != maxSize) or used as
|
// either proportioned out to the views (if their minSize != maxSize) or used as
|
||||||
// margin
|
// margin
|
||||||
var itemSize = CGSizeMakeCopy(_minItemSize);
|
var itemSize = CGSizeMakeCopy(_minItemSize);
|
||||||
|
|
||||||
_numberOfColumns = MAX(1.0, FLOOR(width / itemSize.width));
|
_numberOfColumns = MAX(1.0, FLOOR(width / itemSize.width));
|
||||||
|
|
||||||
if (_maxNumberOfColumns > 0)
|
if (_maxNumberOfColumns > 0)
|
||||||
_numberOfColumns = MIN(_maxNumberOfColumns, _numberOfColumns);
|
_numberOfColumns = MIN(_maxNumberOfColumns, _numberOfColumns);
|
||||||
|
|
||||||
var remaining = width - _numberOfColumns * itemSize.width,
|
var remaining = width - _numberOfColumns * itemSize.width,
|
||||||
itemsNeedSizeUpdate = NO;
|
itemsNeedSizeUpdate = NO;
|
||||||
|
|
||||||
if (remaining > 0 && itemSize.width < _maxItemSize.width)
|
if (remaining > 0 && itemSize.width < _maxItemSize.width)
|
||||||
itemSize.width = MIN(_maxItemSize.width, itemSize.width + FLOOR(remaining / _numberOfColumns));
|
itemSize.width = MIN(_maxItemSize.width, itemSize.width + FLOOR(remaining / _numberOfColumns));
|
||||||
|
|
||||||
// When we ONE column and a non-integral width, the FLOORing above can cause the item width to be smaller than the total width.
|
// When we ONE column and a non-integral width, the FLOORing above can cause the item width to be smaller than the total width.
|
||||||
if (_maxNumberOfColumns == 1 && itemSize.width < _maxItemSize.width && itemSize.width < width)
|
if (_maxNumberOfColumns == 1 && itemSize.width < _maxItemSize.width && itemSize.width < width)
|
||||||
itemSize.width = MIN(_maxItemSize.width, width);
|
itemSize.width = MIN(_maxItemSize.width, width);
|
||||||
|
|
||||||
if (!CGSizeEqualToSize(_itemSize, itemSize))
|
if (!CGSizeEqualToSize(_itemSize, itemSize))
|
||||||
{
|
{
|
||||||
_itemSize = itemSize;
|
_itemSize = itemSize;
|
||||||
itemsNeedSizeUpdate = YES;
|
itemsNeedSizeUpdate = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = 0,
|
var index = 0,
|
||||||
count = _items.length;
|
count = _items.length;
|
||||||
|
|
||||||
if (_maxNumberOfColumns > 0 && _maxNumberOfRows > 0)
|
if (_maxNumberOfColumns > 0 && _maxNumberOfRows > 0)
|
||||||
count = MIN(count, _maxNumberOfColumns * _maxNumberOfRows);
|
count = MIN(count, _maxNumberOfColumns * _maxNumberOfRows);
|
||||||
|
|
||||||
_numberOfRows = CEIL(count / _numberOfColumns);
|
_numberOfRows = CEIL(count / _numberOfColumns);
|
||||||
|
|
||||||
_horizontalMargin = FLOOR((width - _numberOfColumns * itemSize.width) / (_numberOfColumns + 1));
|
_horizontalMargin = FLOOR((width - _numberOfColumns * itemSize.width) / (_numberOfColumns + 1));
|
||||||
|
|
||||||
var x = _horizontalMargin,
|
var x = _horizontalMargin,
|
||||||
y = -itemSize.height;
|
y = -itemSize.height;
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
{
|
{
|
||||||
if (index % _numberOfColumns == 0)
|
if (index % _numberOfColumns == 0)
|
||||||
@@ -417,28 +393,19 @@
|
|||||||
x = _horizontalMargin;
|
x = _horizontalMargin;
|
||||||
y += _verticalMargin + itemSize.height;
|
y += _verticalMargin + itemSize.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
var view = [_items[index] view];
|
var view = [_items[index] view];
|
||||||
|
|
||||||
[view setFrameOrigin:CGPointMake(x, y)];
|
[view setFrameOrigin:CGPointMake(x, y)];
|
||||||
|
|
||||||
if (itemsNeedSizeUpdate)
|
if (itemsNeedSizeUpdate)
|
||||||
[view setFrameSize:_itemSize];
|
[view setFrameSize:_itemSize];
|
||||||
|
|
||||||
x += itemSize.width + _horizontalMargin;
|
x += itemSize.width + _horizontalMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
var superview = [self superview],
|
|
||||||
proposedHeight = y + itemSize.height + _verticalMargin;
|
|
||||||
|
|
||||||
if ([superview isKindOfClass:[CPClipView class]])
|
|
||||||
{
|
|
||||||
var superviewSize = [superview bounds].size;
|
|
||||||
proposedHeight = MAX(superviewSize.height, proposedHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
_tileWidth = width;
|
_tileWidth = width;
|
||||||
[self setFrameSize:CGSizeMake(width, proposedHeight)];
|
[self setFrameSize:CGSizeMake(width, y + itemSize.height + _verticalMargin)];
|
||||||
_tileWidth = -1.0;
|
_tileWidth = -1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,9 +423,9 @@
|
|||||||
{
|
{
|
||||||
if (_maxNumberOfRows == aMaxNumberOfRows)
|
if (_maxNumberOfRows == aMaxNumberOfRows)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_maxNumberOfRows = aMaxNumberOfRows;
|
_maxNumberOfRows = aMaxNumberOfRows;
|
||||||
|
|
||||||
[self tile];
|
[self tile];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,9 +445,9 @@
|
|||||||
{
|
{
|
||||||
if (_maxNumberOfColumns == aMaxNumberOfColumns)
|
if (_maxNumberOfColumns == aMaxNumberOfColumns)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_maxNumberOfColumns = aMaxNumberOfColumns;
|
_maxNumberOfColumns = aMaxNumberOfColumns;
|
||||||
|
|
||||||
[self tile];
|
[self tile];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,9 +484,9 @@
|
|||||||
{
|
{
|
||||||
if (CGSizeEqualToSize(_minItemSize, aSize))
|
if (CGSizeEqualToSize(_minItemSize, aSize))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_minItemSize = CGSizeMakeCopy(aSize);
|
_minItemSize = CGSizeMakeCopy(aSize);
|
||||||
|
|
||||||
[self tile];
|
[self tile];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,9 +506,9 @@
|
|||||||
{
|
{
|
||||||
if (CGSizeEqualToSize(_maxItemSize, aSize))
|
if (CGSizeEqualToSize(_maxItemSize, aSize))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_maxItemSize = CGSizeMakeCopy(aSize);
|
_maxItemSize = CGSizeMakeCopy(aSize);
|
||||||
|
|
||||||
[self tile];
|
[self tile];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,30 +520,6 @@
|
|||||||
return _maxItemSize;
|
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
|
- (void)mouseUp:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
if ([_selectionIndexes count] && [anEvent clickCount] == 2 && [_delegate respondsToSelector:@selector(collectionView:didDoubleClickOnItemAtIndex:)])
|
if ([_selectionIndexes count] && [anEvent clickCount] == 2 && [_delegate respondsToSelector:@selector(collectionView:didDoubleClickOnItemAtIndex:)])
|
||||||
@@ -585,89 +528,45 @@
|
|||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
_mouseDownEvent = anEvent;
|
|
||||||
|
|
||||||
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||||
row = FLOOR(location.y / (_itemSize.height + _verticalMargin)),
|
row = FLOOR(location.y / (_itemSize.height + _verticalMargin)),
|
||||||
column = FLOOR(location.x / (_itemSize.width + _horizontalMargin)),
|
column = FLOOR(location.x / (_itemSize.width + _horizontalMargin)),
|
||||||
index = row * _numberOfColumns + column;
|
index = row * _numberOfColumns + column;
|
||||||
|
|
||||||
if (index >= 0 && index < _items.length)
|
if (index >= 0 && index < _items.length)
|
||||||
{
|
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
||||||
if (_allowsMultipleSelection && ([anEvent modifierFlags] & CPCommandKeyMask || [anEvent modifierFlags] & CPShiftKeyMask))
|
|
||||||
{
|
|
||||||
if ([anEvent modifierFlags] & CPCommandKeyMask)
|
|
||||||
{
|
|
||||||
var indexes = [_selectionIndexes copy];
|
|
||||||
|
|
||||||
if ([indexes containsIndex:index])
|
|
||||||
[indexes removeIndex:index];
|
|
||||||
else
|
|
||||||
[indexes addIndex:index];
|
|
||||||
}
|
|
||||||
else if ([anEvent modifierFlags] & CPShiftKeyMask)
|
|
||||||
{
|
|
||||||
var firstSelectedIndex = [[self selectionIndexes] firstIndex],
|
|
||||||
newSelectedRange = nil;
|
|
||||||
|
|
||||||
if (index < firstSelectedIndex)
|
|
||||||
newSelectedRange = CPMakeRange(index, (firstSelectedIndex - index) + 1);
|
|
||||||
else
|
|
||||||
newSelectedRange = CPMakeRange(firstSelectedIndex, (index - firstSelectedIndex) + 1);
|
|
||||||
|
|
||||||
indexes = [[self selectionIndexes] copy];
|
|
||||||
[indexes addIndexesInRange:newSelectedRange];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
indexes = [CPIndexSet indexSetWithIndex:index];
|
|
||||||
|
|
||||||
[self setSelectionIndexes:indexes];
|
|
||||||
}
|
|
||||||
else if (_allowsEmptySelection)
|
else if (_allowsEmptySelection)
|
||||||
[self setSelectionIndexes:[CPIndexSet indexSet]];
|
[self setSelectionIndexes:[CPIndexSet indexSet]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDragged:(CPEvent)anEvent
|
- (void)mouseDragged:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var locationInWindow = [anEvent locationInWindow],
|
|
||||||
mouseDownLocationInWindow = [_mouseDownEvent locationInWindow];
|
|
||||||
|
|
||||||
// FIXME: This is because Safari's drag hysteresis is 3px x 3px
|
|
||||||
if ((ABS(locationInWindow.x - mouseDownLocationInWindow.x) < 3) &&
|
|
||||||
(ABS(locationInWindow.y - mouseDownLocationInWindow.y) < 3))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (![_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)])
|
if (![_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If we don't have any selected items, we've clicked away, and thus the drag is meaningless.
|
// If we don't have any selected items, we've clicked away, and thus the drag is meaningless.
|
||||||
if (![_selectionIndexes count])
|
if (![_selectionIndexes count])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(collectionView:canDragItemsAtIndexes:withEvent:)] &&
|
|
||||||
![_delegate collectionView:self canDragItemsAtIndexes:_selectionIndexes withEvent:_mouseDownEvent])
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Set up the pasteboard
|
// Set up the pasteboard
|
||||||
var dragTypes = [_delegate collectionView:self dragTypesForItemsAtIndexes:_selectionIndexes];
|
var dragTypes = [_delegate collectionView:self dragTypesForItemsAtIndexes:_selectionIndexes];
|
||||||
|
|
||||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:dragTypes owner:self];
|
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:dragTypes owner:self];
|
||||||
|
|
||||||
|
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
|
|
||||||
if (!_itemForDragging)
|
[_itemForDragging setRepresentedObject:_content[[_selectionIndexes firstIndex]]];
|
||||||
_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 setFrameSize:_itemSize];
|
||||||
[view setAlphaValue:0.7];
|
[view setAlphaValue:0.7];
|
||||||
|
|
||||||
[self dragView:view
|
[self dragView:view
|
||||||
at:[[_items[[_selectionIndexes firstIndex]] view] frame].origin
|
at:[[_items[[_selectionIndexes firstIndex]] view] frame].origin
|
||||||
offset:CGSizeMakeZero()
|
offset:CGPointMakeZero()
|
||||||
event:_mouseDownEvent
|
event:anEvent
|
||||||
pasteboard:nil
|
pasteboard:nil
|
||||||
source:self
|
source:self
|
||||||
slideBack:YES];
|
slideBack:YES];
|
||||||
@@ -694,9 +593,9 @@
|
|||||||
{
|
{
|
||||||
if (_verticalMargin == aVerticalMargin)
|
if (_verticalMargin == aVerticalMargin)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_verticalMargin = aVerticalMargin;
|
_verticalMargin = aVerticalMargin;
|
||||||
|
|
||||||
[self tile];
|
[self tile];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,173 +625,100 @@
|
|||||||
return _delegate;
|
return _delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPCollectionViewItem)itemAtIndex:(unsigned)anIndex
|
@end
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Represents an object inside a CPCollectionView.
|
||||||
|
*/
|
||||||
|
@implementation CPCollectionViewItem : CPObject
|
||||||
{
|
{
|
||||||
return [_items objectAtIndex:anIndex];
|
id _representedObject;
|
||||||
|
|
||||||
|
CPView _view;
|
||||||
|
|
||||||
|
BOOL _isSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)frameForItemAtIndex:(unsigned)anIndex
|
// Setting the Represented Object
|
||||||
|
/*!
|
||||||
|
Sets the object to be represented by this item.
|
||||||
|
@param anObject the object to be represented
|
||||||
|
*/
|
||||||
|
- (void)setRepresentedObject:(id)anObject
|
||||||
{
|
{
|
||||||
return [[[self itemAtIndex:anIndex] view] frame];
|
if (_representedObject == anObject)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_representedObject = anObject;
|
||||||
|
|
||||||
|
// FIXME: This should be set up by bindings
|
||||||
|
[_view setRepresentedObject:anObject];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)frameForItemsAtIndexes:(CPIndexSet)anIndexSet
|
/*!
|
||||||
|
Returns the object represented by this view item
|
||||||
|
*/
|
||||||
|
- (id)representedObject
|
||||||
{
|
{
|
||||||
var indexArray = [],
|
return _representedObject;
|
||||||
frame = CGRectNull;
|
}
|
||||||
|
|
||||||
[anIndexSet getIndexes:indexArray maxCount:-1 inIndexRange:nil];
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
var index = 0,
|
/*!
|
||||||
count = [indexArray count];
|
Returns the view that represents this object.
|
||||||
|
*/
|
||||||
|
- (CPView)view
|
||||||
|
{
|
||||||
|
return _view;
|
||||||
|
}
|
||||||
|
|
||||||
for (; index < count; ++index)
|
// Modifying the Selection
|
||||||
frame = CGRectUnion(frame, [self frameForItemAtIndex:indexArray[index]]);
|
/*!
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
return frame;
|
/*!
|
||||||
|
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
|
@end
|
||||||
|
|
||||||
@implementation CPCollectionView (KeyboardInteraction)
|
var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
||||||
|
CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey",
|
||||||
- (void)_modifySelectionWithNewIndex:(int)anIndex direction:(int)aDirection expand:(BOOL)shouldExpand
|
CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey";
|
||||||
{
|
|
||||||
anIndex = MIN(MAX(anIndex, 0), [[self items] count] - 1);
|
|
||||||
|
|
||||||
if (_allowsMultipleSelection && shouldExpand)
|
|
||||||
{
|
|
||||||
var indexes = [_selectionIndexes copy],
|
|
||||||
bottomAnchor = [indexes firstIndex],
|
|
||||||
topAnchor = [indexes lastIndex];
|
|
||||||
|
|
||||||
// if the direction is backward (-1) check with the bottom anchor
|
|
||||||
if (aDirection === -1)
|
|
||||||
[indexes addIndexesInRange:CPMakeRange(anIndex, bottomAnchor - anIndex + 1)];
|
|
||||||
else
|
|
||||||
[indexes addIndexesInRange:CPMakeRange(topAnchor, anIndex - topAnchor + 1)];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
indexes = [CPIndexSet indexSetWithIndex:anIndex];
|
|
||||||
|
|
||||||
[self setSelectionIndexes:indexes];
|
|
||||||
[self _scrollToSelection];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_scrollToSelection
|
|
||||||
{
|
|
||||||
var frame = [self frameForItemsAtIndexes:[self selectionIndexes]];
|
|
||||||
|
|
||||||
if (!CGRectIsNull(frame))
|
|
||||||
[self scrollRectToVisible:frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveLeft:(id)sender
|
|
||||||
{
|
|
||||||
var index = [[self selectionIndexes] firstIndex];
|
|
||||||
if (index === CPNotFound)
|
|
||||||
index = [[self items] count];
|
|
||||||
|
|
||||||
[self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveLeftAndModifySelection:(id)sender
|
|
||||||
{
|
|
||||||
var index = [[self selectionIndexes] firstIndex];
|
|
||||||
if (index === CPNotFound)
|
|
||||||
index = [[self items] count];
|
|
||||||
|
|
||||||
[self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveRight:(id)sender
|
|
||||||
{
|
|
||||||
[self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveRightAndModifySelection:(id)sender
|
|
||||||
{
|
|
||||||
[self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveDown:(id)sender
|
|
||||||
{
|
|
||||||
[self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveDownAndModifySelection:(id)sender
|
|
||||||
{
|
|
||||||
[self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveUp:(id)sender
|
|
||||||
{
|
|
||||||
var index = [[self selectionIndexes] firstIndex];
|
|
||||||
if (index == CPNotFound)
|
|
||||||
index = [[self items] count];
|
|
||||||
|
|
||||||
[self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveUpAndModifySelection:(id)sender
|
|
||||||
{
|
|
||||||
var index = [[self selectionIndexes] firstIndex];
|
|
||||||
if (index == CPNotFound)
|
|
||||||
index = [[self items] count];
|
|
||||||
|
|
||||||
[self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)deleteBackward:(id)sender
|
|
||||||
{
|
|
||||||
if ([[self delegate] respondsToSelector:@selector(collectionView:shouldDeleteItemsAtIndexes:)])
|
|
||||||
{
|
|
||||||
[[self delegate] collectionView:self shouldDeleteItemsAtIndexes:[self selectionIndexes]];
|
|
||||||
|
|
||||||
var index = [[self selectionIndexes] firstIndex];
|
|
||||||
if (index > [[self content] count] - 1)
|
|
||||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:[[self content] count] - 1]];
|
|
||||||
|
|
||||||
[self _scrollToSelection];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)keyDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
[self interpretKeyEvents:[anEvent]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPCollectionView (Deprecated)
|
|
||||||
|
|
||||||
- (CGRect)rectForItemAtIndex:(int)anIndex
|
|
||||||
{
|
|
||||||
_CPReportLenientDeprecation([self class], _cmd, @selector(frameForItemAtIndex:));
|
|
||||||
|
|
||||||
// Don't re-compute anything just grab the current frame
|
|
||||||
// This allows subclasses to override tile without messing this up.
|
|
||||||
return [self frameForItemAtIndex:anIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectForItemsAtIndexes:(CPIndexSet)anIndexSet
|
|
||||||
{
|
|
||||||
_CPReportLenientDeprecation([self class], _cmd, @selector(frameForItemsAtIndexes:));
|
|
||||||
|
|
||||||
return [self frameForItemsAtIndexes:anIndexSet];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
|
||||||
CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey",
|
|
||||||
CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey",
|
|
||||||
CPCollectionViewMaxNumberOfRowsKey = @"CPCollectionViewMaxNumberOfRowsKey",
|
|
||||||
CPCollectionViewMaxNumberOfColumnsKey = @"CPCollectionViewMaxNumberOfColumnsKey",
|
|
||||||
CPCollectionViewSelectableKey = @"CPCollectionViewSelectableKey",
|
|
||||||
CPCollectionViewAllowsMultipleSelectionKey = @"CPCollectionViewAllowsMultipleSelectionKey",
|
|
||||||
CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey";
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPCollectionView (CPCoding)
|
@implementation CPCollectionView (CPCoding)
|
||||||
@@ -909,25 +735,13 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeK
|
|||||||
_cachedItems = [];
|
_cachedItems = [];
|
||||||
|
|
||||||
_itemSize = CGSizeMakeZero();
|
_itemSize = CGSizeMakeZero();
|
||||||
|
_minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey];
|
||||||
|
_maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey];
|
||||||
|
|
||||||
_minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey] || CGSizeMakeZero();
|
_verticalMargin = [aCoder decodeSizeForKey:CPCollectionViewVerticalMarginKey];
|
||||||
_maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey] || CGSizeMakeZero();
|
|
||||||
|
|
||||||
_maxNumberOfRows = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfRowsKey] || 0;
|
|
||||||
_maxNumberOfColumns = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfColumnsKey] || 0;
|
|
||||||
|
|
||||||
_verticalMargin = [aCoder decodeFloatForKey:CPCollectionViewVerticalMarginKey];
|
|
||||||
|
|
||||||
_isSelectable = [aCoder decodeBoolForKey:CPCollectionViewSelectableKey];
|
|
||||||
_allowsMultipleSelection = [aCoder decodeBoolForKey:CPCollectionViewAllowsMultipleSelectionKey];
|
|
||||||
|
|
||||||
[self setBackgroundColors:[aCoder decodeObjectForKey:CPCollectionViewBackgroundColorsKey]];
|
|
||||||
|
|
||||||
_tileWidth = -1.0;
|
_tileWidth = -1.0;
|
||||||
|
|
||||||
_selectionIndexes = [CPIndexSet indexSet];
|
_selectionIndexes = [CPIndexSet indexSet];
|
||||||
|
|
||||||
_allowsEmptySelection = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -937,21 +751,54 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeK
|
|||||||
{
|
{
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
if (!CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
|
[aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
|
||||||
[aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
|
[aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
|
||||||
|
|
||||||
if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
|
[aCoder encodeSize:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
|
||||||
[aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
|
|
||||||
|
|
||||||
[aCoder encodeInt:_maxNumberOfRows forKey:CPCollectionViewMaxNumberOfRowsKey];
|
|
||||||
[aCoder encodeInt:_maxNumberOfColumns forKey:CPCollectionViewMaxNumberOfColumnsKey];
|
|
||||||
|
|
||||||
[aCoder encodeBool:_isSelectable forKey:CPCollectionViewSelectableKey];
|
|
||||||
[aCoder encodeBool:_allowsMultipleSelection forKey:CPCollectionViewAllowsMultipleSelectionKey];
|
|
||||||
|
|
||||||
[aCoder encodeFloat:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_backgroundColors forKey:CPCollectionViewBackgroundColorsKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@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
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
+149
-181
@@ -32,7 +32,7 @@ var _redComponent = 0,
|
|||||||
_greenComponent = 1,
|
_greenComponent = 1,
|
||||||
_blueComponent = 2,
|
_blueComponent = 2,
|
||||||
_alphaCompnent = 3;
|
_alphaCompnent = 3;
|
||||||
|
|
||||||
var _hueComponent = 0,
|
var _hueComponent = 0,
|
||||||
_saturationComponent = 1,
|
_saturationComponent = 1,
|
||||||
_brightnessComponent = 2;
|
_brightnessComponent = 2;
|
||||||
@@ -46,29 +46,30 @@ var cachedBlackColor,
|
|||||||
cachedLightGrayColor,
|
cachedLightGrayColor,
|
||||||
cachedDarkGrayColor,
|
cachedDarkGrayColor,
|
||||||
cachedWhiteColor,
|
cachedWhiteColor,
|
||||||
cachedBrownColor,
|
|
||||||
cachedCyanColor,
|
|
||||||
cachedMagentaColor,
|
|
||||||
cachedOrangeColor,
|
|
||||||
cachedPurpleColor,
|
|
||||||
cachedShadowColor,
|
cachedShadowColor,
|
||||||
cachedClearColor;
|
cachedClearColor;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
|
@code CPColor
|
||||||
|
|
||||||
\c CPColor can be used to represent color
|
<code>CPColor</code> can be used to represent color
|
||||||
in an RGB or HSB model with an optional transparency value.</p>
|
in an RGB or HSB model with an optional transparency value.</p>
|
||||||
|
|
||||||
<p>It also provides some class helper methods that
|
<p>It also provides some class helper methods that
|
||||||
returns instances of commonly used colors.</p>
|
returns instances of commonly used colors.</p>
|
||||||
|
|
||||||
|
<p>The class does not have a <code>set:</code> method
|
||||||
|
like NextStep based frameworks to change the color of
|
||||||
|
the current context. To change the color of the current
|
||||||
|
context, use CGContextSetFillColor().
|
||||||
*/
|
*/
|
||||||
@implementation CPColor : CPObject
|
@implementation CPColor : CPObject
|
||||||
{
|
{
|
||||||
CPArray _components;
|
CPArray _components;
|
||||||
|
|
||||||
CPImage _patternImage;
|
CPImage _patternImage;
|
||||||
CPString _cssString;
|
CPString _cssString;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -76,12 +77,12 @@ var cachedBlackColor,
|
|||||||
Each component should be between the range of 0.0 to 1.0. For
|
Each component should be between the range of 0.0 to 1.0. For
|
||||||
the alpha component, a value of 1.0 is opaque, and 0.0 means
|
the alpha component, a value of 1.0 is opaque, and 0.0 means
|
||||||
completely transparent.
|
completely transparent.
|
||||||
|
|
||||||
@param red the red component of the color
|
@param red the red component of the color
|
||||||
@param green the green component of the color
|
@param green the green component of the color
|
||||||
@param blue the blue component of the color
|
@param blue the blue component of the color
|
||||||
@param alpha the alpha component
|
@param alpha the alpha component
|
||||||
|
|
||||||
@return a color initialized to the values specified
|
@return a color initialized to the values specified
|
||||||
*/
|
*/
|
||||||
+ (CPColor)colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
|
+ (CPColor)colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
|
||||||
@@ -91,17 +92,17 @@ var cachedBlackColor,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@deprecated in favor of colorWithRed:green:blue:alpha:
|
@deprecated in favor of colorWithRed:green:blue:alpha:
|
||||||
|
|
||||||
Creates a color in the RGB color space, with an alpha value.
|
Creates a color in the RGB color space, with an alpha value.
|
||||||
Each component should be between the range of 0.0 to 1.0. For
|
Each component should be between the range of 0.0 to 1.0. For
|
||||||
the alpha component, a value of 1.0 is opaque, and 0.0 means
|
the alpha component, a value of 1.0 is opaque, and 0.0 means
|
||||||
completely transparent.
|
completely transparent.
|
||||||
|
|
||||||
@param red the red component of the color
|
@param red the red component of the color
|
||||||
@param green the green component of the color
|
@param green the green component of the color
|
||||||
@param blue the blue component of the color
|
@param blue the blue component of the color
|
||||||
@param alpha the alpha component
|
@param alpha the alpha component
|
||||||
|
|
||||||
@return a color initialized to the values specified
|
@return a color initialized to the values specified
|
||||||
*/
|
*/
|
||||||
+ (CPColor)colorWithCalibratedRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
|
+ (CPColor)colorWithCalibratedRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
|
||||||
@@ -111,12 +112,12 @@ var cachedBlackColor,
|
|||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a new color object with \c white for the RGB components.
|
Creates a new color object with <code>white</code> for the RGB components.
|
||||||
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
|
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
|
@param white a float between 0.0 and 1.0
|
||||||
@param alpha the alpha component between 0.0 and 1.0
|
@param alpha the alpha component between 0.0 and 1.0
|
||||||
|
|
||||||
@return a color initialized to the values specified
|
@return a color initialized to the values specified
|
||||||
*/
|
*/
|
||||||
+ (CPColor)colorWithWhite:(float)white alpha:(float)alpha
|
+ (CPColor)colorWithWhite:(float)white alpha:(float)alpha
|
||||||
@@ -126,13 +127,13 @@ var cachedBlackColor,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@deprecated in favor of colorWithWhite:apha:
|
@deprecated in favor of colorWithWhite:apha:
|
||||||
|
|
||||||
Creates a new color object with \c white for the RGB components.
|
Creates a new color object with <code>white</code> for the RGB components.
|
||||||
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
|
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
|
@param white a float between 0.0 and 1.0
|
||||||
@param alpha the alpha component between 0.0 and 1.0
|
@param alpha the alpha component between 0.0 and 1.0
|
||||||
|
|
||||||
@return a color initialized to the values specified
|
@return a color initialized to the values specified
|
||||||
*/
|
*/
|
||||||
+ (CPColor)colorWithCalibratedWhite:(float)white alpha:(float)alpha
|
+ (CPColor)colorWithCalibratedWhite:(float)white alpha:(float)alpha
|
||||||
@@ -142,11 +143,11 @@ var cachedBlackColor,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a new color in HSB space.
|
Creates a new color in HSB space.
|
||||||
|
|
||||||
@param hue the hue value
|
@param hue the hue value
|
||||||
@param saturation the saturation value
|
@param saturation the saturation value
|
||||||
@param brightness the brightness value
|
@param brightness the brightness value
|
||||||
|
|
||||||
@return the initialized color
|
@return the initialized color
|
||||||
*/
|
*/
|
||||||
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness
|
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness
|
||||||
@@ -156,16 +157,16 @@ var cachedBlackColor,
|
|||||||
|
|
||||||
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
|
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
|
||||||
{
|
{
|
||||||
if (saturation === 0.0)
|
if(saturation === 0.0)
|
||||||
return [CPColor colorWithCalibratedWhite:brightness / 100.0 alpha:alpha];
|
return [CPColor colorWithCalibratedWhite:brightness / 100.0 alpha:alpha];
|
||||||
|
|
||||||
var f = hue % 60,
|
var f = hue % 60,
|
||||||
p = (brightness * (100 - saturation)) / 10000,
|
p = (brightness * (100 - saturation)) / 10000,
|
||||||
q = (brightness * (6000 - saturation * f)) / 600000,
|
q = (brightness * (6000 - saturation * f)) / 600000,
|
||||||
t = (brightness * (6000 - saturation * (60 -f))) / 600000,
|
t = (brightness * (6000 - saturation * (60 -f))) / 600000,
|
||||||
b = brightness / 100.0;
|
b = brightness / 100.0;
|
||||||
|
|
||||||
switch (FLOOR(hue / 60))
|
switch(FLOOR(hue / 60))
|
||||||
{
|
{
|
||||||
case 0: return [CPColor colorWithCalibratedRed: b green: t blue: p alpha: alpha];
|
case 0: return [CPColor colorWithCalibratedRed: b green: t blue: p alpha: alpha];
|
||||||
case 1: return [CPColor colorWithCalibratedRed: q green: b blue: p alpha: alpha];
|
case 1: return [CPColor colorWithCalibratedRed: q green: b blue: p alpha: alpha];
|
||||||
@@ -180,7 +181,7 @@ var cachedBlackColor,
|
|||||||
Creates an RGB color from a hexadecimal string. For example,
|
Creates an RGB color from a hexadecimal string. For example,
|
||||||
the a string of "FFFFFF" would return a white CPColor.
|
the a string of "FFFFFF" would return a white CPColor.
|
||||||
"FF0000" would return a pure red, "00FF00" would return a
|
"FF0000" would return a pure red, "00FF00" would return a
|
||||||
pure blue, and "0000FF" would return a pure green.
|
pure blue, and "0000FF" would return a pure green.
|
||||||
|
|
||||||
@param hex a 6 character long string of hex
|
@param hex a 6 character long string of hex
|
||||||
|
|
||||||
@@ -188,8 +189,7 @@ var cachedBlackColor,
|
|||||||
*/
|
*/
|
||||||
+ (CPColor)colorWithHexString:(string)hex
|
+ (CPColor)colorWithHexString:(string)hex
|
||||||
{
|
{
|
||||||
var rgba = hexToRGB(hex);
|
return [[CPColor alloc] _initWithRGBA: hexToRGB(hex)];
|
||||||
return rgba ? [[CPColor alloc] _initWithRGBA: rgba] : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -291,61 +291,6 @@ var cachedBlackColor,
|
|||||||
return cachedYellowColor;
|
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])
|
Returns a shadow looking color (RGBA=[0.0, 0.0, 0.0, 0.33])
|
||||||
*/
|
*/
|
||||||
@@ -370,18 +315,8 @@ var cachedBlackColor,
|
|||||||
return cachedClearColor;
|
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 \c anImage
|
Creates a color using a tile pattern with <code>anImage</code>
|
||||||
@param the image to tile
|
@param the image to tile
|
||||||
@return a tiled image color object
|
@return a tiled image color object
|
||||||
*/
|
*/
|
||||||
@@ -392,7 +327,7 @@ var cachedBlackColor,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a CPColor from a valid CSS RGB string. Example, "rgb(32,64,129)".
|
Creates a CPColor from a valid CSS RGB string. Example, "rgb(32,64,129)".
|
||||||
|
|
||||||
@param aString a CSS color string
|
@param aString a CSS color string
|
||||||
@return a color initialized to the value in the css string
|
@return a color initialized to the value in the css string
|
||||||
*/
|
*/
|
||||||
@@ -404,23 +339,23 @@ var cachedBlackColor,
|
|||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (id)_initWithCSSString:(CPString)aString
|
- (id)_initWithCSSString:(CPString)aString
|
||||||
{
|
{
|
||||||
if (aString.indexOf("rgb") == CPNotFound)
|
if(aString.indexOf("rgb") == CPNotFound)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
var startingIndex = aString.indexOf("("),
|
var startingIndex = aString.indexOf("(");
|
||||||
parts = aString.substring(startingIndex + 1).split(',');
|
var parts = aString.substring(startingIndex+1).split(',');
|
||||||
|
|
||||||
_components = [
|
_components = [
|
||||||
parseInt(parts[0], 10) / 255.0,
|
parseInt(parts[0], 10) / 255.0,
|
||||||
parseInt(parts[1], 10) / 255.0,
|
parseInt(parts[1], 10) / 255.0,
|
||||||
parseInt(parts[2], 10) / 255.0,
|
parseInt(parts[2], 10) / 255.0,
|
||||||
parts[3] ? parseInt(parts[3], 10) / 255.0 : 1.0
|
parts[3] ? parseInt(parts[3], 10) / 255.0 : 1.0
|
||||||
];
|
]
|
||||||
|
|
||||||
_cssString = aString;
|
_cssString = aString;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,20 +363,55 @@ var cachedBlackColor,
|
|||||||
- (id)_initWithRGBA:(CPArray)components
|
- (id)_initWithRGBA:(CPArray)components
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_components = components;
|
_components = components;
|
||||||
|
|
||||||
var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
|
if (!CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0 && window.Base64 && window.CRC32)
|
||||||
|
{
|
||||||
_cssString = (hasAlpha ? "rgba(" : "rgb(") +
|
var bytes = [0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x8,0x3,0x0,0x0,0x0,0x28,0xcb,0x34,0xbb,0x0,0x0,0x3,0x0,0x50,0x4c,0x54,0x45,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x89,0x99,0x55,0x0,0x0,0x0,0x1,0x74,0x52,0x4e,0x53,0x0,0x40,0xe6,0xd8,0x66,0x0,0x0,0x0,0x10,0x49,0x44,0x41,0x54,0x78,0xda,0x62,0x60,0x0,0x0,0x0,0x0,0xff,0xff,0x3,0x0,0x0,0x2,0x0,0x1,0x24,0x7f,0x24,0xf1,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,0xff];
|
||||||
parseInt(_components[0] * 255.0) + ", " +
|
var r_off = 41;
|
||||||
parseInt(_components[1] * 255.0) + ", " +
|
var g_off = 42;
|
||||||
parseInt(_components[2] * 255.0) +
|
var b_off = 43;
|
||||||
(hasAlpha ? (", " + _components[3]) : "") + ")";
|
var a_off = 821;
|
||||||
|
var plte_crc_off = 809;
|
||||||
|
var trns_crc_off = 822;
|
||||||
|
var plte_type_off = 37;
|
||||||
|
var trns_type_off = 817;
|
||||||
|
|
||||||
|
bytes[r_off] = Math.round(_components[0]*255);
|
||||||
|
bytes[g_off] = Math.round(_components[1]*255);
|
||||||
|
bytes[b_off] = Math.round(_components[2]*255);
|
||||||
|
bytes[a_off] = Math.round(_components[3]*255);
|
||||||
|
|
||||||
|
// calculate new CRCs
|
||||||
|
var new_plte_crc = integerToBytes(CRC32.getCRC(bytes, plte_type_off, 4+768), 4);
|
||||||
|
var new_trns_crc = integerToBytes(CRC32.getCRC(bytes, trns_type_off, 4+1), 4);
|
||||||
|
|
||||||
|
// overwrite old CRCs with new ones
|
||||||
|
for (var i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
bytes[plte_crc_off+i] = new_plte_crc[i];
|
||||||
|
bytes[trns_crc_off+i] = new_trns_crc[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64 encode, strip whitespace and build data URL
|
||||||
|
var base64image = Base64.encode(bytes); //.replace(/[\s]/g, "");
|
||||||
|
|
||||||
|
_cssString = "url(\"data:image/png;base64," + base64image + "\")";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
|
||||||
|
|
||||||
|
_cssString = (hasAlpha ? "rgba(" : "rgb(") +
|
||||||
|
parseInt(_components[0] * 255.0) + ", " +
|
||||||
|
parseInt(_components[1] * 255.0) + ", " +
|
||||||
|
parseInt(_components[2] * 255.0) +
|
||||||
|
(hasAlpha ? (", " + _components[3]) : "") + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,14 +419,13 @@ var cachedBlackColor,
|
|||||||
- (id)_initWithPatternImage:(CPImage)anImage
|
- (id)_initWithPatternImage:(CPImage)anImage
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_patternImage = anImage;
|
_patternImage = anImage;
|
||||||
_cssString = "url(\"" + [_patternImage filename] + "\")";
|
_cssString = "url(\"" + [_patternImage filename] + "\")";
|
||||||
_components = [0.0, 0.0, 0.0, 1.0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,17 +487,17 @@ var cachedBlackColor,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a new color with the same RGB as the receiver but a new alpha component.
|
Returns a new color with the same RGB as the receiver but a new alpha component.
|
||||||
|
|
||||||
@param anAlphaComponent the alpha component for the new color
|
@param anAlphaComponent the alpha component for the new color
|
||||||
|
|
||||||
@return a new color object
|
@return a new color object
|
||||||
*/
|
*/
|
||||||
- (CPColor)colorWithAlphaComponent:(float)anAlphaComponent
|
- (CPColor)colorWithAlphaComponent:(float)anAlphaComponent
|
||||||
{
|
{
|
||||||
var components = _components.slice();
|
var components = _components.slice();
|
||||||
|
|
||||||
components[components.length - 1] = anAlphaComponent;
|
components[components.length - 1] = anAlphaComponent;
|
||||||
|
|
||||||
return [[[self class] alloc] _initWithRGBA:components];
|
return [[[self class] alloc] _initWithRGBA:components];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,40 +516,37 @@ var cachedBlackColor,
|
|||||||
var red = ROUND(_components[_redComponent] * 255.0),
|
var red = ROUND(_components[_redComponent] * 255.0),
|
||||||
green = ROUND(_components[_greenComponent] * 255.0),
|
green = ROUND(_components[_greenComponent] * 255.0),
|
||||||
blue = ROUND(_components[_blueComponent] * 255.0);
|
blue = ROUND(_components[_blueComponent] * 255.0);
|
||||||
|
|
||||||
var max = MAX(red, green, blue),
|
var max = MAX(red, green, blue),
|
||||||
min = MIN(red, green, blue),
|
min = MIN(red, green, blue),
|
||||||
delta = max - min;
|
delta = max - min;
|
||||||
|
|
||||||
var brightness = max / 255.0,
|
var brightness = max / 255.0,
|
||||||
saturation = (max != 0) ? delta / max : 0;
|
saturation = (max != 0) ? delta / max : 0;
|
||||||
|
|
||||||
var hue;
|
var hue;
|
||||||
|
if(saturation == 0)
|
||||||
if (saturation == 0)
|
|
||||||
{
|
|
||||||
hue = 0;
|
hue = 0;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var rr = (max - red) / delta,
|
var rr = (max - red) / delta;
|
||||||
gr = (max - green) / delta,
|
var gr = (max - green) / delta;
|
||||||
br = (max - blue) / delta;
|
var br = (max - blue) / delta;
|
||||||
|
|
||||||
if (red == max)
|
if (red == max)
|
||||||
hue = br - gr;
|
hue = br - gr;
|
||||||
else if (green == max)
|
else if (green == max)
|
||||||
hue = 2 + rr - br;
|
hue = 2 + rr - br;
|
||||||
else
|
else
|
||||||
hue = 4 + gr - rr;
|
hue = 4 + gr - rr;
|
||||||
|
|
||||||
hue /= 6;
|
hue /= 6;
|
||||||
if (hue < 0)
|
if (hue < 0)
|
||||||
hue++;
|
hue++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
ROUND(hue * 360.0),
|
ROUND(hue * 360.0),
|
||||||
ROUND(saturation * 100.0),
|
ROUND(saturation * 100.0),
|
||||||
ROUND(brightness * 100.0)
|
ROUND(brightness * 100.0)
|
||||||
];
|
];
|
||||||
@@ -628,7 +594,7 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
|||||||
|
|
||||||
@implementation CPColor (CoreGraphicsExtensions)
|
@implementation CPColor (CoreGraphicsExtensions)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Set's the receiver to be the fill and stroke color in the current graphics context
|
Set's the receiver to be the fill and stroke color in the current graphics context
|
||||||
*/
|
*/
|
||||||
- (void)set
|
- (void)set
|
||||||
@@ -637,16 +603,16 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
|||||||
[self setStroke];
|
[self setStroke];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Set's the receiver to be the fill color in the current graphics context
|
Set's the receiver to be the fill color in the current graphics context
|
||||||
*/
|
*/
|
||||||
- (void)setFill
|
- (void)setFill
|
||||||
{
|
{
|
||||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||||
CGContextSetFillColor(ctx, self);
|
CGContextSetFillColor(ctx, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Set's the receiver to be the stroke color in the current graphics context
|
Set's the receiver to be the stroke color in the current graphics context
|
||||||
*/
|
*/
|
||||||
- (void)setStroke
|
- (void)setStroke
|
||||||
@@ -657,15 +623,6 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPColor (Debugging)
|
|
||||||
|
|
||||||
+ (CPColor)randomColor
|
|
||||||
{
|
|
||||||
return [CPColor colorWithRed:RAND() green:RAND() blue:RAND() alpha:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPColorComponentsKey = @"CPColorComponentsKey",
|
var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||||
CPColorPatternImageKey = @"CPColorPatternImageKey";
|
CPColorPatternImageKey = @"CPColorPatternImageKey";
|
||||||
|
|
||||||
@@ -679,7 +636,7 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
|||||||
{
|
{
|
||||||
if ([aCoder containsValueForKey:CPColorPatternImageKey])
|
if ([aCoder containsValueForKey:CPColorPatternImageKey])
|
||||||
return [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
|
return [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
|
||||||
|
|
||||||
return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
|
return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,48 +654,59 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
var hexCharacters = "0123456789ABCDEF";
|
var hexCharacters = "0123456789ABCDEF";
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Used for the CPColor \c +colorWithHexString: implementation
|
Used for the CPColor <code>colorWithHexString:</code> implementation
|
||||||
@ignore
|
@ignore
|
||||||
@class CPColor
|
@class CPColor
|
||||||
@return an array of rgb components
|
@return an array of rgb components
|
||||||
*/
|
*/
|
||||||
var hexToRGB = function(hex)
|
function hexToRGB(hex)
|
||||||
{
|
{
|
||||||
if (hex.length == 3)
|
if ( hex.length == 3 )
|
||||||
hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
|
hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
|
||||||
|
if(hex.length != 6)
|
||||||
if (hex.length != 6)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
hex = hex.toUpperCase();
|
hex = hex.toUpperCase();
|
||||||
|
|
||||||
for (var i = 0; i < hex.length; i++)
|
for(var i=0; i<hex.length; i++)
|
||||||
if (hexCharacters.indexOf(hex.charAt(i)) == -1)
|
if(hexCharacters.indexOf(hex.charAt(i)) == -1)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var red = (hexCharacters.indexOf(hex.charAt(0)) * 16 + hexCharacters.indexOf(hex.charAt(1))) / 255.0,
|
var red = (hexCharacters.indexOf(hex.charAt(0)) * 16 + hexCharacters.indexOf(hex.charAt(1))) / 255.0;
|
||||||
green = (hexCharacters.indexOf(hex.charAt(2)) * 16 + hexCharacters.indexOf(hex.charAt(3))) / 255.0,
|
var green = (hexCharacters.indexOf(hex.charAt(2)) * 16 + hexCharacters.indexOf(hex.charAt(3))) / 255.0;
|
||||||
blue = (hexCharacters.indexOf(hex.charAt(4)) * 16 + hexCharacters.indexOf(hex.charAt(5))) / 255.0;
|
var blue = (hexCharacters.indexOf(hex.charAt(4)) * 16 + hexCharacters.indexOf(hex.charAt(5))) / 255.0;
|
||||||
|
|
||||||
return [red, green, blue, 1.0];
|
return [red, green, blue, 1.0];
|
||||||
};
|
}
|
||||||
|
|
||||||
var rgbToHex = function(r,g,b)
|
function integerToBytes(integer, length) {
|
||||||
{
|
if (!length)
|
||||||
|
length = (integer == 0) ? 1 : Math.round((Math.log(integer)/Math.log(2))/8+0.5);
|
||||||
|
|
||||||
|
var bytes = new Array(length);
|
||||||
|
for (var i = length-1; i >= 0; i--) {
|
||||||
|
bytes[i] = integer & 255;
|
||||||
|
integer = integer >> 8
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rgbToHex(r,g,b) {
|
||||||
return byteToHex(r) + byteToHex(g) + byteToHex(b);
|
return byteToHex(r) + byteToHex(g) + byteToHex(b);
|
||||||
};
|
}
|
||||||
|
|
||||||
var byteToHex = function(n)
|
function byteToHex(n) {
|
||||||
{
|
if (!n || isNaN(n)) return "00";
|
||||||
if (!n || isNaN(n))
|
n = ROUND(MIN(255,MAX(0,256*n)));
|
||||||
return "00";
|
return hexCharacters.charAt((n - n % 16) / 16) +
|
||||||
|
hexCharacters.charAt(n % 16);
|
||||||
|
}
|
||||||
|
|
||||||
n = FLOOR(MIN(255, MAX(0, 256 * n)));
|
// Toll-Free bridge CPColor to CGColor.
|
||||||
|
//CGColor.prototype.isa = CPColor;
|
||||||
|
//[CPColor initialize];
|
||||||
|
|
||||||
return hexCharacters.charAt((n - n % 16) / 16) +
|
//http://dev.mootools.net/browser/trunk/Source/Utilities/Color.js?rev=1184
|
||||||
hexCharacters.charAt(n % 16);
|
|
||||||
};
|
|
||||||
|
|||||||
+90
-94
@@ -20,13 +20,11 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPApplication.j"
|
|
||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
@import "CPCookie.j"
|
@import "CPCookie.j"
|
||||||
@import "CPPanel.j"
|
@import "CPPanel.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
|
|
||||||
CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification";
|
CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification";
|
||||||
|
|
||||||
var PREVIEW_HEIGHT = 20.0,
|
var PREVIEW_HEIGHT = 20.0,
|
||||||
@@ -37,7 +35,7 @@ var PREVIEW_HEIGHT = 20.0,
|
|||||||
|
|
||||||
var SharedColorPanel = nil,
|
var SharedColorPanel = nil,
|
||||||
ColorPickerClasses = [];
|
ColorPickerClasses = [];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A color wheel
|
A color wheel
|
||||||
@global
|
@global
|
||||||
@@ -54,16 +52,16 @@ CPSliderColorPickerMode = 2;
|
|||||||
CPColorPickerViewWidth = 265,
|
CPColorPickerViewWidth = 265,
|
||||||
CPColorPickerViewHeight = 370;
|
CPColorPickerViewHeight = 370;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPColorPanel
|
@class CPColorPanel
|
||||||
|
|
||||||
CPColorPanel provides a reusable panel that can be used
|
CPColorPanel provides a reusable panel that can be used
|
||||||
displayed on screen to prompt the user for a color selection. To
|
displayed on screen to prompt the user for a color selection. To
|
||||||
obtain the panel, call the \c +sharedColorPanel method.
|
obtain the panel, call the <code>sharedColorPanel</code> method.
|
||||||
*/
|
*/
|
||||||
@implementation CPColorPanel : CPPanel
|
@implementation CPColorPanel : CPPanel
|
||||||
{
|
{
|
||||||
_CPColorPanelToolbar _toolbar;
|
_CPColorPanelToolbar _toolbar;
|
||||||
_CPColorPanelSwatches _swatchView;
|
_CPColorPanelSwatches _swatchView;
|
||||||
_CPColorPanelPreview _previewView;
|
_CPColorPanelPreview _previewView;
|
||||||
@@ -82,8 +80,8 @@ CPColorPickerViewHeight = 370;
|
|||||||
int _mode;
|
int _mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
A list of color pickers is collected here, and any color panel created will contain
|
A list of color pickers is collected here, and any color panel created will contain
|
||||||
any picker in this list up to this point. In other words, call before creating a color panel.
|
any picker in this list up to this point. In other words, call before creating a color panel.
|
||||||
*/
|
*/
|
||||||
+ (void)provideColorPickerClass:(Class)aColorPickerSubclass
|
+ (void)provideColorPickerClass:(Class)aColorPickerSubclass
|
||||||
@@ -98,7 +96,7 @@ CPColorPickerViewHeight = 370;
|
|||||||
{
|
{
|
||||||
if (!SharedColorPanel)
|
if (!SharedColorPanel)
|
||||||
SharedColorPanel = [[CPColorPanel alloc] init];
|
SharedColorPanel = [[CPColorPanel alloc] init];
|
||||||
|
|
||||||
return SharedColorPanel;
|
return SharedColorPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,16 +107,16 @@ CPColorPickerViewHeight = 370;
|
|||||||
+ (void)setPickerMode:(CPColorPanelMode)mode
|
+ (void)setPickerMode:(CPColorPanelMode)mode
|
||||||
{
|
{
|
||||||
var panel = [CPColorPanel sharedColorPanel];
|
var panel = [CPColorPanel sharedColorPanel];
|
||||||
[panel setMode:mode];
|
[panel setMode: mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
To obtain the color panel, use \c +sharedColorPanel.
|
To obtain the color panel, use <code>sharedColorPanel</code>.
|
||||||
@ignore
|
@ignore
|
||||||
*/
|
*/
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super initWithContentRect:CGRectMake(500.0, 50.0, 219.0, 370.0)
|
self = [super initWithContentRect:CGRectMake(500.0, 50.0, 219.0, 370.0)
|
||||||
styleMask:(CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask)];
|
styleMask:(CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask)];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
@@ -127,30 +125,30 @@ CPColorPickerViewHeight = 370;
|
|||||||
|
|
||||||
[self setTitle:@"Color Panel"];
|
[self setTitle:@"Color Panel"];
|
||||||
[self setLevel:CPFloatingWindowLevel];
|
[self setLevel:CPFloatingWindowLevel];
|
||||||
|
|
||||||
[self setFloatingPanel:YES];
|
[self setFloatingPanel:YES];
|
||||||
[self setBecomesKeyOnlyIfNeeded:YES];
|
[self setBecomesKeyOnlyIfNeeded:YES];
|
||||||
|
|
||||||
[self setMinSize:CGSizeMake(219.0, 342.0)];
|
[self setMinSize:CGSizeMake(219.0, 342.0)];
|
||||||
[self setMaxSize:CGSizeMake(323.0, 537.0)];
|
[self setMaxSize:CGSizeMake(323.0, 537.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the color of the panel, and updates the picker. Also posts a \c CPColorPanelDidChangeNotification.
|
Sets the color of the panel, and updates the picker. Also posts a <code>CPColorPanelDidChangeNotification</code>.
|
||||||
*/
|
*/
|
||||||
- (void)setColor:(CPColor)aColor
|
- (void)setColor:(CPColor)aColor
|
||||||
{
|
{
|
||||||
_color = aColor;
|
_color = aColor;
|
||||||
[_previewView setBackgroundColor: _color];
|
[_previewView setBackgroundColor: _color];
|
||||||
|
|
||||||
[CPApp sendAction:@selector(changeColor:) to:nil from:self];
|
[CPApp sendAction:@selector(changeColor:) to:nil from:self];
|
||||||
|
|
||||||
if (_target && _action)
|
if (_target && _action)
|
||||||
[CPApp sendAction:_action to:_target from:self];
|
[CPApp sendAction:_action to:_target from:self];
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
postNotificationName:CPColorPanelColorDidChangeNotification
|
postNotificationName:CPColorPanelColorDidChangeNotification
|
||||||
object:self];
|
object:self];
|
||||||
@@ -167,7 +165,7 @@ CPColorPickerViewHeight = 370;
|
|||||||
- (void)setColor:(CPColor)aColor updatePicker:(BOOL)bool
|
- (void)setColor:(CPColor)aColor updatePicker:(BOOL)bool
|
||||||
{
|
{
|
||||||
[self setColor:aColor];
|
[self setColor:aColor];
|
||||||
|
|
||||||
if (bool)
|
if (bool)
|
||||||
[_activePicker setColor:_color];
|
[_activePicker setColor:_color];
|
||||||
}
|
}
|
||||||
@@ -234,30 +232,30 @@ CPColorPickerViewHeight = 370;
|
|||||||
{
|
{
|
||||||
var picker = _colorPickers[[sender tag]],
|
var picker = _colorPickers[[sender tag]],
|
||||||
view = [picker provideNewView:NO];
|
view = [picker provideNewView:NO];
|
||||||
|
|
||||||
if (!view)
|
if (!view)
|
||||||
view = [picker provideNewView:YES];
|
view = [picker provideNewView:YES];
|
||||||
|
|
||||||
if (view == _currentView)
|
if (view == _currentView)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_currentView)
|
if (_currentView)
|
||||||
[view setFrame:[_currentView frame]];
|
[view setFrame:[_currentView frame]];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var height = (TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5 + SWATCH_HEIGHT + 32),
|
var height = (TOOLBAR_HEIGHT+10+PREVIEW_HEIGHT+5+SWATCH_HEIGHT+32),
|
||||||
bounds = [[self contentView] bounds];
|
bounds = [[self contentView] bounds];
|
||||||
|
|
||||||
[view setFrameSize:CPSizeMake(bounds.size.width - 10, bounds.size.height - height)];
|
[view setFrameSize: CPSizeMake(bounds.size.width - 10, bounds.size.height - height)];
|
||||||
[view setFrameOrigin:CPPointMake(5, height)];
|
[view setFrameOrigin: CPPointMake(5, height)];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_currentView removeFromSuperview];
|
[_currentView removeFromSuperview];
|
||||||
[[self contentView] addSubview:view];
|
[[self contentView] addSubview:view];
|
||||||
|
|
||||||
_currentView = view;
|
_currentView = view;
|
||||||
_activePicker = picker;
|
_activePicker = picker;
|
||||||
|
|
||||||
[picker setColor:[self color]];
|
[picker setColor:[self color]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +285,7 @@ CPColorPickerViewHeight = 370;
|
|||||||
_colorPickers = [];
|
_colorPickers = [];
|
||||||
|
|
||||||
var count = [ColorPickerClasses count];
|
var count = [ColorPickerClasses count];
|
||||||
for (var i = 0; i < count; i++)
|
for (var i=0; i<count; i++)
|
||||||
{
|
{
|
||||||
var currentPickerClass = ColorPickerClasses[i],
|
var currentPickerClass = ColorPickerClasses[i],
|
||||||
currentPicker = [[currentPickerClass alloc] initWithPickerMask:0 colorPanel:self];
|
currentPicker = [[currentPickerClass alloc] initWithPickerMask:0 colorPanel:self];
|
||||||
@@ -299,29 +297,29 @@ CPColorPickerViewHeight = 370;
|
|||||||
bounds = [contentView bounds];
|
bounds = [contentView bounds];
|
||||||
|
|
||||||
_toolbar = [[CPView alloc] initWithFrame:CGRectMake(0, 6, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)];
|
_toolbar = [[CPView alloc] initWithFrame:CGRectMake(0, 6, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)];
|
||||||
[_toolbar setAutoresizingMask: CPViewWidthSizable];
|
[_toolbar setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
var totalToolbarWidth = count * ICON_WIDTH + (count - 1) * ICON_PADDING,
|
var totalToolbarWidth = count * ICON_WIDTH + (count - 1) * ICON_PADDING,
|
||||||
leftOffset = (CGRectGetWidth(bounds) - totalToolbarWidth) / 2.0,
|
leftOffset = (CGRectGetWidth(bounds) - totalToolbarWidth) / 2.0,
|
||||||
buttonForLater = nil;
|
buttonForLater = nil;
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
for (var i=0; i<count; i++)
|
||||||
{
|
{
|
||||||
var image = [_colorPickers[i] provideNewButtonImage],
|
var image = [_colorPickers[i] provideNewButtonImage],
|
||||||
highlightImage = [_colorPickers[i] provideNewAlternateButtonImage],
|
highlightImage = [_colorPickers[i] provideNewAlternateButtonImage],
|
||||||
button = [[CPButton alloc] initWithFrame:CGRectMake(leftOffset + i * (ICON_WIDTH + ICON_PADDING), 0, ICON_WIDTH, ICON_WIDTH)];
|
button = [[CPButton alloc] initWithFrame:CGRectMake(leftOffset + i*(ICON_WIDTH+ICON_PADDING), 0, ICON_WIDTH, ICON_WIDTH)];
|
||||||
|
|
||||||
[button setTag:i];
|
[button setTag:i];
|
||||||
[button setTarget:self];
|
[button setTarget:self];
|
||||||
[button setAction:@selector(_setPicker:)];
|
[button setAction:@selector(_setPicker:)];
|
||||||
[button setBordered:NO];
|
[button setBordered:NO];
|
||||||
[button setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
|
[button setAutoresizingMask:CPViewMinXMargin|CPViewMaxXMargin];
|
||||||
|
|
||||||
[button setImage:image];
|
[button setImage:image];
|
||||||
[button setAlternateImage:highlightImage];
|
[button setAlternateImage:highlightImage];
|
||||||
|
|
||||||
[_toolbar addSubview:button];
|
[_toolbar addSubview:button];
|
||||||
|
|
||||||
if (!buttonForLater)
|
if (!buttonForLater)
|
||||||
buttonForLater = button;
|
buttonForLater = button;
|
||||||
}
|
}
|
||||||
@@ -332,7 +330,7 @@ CPColorPickerViewHeight = 370;
|
|||||||
_previewView = [[_CPColorPanelPreview alloc] initWithFrame:CGRectInset([previewBox bounds], 2.0, 2.0)];
|
_previewView = [[_CPColorPanelPreview alloc] initWithFrame:CGRectInset([previewBox bounds], 2.0, 2.0)];
|
||||||
|
|
||||||
[_previewView setColorPanel:self];
|
[_previewView setColorPanel:self];
|
||||||
[_previewView setAutoresizingMask:CPViewWidthSizable];
|
[_previewView setAutoresizingMask:CPViewWidthSizable];
|
||||||
|
|
||||||
[previewBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]];
|
[previewBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]];
|
||||||
[previewBox setAutoresizingMask:CPViewWidthSizable];
|
[previewBox setAutoresizingMask:CPViewWidthSizable];
|
||||||
@@ -340,7 +338,7 @@ CPColorPickerViewHeight = 370;
|
|||||||
[previewBox addSubview:_previewView];
|
[previewBox addSubview:_previewView];
|
||||||
|
|
||||||
var _previewLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 10, 60, 15)];
|
var _previewLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 10, 60, 15)];
|
||||||
[_previewLabel setStringValue:"Preview:"];
|
[_previewLabel setStringValue: "Preview:"];
|
||||||
[_previewLabel setTextColor:[CPColor blackColor]];
|
[_previewLabel setTextColor:[CPColor blackColor]];
|
||||||
[_previewLabel setAlignment:CPRightTextAlignment];
|
[_previewLabel setAlignment:CPRightTextAlignment];
|
||||||
|
|
||||||
@@ -352,8 +350,8 @@ CPColorPickerViewHeight = 370;
|
|||||||
|
|
||||||
_swatchView = [[_CPColorPanelSwatches alloc] initWithFrame:CGRectInset([swatchBox bounds], 1.0, 1.0)];
|
_swatchView = [[_CPColorPanelSwatches alloc] initWithFrame:CGRectInset([swatchBox bounds], 1.0, 1.0)];
|
||||||
|
|
||||||
[_swatchView setColorPanel:self];
|
[_swatchView setColorPanel: self];
|
||||||
[_swatchView setAutoresizingMask:CPViewWidthSizable];
|
[_swatchView setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
[swatchBox addSubview:_swatchView];
|
[swatchBox addSubview:_swatchView];
|
||||||
|
|
||||||
@@ -369,7 +367,7 @@ CPColorPickerViewHeight = 370;
|
|||||||
[opacityLabel setAlignment:CPRightTextAlignment];
|
[opacityLabel setAlignment:CPRightTextAlignment];
|
||||||
|
|
||||||
_opacitySlider = [[CPSlider alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 34, CGRectGetWidth(bounds) - 86, 20.0)];
|
_opacitySlider = [[CPSlider alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 34, CGRectGetWidth(bounds) - 86, 20.0)];
|
||||||
|
|
||||||
[_opacitySlider setMinValue:0.0];
|
[_opacitySlider setMinValue:0.0];
|
||||||
[_opacitySlider setMaxValue:1.0];
|
[_opacitySlider setMaxValue:1.0];
|
||||||
|
|
||||||
@@ -417,37 +415,37 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
CPCookie _swatchCookie;
|
CPCookie _swatchCookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CPRect)aFrame
|
-(id)initWithFrame:(CPRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
[self setBackgroundColor: [CPColor grayColor]];
|
[self setBackgroundColor: [CPColor grayColor]];
|
||||||
|
|
||||||
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
|
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
|
||||||
|
|
||||||
var whiteColor = [CPColor whiteColor];
|
var whiteColor = [CPColor whiteColor];
|
||||||
|
|
||||||
_swatchCookie = [[CPCookie alloc] initWithName: CPColorPanelSwatchesCookie];
|
_swatchCookie = [[CPCookie alloc] initWithName: CPColorPanelSwatchesCookie];
|
||||||
var colorList = [self startingColorList];
|
var colorList = [self startingColorList];
|
||||||
|
|
||||||
_swatches = [];
|
_swatches = [];
|
||||||
|
|
||||||
for (var i = 0; i < 50; i++)
|
for(var i=0; i < 50; i++)
|
||||||
{
|
{
|
||||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||||
var view = [[CPView alloc] initWithFrame: CPRectMake(13 * i + 1, 1, 12, 12)],
|
var view = [[CPView alloc] initWithFrame: CPRectMake(13*i+1, 1, 12, 12)],
|
||||||
fillView = [[CPView alloc] initWithFrame:CGRectInset([view bounds], 1.0, 1.0)];
|
fillView = [[CPView alloc] initWithFrame:CGRectInset([view bounds], 1.0, 1.0)];
|
||||||
|
|
||||||
[view setBackgroundColor:whiteColor];
|
[view setBackgroundColor:whiteColor];
|
||||||
[fillView setBackgroundColor: (i < colorList.length) ? colorList[i] : whiteColor];
|
[fillView setBackgroundColor: (i < colorList.length) ? colorList[i] : whiteColor];
|
||||||
|
|
||||||
[view addSubview:fillView];
|
[view addSubview:fillView];
|
||||||
|
|
||||||
[self addSubview: view];
|
[self addSubview: view];
|
||||||
|
|
||||||
_swatches.push(view);
|
_swatches.push(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,8 +457,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
- (CPArray)startingColorList
|
- (CPArray)startingColorList
|
||||||
{
|
{
|
||||||
var cookieValue = [_swatchCookie value];
|
var cookieValue = [_swatchCookie value];
|
||||||
|
if(cookieValue == "")
|
||||||
if (cookieValue == "")
|
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[CPColor blackColor],
|
[CPColor blackColor],
|
||||||
@@ -474,11 +471,11 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
[CPColor yellowColor]
|
[CPColor yellowColor]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
var cookieValue = eval(cookieValue),
|
var cookieValue = eval(cookieValue);
|
||||||
result = [];
|
var result = [];
|
||||||
|
|
||||||
for (var i = 0; i < cookieValue.length; i++)
|
for(var i=0; i<cookieValue.length; i++)
|
||||||
result.push([CPColor colorWithHexString: cookieValue[i]]);
|
result.push([CPColor colorWithHexString: cookieValue[i]]);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -488,12 +485,12 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
{
|
{
|
||||||
var result = [];
|
var result = [];
|
||||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||||
for (var i = 0; i < _swatches.length; i++)
|
for(var i=0; i<_swatches.length; i++)
|
||||||
result.push([[[_swatches[i] subviews][0] backgroundColor] hexString]);
|
result.push([[[_swatches[i] subviews][0] backgroundColor] hexString]);
|
||||||
|
|
||||||
var future = new Date();
|
var future = new Date();
|
||||||
future.setYear(2019);
|
future.setYear(2019);
|
||||||
|
|
||||||
[_swatchCookie setValue: JSON.stringify(result) expires:future domain: nil];
|
[_swatchCookie setValue: JSON.stringify(result) expires:future domain: nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,10 +518,9 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
|
|
||||||
- (void)mouseUp:(CPEvent)anEvent
|
- (void)mouseUp:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
bounds = [self bounds];
|
|
||||||
|
if(point.x > [self bounds].size.width - 1 || point.x < 1)
|
||||||
if (!CGRectContainsPoint(bounds, point) || point.x > [self bounds].size.width - 1 || point.x < 1)
|
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[_colorPanel setColor: [self colorAtIndex:FLOOR(point.x / 13)] updatePicker: YES];
|
[_colorPanel setColor: [self colorAtIndex:FLOOR(point.x / 13)] updatePicker: YES];
|
||||||
@@ -538,21 +534,21 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
||||||
|
|
||||||
var swatch = _swatches[FLOOR(point.x / 13)];
|
var swatch = _swatches[FLOOR(point.x / 13)];
|
||||||
|
|
||||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||||
_dragColor = [[swatch subviews][0] backgroundColor];
|
_dragColor = [[swatch subviews][0] backgroundColor];
|
||||||
|
|
||||||
var bounds = CPRectCreateCopy([swatch bounds]);
|
var bounds = CPRectCreateCopy([swatch bounds]);
|
||||||
|
|
||||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||||
var dragView = [[CPView alloc] initWithFrame: bounds],
|
var dragView = [[CPView alloc] initWithFrame: bounds];
|
||||||
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
|
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
|
||||||
|
|
||||||
[dragView setBackgroundColor:[CPColor blackColor]];
|
[dragView setBackgroundColor:[CPColor blackColor]];
|
||||||
[dragFillView setBackgroundColor:_dragColor];
|
[dragFillView setBackgroundColor:_dragColor];
|
||||||
|
|
||||||
[dragView addSubview:dragFillView];
|
[dragView addSubview:dragFillView];
|
||||||
|
|
||||||
[self dragView: dragView
|
[self dragView: dragView
|
||||||
@@ -566,20 +562,20 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
|
|
||||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||||
{
|
{
|
||||||
if (aType == CPColorDragType)
|
if(aType == CPColorDragType)
|
||||||
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:aType];
|
[aPasteboard setData:_dragColor forType:aType];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)performDragOperation:(id <CPDraggingInfo>)aSender
|
- (void)performDragOperation:(id <CPDraggingInfo>)aSender
|
||||||
{
|
{
|
||||||
var location = [self convertPoint:[aSender draggingLocation] fromView:nil],
|
var location = [self convertPoint:[aSender draggingLocation] fromView:nil],
|
||||||
pasteboard = [aSender draggingPasteboard],
|
pasteboard = [aSender draggingPasteboard],
|
||||||
swatch = nil;
|
swatch = nil;
|
||||||
|
|
||||||
if (![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1)
|
if(![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1)
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex: FLOOR(location.x / 13)];
|
[self setColor:[pasteboard dataForType:CPColorDragType] atIndex: FLOOR(location.x / 13)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -593,9 +589,9 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
- (id)initWithFrame:(CPRect)aFrame
|
- (id)initWithFrame:(CPRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
|
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,11 +609,11 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
{
|
{
|
||||||
var pasteboard = [aSender draggingPasteboard];
|
var pasteboard = [aSender draggingPasteboard];
|
||||||
|
|
||||||
if (![pasteboard availableTypeFromArray:[CPColorDragType]])
|
if(![pasteboard availableTypeFromArray:[CPColorDragType]])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
var color = [CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]];
|
var color = [pasteboard dataForType:CPColorDragType];
|
||||||
[_colorPanel setColor:color updatePicker:YES];
|
[_colorPanel setColor: color updatePicker: YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isOpaque
|
- (BOOL)isOpaque
|
||||||
@@ -629,19 +625,19 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
{
|
{
|
||||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
|
|
||||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
|
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
||||||
|
|
||||||
var bounds = CPRectMake(0, 0, 15, 15);
|
var bounds = CPRectMake(0, 0, 15, 15);
|
||||||
|
|
||||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||||
var dragView = [[CPView alloc] initWithFrame: bounds],
|
var dragView = [[CPView alloc] initWithFrame: bounds];
|
||||||
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
|
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
|
||||||
|
|
||||||
[dragView setBackgroundColor:[CPColor blackColor]];
|
[dragView setBackgroundColor:[CPColor blackColor]];
|
||||||
[dragFillView setBackgroundColor:[self backgroundColor]];
|
[dragFillView setBackgroundColor:[self backgroundColor]];
|
||||||
|
|
||||||
[dragView addSubview:dragFillView];
|
[dragView addSubview:dragFillView];
|
||||||
|
|
||||||
[self dragView: dragView
|
[self dragView: dragView
|
||||||
at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
|
at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
|
||||||
offset: CPPointMake(0.0, 0.0)
|
offset: CPPointMake(0.0, 0.0)
|
||||||
@@ -653,8 +649,8 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
|
|
||||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||||
{
|
{
|
||||||
if (aType == CPColorDragType)
|
if(aType == CPColorDragType)
|
||||||
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:aType];
|
[aPasteboard setData:[self backgroundColor] forType:aType];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+40
-41
@@ -21,15 +21,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
@import "CPColorPanel.j"
|
||||||
|
|
||||||
@import "CPView.j"
|
/*!
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPColorPicker
|
@class 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.
|
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.
|
||||||
*/
|
*/
|
||||||
@implementation CPColorPicker : CPObject
|
@implementation CPColorPicker : CPObject
|
||||||
{
|
{
|
||||||
@@ -62,7 +60,7 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
FIXME Not implemented.
|
FIXME Not implemented.
|
||||||
@return \c nil
|
@return <code>nil</code>
|
||||||
@ignore
|
@ignore
|
||||||
*/
|
*/
|
||||||
- (CPImage)provideNewButtonImage
|
- (CPImage)provideNewButtonImage
|
||||||
@@ -102,18 +100,18 @@
|
|||||||
|
|
||||||
CPColor _cachedColor;
|
CPColor _cachedColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
||||||
{
|
{
|
||||||
return [super initWithPickerMask:mask colorPanel: owningColorPanel];
|
return [super initWithPickerMask:mask colorPanel: owningColorPanel];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initView
|
-(id)initView
|
||||||
{
|
{
|
||||||
aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
||||||
|
|
||||||
_pickerView = [[CPView alloc] initWithFrame:aFrame];
|
_pickerView = [[CPView alloc] initWithFrame:aFrame];
|
||||||
[_pickerView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[_pickerView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||||
|
|
||||||
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)];
|
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)];
|
||||||
|
|
||||||
@@ -136,17 +134,17 @@
|
|||||||
[_pickerView addSubview:_brightnessSlider];
|
[_pickerView addSubview:_brightnessSlider];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)brightnessSliderDidChange:(id)sender
|
-(void)brightnessSliderDidChange:(id)sender
|
||||||
{
|
{
|
||||||
[self updateColor];
|
[self updateColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)colorWheelDidChange:(id)sender
|
-(void)colorWheelDidChange:(id)sender
|
||||||
{
|
{
|
||||||
[self updateColor];
|
[self updateColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateColor
|
-(void)updateColor
|
||||||
{
|
{
|
||||||
var hue = [_hueSaturationView angle],
|
var hue = [_hueSaturationView angle],
|
||||||
saturation = [_hueSaturationView distance],
|
saturation = [_hueSaturationView distance],
|
||||||
@@ -223,7 +221,7 @@
|
|||||||
float _radius;
|
float _radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CPRect)aFrame
|
-(id)initWithFrame:(CPRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
@@ -241,10 +239,8 @@
|
|||||||
_blackWheelImage.style.filter = "alpha(opacity=0)"
|
_blackWheelImage.style.filter = "alpha(opacity=0)"
|
||||||
_blackWheelImage.style.position = "absolute";
|
_blackWheelImage.style.position = "absolute";
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
_DOMElement.appendChild(_wheelImage);
|
_DOMElement.appendChild(_wheelImage);
|
||||||
_DOMElement.appendChild(_blackWheelImage);
|
_DOMElement.appendChild(_blackWheelImage);
|
||||||
#endif
|
|
||||||
|
|
||||||
[self setWheelSize:aFrame.size];
|
[self setWheelSize:aFrame.size];
|
||||||
|
|
||||||
@@ -261,19 +257,19 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setWheelBrightness:(float)brightness
|
-(void)setWheelBrightness:(float)brightness
|
||||||
{
|
{
|
||||||
_blackWheelImage.style.opacity = 1.0 - brightness;
|
_blackWheelImage.style.opacity = 1.0 - brightness;
|
||||||
_blackWheelImage.style.filter = "alpha(opacity=" + (1.0 - brightness) * 100 + ")"
|
_blackWheelImage.style.filter = "alpha(opacity=" + (1.0 - brightness)*100 + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFrameSize:(CPSize)aSize
|
-(void)setFrameSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
[super setFrameSize:aSize];
|
[super setFrameSize:aSize];
|
||||||
[self setWheelSize:aSize];
|
[self setWheelSize:aSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setWheelSize:(CPSize)aSize
|
-(void)setWheelSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
var min = MIN(aSize.width, aSize.height);
|
var min = MIN(aSize.width, aSize.height);
|
||||||
|
|
||||||
@@ -296,55 +292,57 @@
|
|||||||
[self setAngle:[self degreesToRadians:_angle] distance:(_distance / 100.0) * _radius];
|
[self setAngle:[self degreesToRadians:_angle] distance:(_distance / 100.0) * _radius];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDelegate:(id)aDelegate
|
-(void)setDelegate:(id)aDelegate
|
||||||
{
|
{
|
||||||
_delegate = aDelegate;
|
_delegate = aDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)delegate
|
-(id)delegate
|
||||||
{
|
{
|
||||||
return _delegate;
|
return _delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)angle
|
-(float)angle
|
||||||
{
|
{
|
||||||
return _angle;
|
return _angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)distance
|
-(float)distance
|
||||||
{
|
{
|
||||||
return _distance;
|
return _distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
-(void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
[self reposition:anEvent];
|
[self reposition:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDragged:(CPEvent)anEvent
|
-(void)mouseDragged:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
[self reposition:anEvent];
|
[self reposition:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reposition:(CPEvent)anEvent
|
-(void)reposition:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var bounds = [self bounds],
|
var bounds = [self bounds],
|
||||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
midX = CGRectGetMidX(bounds),
|
|
||||||
midY = CGRectGetMidY(bounds),
|
var midX = CGRectGetMidX(bounds);
|
||||||
distance = MIN(SQRT((location.x - midX) * (location.x - midX) + (location.y - midY) * (location.y - midY)), _radius),
|
var midY = CGRectGetMidY(bounds);
|
||||||
angle = ATAN2(location.y - midY, location.x - midX);
|
|
||||||
|
var distance = MIN(SQRT((location.x - midX)*(location.x - midX) + (location.y - midY)*(location.y - midY)), _radius);
|
||||||
|
var angle = ATAN2(location.y - midY, location.x - midX);
|
||||||
|
|
||||||
[self setAngle:angle distance:distance];
|
[self setAngle:angle distance:distance];
|
||||||
|
|
||||||
[_delegate colorWheelDidChange:self];
|
[_delegate colorWheelDidChange:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setAngle:(int)angle distance:(float)distance
|
-(void)setAngle:(int)angle distance:(float)distance
|
||||||
{
|
{
|
||||||
var bounds = [self bounds],
|
var bounds = [self bounds];
|
||||||
midX = CGRectGetMidX(bounds),
|
var midX = CGRectGetMidX(bounds);
|
||||||
midY = CGRectGetMidY(bounds);
|
var midY = CGRectGetMidY(bounds);
|
||||||
|
|
||||||
_angle = [self radiansToDegrees:angle];
|
_angle = [self radiansToDegrees:angle];
|
||||||
_distance = (distance / _radius) * 100.0;
|
_distance = (distance / _radius) * 100.0;
|
||||||
@@ -352,22 +350,23 @@
|
|||||||
[_crosshair setFrameOrigin:CPPointMake(COS(angle) * distance + midX - 2.0, SIN(angle) * distance + midY - 2.0)];
|
[_crosshair setFrameOrigin:CPPointMake(COS(angle) * distance + midX - 2.0, SIN(angle) * distance + midY - 2.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPositionToColor:(CPColor)aColor
|
-(void)setPositionToColor:(CPColor)aColor
|
||||||
{
|
{
|
||||||
var hsb = [aColor hsbComponents],
|
var hsb = [aColor hsbComponents],
|
||||||
bounds = [self bounds],
|
bounds = [self bounds];
|
||||||
angle = [self degreesToRadians:hsb[0]],
|
|
||||||
|
var angle = [self degreesToRadians:hsb[0]],
|
||||||
distance = (hsb[1] / 100.0) * _radius;
|
distance = (hsb[1] / 100.0) * _radius;
|
||||||
|
|
||||||
[self setAngle:angle distance:distance];
|
[self setAngle:angle distance:distance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)radiansToDegrees:(float)radians
|
-(int)radiansToDegrees:(float)radians
|
||||||
{
|
{
|
||||||
return ((-radians / PI) * 180 + 360) % 360;
|
return ((-radians / PI) * 180 + 360) % 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)degreesToRadians:(float)degrees
|
-(float)degreesToRadians:(float)degrees
|
||||||
{
|
{
|
||||||
return -(((degrees - 360) / 180) * PI);
|
return -(((degrees - 360) / 180) * PI);
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-41
@@ -29,19 +29,19 @@
|
|||||||
|
|
||||||
var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiveNotification";
|
var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiveNotification";
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPColorWell
|
@class CPColorWell
|
||||||
|
|
||||||
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>
|
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 \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.
|
<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.
|
||||||
*/
|
*/
|
||||||
@implementation CPColorWell : CPControl
|
@implementation CPColorWell : CPControl
|
||||||
{
|
{
|
||||||
BOOL _active;
|
BOOL _active;
|
||||||
BOOL _bordered;
|
BOOL _bordered;
|
||||||
|
|
||||||
CPColor _color;
|
CPColor _color;
|
||||||
CPView _wellView;
|
CPView _wellView;
|
||||||
}
|
}
|
||||||
@@ -49,19 +49,19 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_active = NO;
|
_active = NO;
|
||||||
_bordered = YES;
|
_bordered = YES;
|
||||||
_color = [CPColor whiteColor];
|
_color = [CPColor whiteColor];
|
||||||
|
|
||||||
[self drawBezelWithHighlight:NO];
|
[self drawBezelWithHighlight:NO];
|
||||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||||
|
|
||||||
[self _registerForNotifications];
|
[self _registerForNotifications];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,9 +97,9 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
{
|
{
|
||||||
if (_bordered == bordered)
|
if (_bordered == bordered)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_bordered = bordered;
|
_bordered = bordered;
|
||||||
|
|
||||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,14 +120,14 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
{
|
{
|
||||||
if (_color == aColor)
|
if (_color == aColor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_color = aColor;
|
_color = aColor;
|
||||||
|
|
||||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Changes the color of the well to that of \c aSender.
|
Changes the color of the well to that of <code>aSender</code>.
|
||||||
@param aSender the object from which to retrieve the color
|
@param aSender the object from which to retrieve the color
|
||||||
*/
|
*/
|
||||||
- (void)takeColorFrom:(id)aSender
|
- (void)takeColorFrom:(id)aSender
|
||||||
@@ -137,8 +137,8 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
|
|
||||||
// Activating and Deactivating Color Wells
|
// 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.
|
Activates the color well, displays the color panel, and makes the panel's current color the same as its own.
|
||||||
If exclusive is \c YES, deactivates any other CPColorWells. \c NO, keeps them active.
|
If exclusive is <code>YES</code>, deactivates any other CPColorWells. <code>NO</code>, keeps them active.
|
||||||
@param shouldBeExclusive whether other color wells should be deactivated.
|
@param shouldBeExclusive whether other color wells should be deactivated.
|
||||||
*/
|
*/
|
||||||
- (void)activate:(BOOL)shouldBeExclusive
|
- (void)activate:(BOOL)shouldBeExclusive
|
||||||
@@ -152,9 +152,9 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
|
|
||||||
if ([self isActive])
|
if ([self isActive])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_active = YES;
|
_active = YES;
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
addObserver:self
|
addObserver:self
|
||||||
selector:@selector(colorPanelDidChangeColor:)
|
selector:@selector(colorPanelDidChangeColor:)
|
||||||
@@ -169,9 +169,9 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
{
|
{
|
||||||
if (![self isActive])
|
if (![self isActive])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_active = NO;
|
_active = NO;
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
removeObserver:self
|
removeObserver:self
|
||||||
name:CPColorPanelColorDidChangeNotification
|
name:CPColorPanelColorDidChangeNotification
|
||||||
@@ -179,7 +179,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the color well is active.
|
Returns <code>YES</code> if the color well is active.
|
||||||
*/
|
*/
|
||||||
- (BOOL)isActive
|
- (BOOL)isActive
|
||||||
{
|
{
|
||||||
@@ -202,19 +202,19 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
{
|
{
|
||||||
_wellView = [[CPView alloc] initWithFrame:aRect];
|
_wellView = [[CPView alloc] initWithFrame:aRect];
|
||||||
[_wellView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[_wellView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
|
|
||||||
[self addSubview:_wellView];
|
[self addSubview:_wellView];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[_wellView setFrame:aRect];
|
[_wellView setFrame:aRect];
|
||||||
|
|
||||||
[_wellView setBackgroundColor:_color];
|
[_wellView setBackgroundColor:_color];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)colorPanelDidChangeColor:(CPNotification)aNotification
|
- (void)colorPanelDidChangeColor:(CPNotification)aNotification
|
||||||
{
|
{
|
||||||
[self takeColorFrom:[aNotification object]];
|
[self takeColorFrom:[aNotification object]];
|
||||||
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
[self sendAction:[self action] to:[self target]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,31 +231,25 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
if (![self isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self drawBezelWithHighlight:YES];
|
[self drawBezelWithHighlight:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDragged:(CPEvent)anEvent
|
- (void)mouseDragged:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
if (![self isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self drawBezelWithHighlight:CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil])];
|
[self drawBezelWithHighlight:CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil])];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseUp:(CPEvent)anEvent
|
-(void)mouseUp:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
[self drawBezelWithHighlight:NO];
|
[self drawBezelWithHighlight:NO];
|
||||||
|
|
||||||
if (!CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]) || ![self isEnabled])
|
if (!CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self activate:YES];
|
[self activate:YES];
|
||||||
|
|
||||||
var colorPanel = [CPColorPanel sharedColorPanel];
|
var colorPanel = [CPColorPanel sharedColorPanel];
|
||||||
|
|
||||||
[colorPanel setColor:_color];
|
[colorPanel setColor:_color];
|
||||||
[colorPanel orderFront:self];
|
[colorPanel orderFront:self];
|
||||||
}
|
}
|
||||||
@@ -268,17 +262,17 @@ var CPColorWellColorKey = "CPColorWellColorKey",
|
|||||||
@implementation CPColorWell (CPCoding)
|
@implementation CPColorWell (CPCoding)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Initializes the color well by unarchiving data from \c aCoder.
|
Initializes the color well by unarchiving data from <code>aCoder</code>.
|
||||||
@param aCoder the coder containing the archived CPColorWell.
|
@param aCoder the coder containing the archived CPColorWell.
|
||||||
*/
|
*/
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super initWithCoder:aCoder];
|
self = [super initWithCoder:aCoder];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_active = NO;
|
_active = NO;
|
||||||
_bordered = [aCoder decodeBoolForKey:CPColorWellBorderedKey];
|
_bordered = [aCoder decodeObjectForKey:CPColorWellBorderedKey];
|
||||||
_color = [aCoder decodeObjectForKey:CPColorWellColorKey];
|
_color = [aCoder decodeObjectForKey:CPColorWellColorKey];
|
||||||
|
|
||||||
[self drawBezelWithHighlight:NO];
|
[self drawBezelWithHighlight:NO];
|
||||||
@@ -286,7 +280,7 @@ var CPColorWellColorKey = "CPColorWellColorKey",
|
|||||||
|
|
||||||
[self _registerForNotifications];
|
[self _registerForNotifications];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,17 +290,17 @@ var CPColorWellColorKey = "CPColorWellColorKey",
|
|||||||
*/
|
*/
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
// We do this in order to avoid encoding the _wellView, which
|
// We do this in order to avoid encoding the _wellView, which
|
||||||
// should just automatically be created programmatically as needed.
|
// should just automatically be created programmatically as needed.
|
||||||
var actualSubviews = _subviews;
|
var actualSubviews = _subviews;
|
||||||
|
|
||||||
_subviews = [_subviews copy];
|
_subviews = [_subviews copy];
|
||||||
[_subviews removeObjectIdenticalTo:_wellView];
|
[_subviews removeObjectIdenticalTo:_wellView];
|
||||||
|
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
_subviews = actualSubviews;
|
_subviews = actualSubviews;
|
||||||
|
|
||||||
[aCoder encodeObject:_color forKey:CPColorWellColorKey];
|
[aCoder encodeObject:_color forKey:CPColorWellColorKey];
|
||||||
[aCoder encodeObject:_bordered forKey:CPColorWellBorderedKey];
|
[aCoder encodeObject:_bordered forKey:CPColorWellBorderedKey];
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-71
@@ -21,8 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPEvent.j"
|
@import "CPEvent.j"
|
||||||
@import "CPPlatform.j"
|
|
||||||
|
|
||||||
|
|
||||||
// Browser Engines
|
// Browser Engines
|
||||||
CPUnknownBrowserEngine = 0;
|
CPUnknownBrowserEngine = 0;
|
||||||
@@ -32,28 +30,22 @@ CPKHTMLBrowserEngine = 3;
|
|||||||
CPOperaBrowserEngine = 4;
|
CPOperaBrowserEngine = 4;
|
||||||
CPWebKitBrowserEngine = 5;
|
CPWebKitBrowserEngine = 5;
|
||||||
|
|
||||||
// Operating Systems
|
|
||||||
CPMacOperatingSystem = 0;
|
|
||||||
CPWindowsOperatingSystem = 1;
|
|
||||||
CPOtherOperatingSystem = 2;
|
|
||||||
|
|
||||||
// Features
|
// Features
|
||||||
CPCSSRGBAFeature = 1 << 5;
|
CPCSSRGBAFeature = 1 << 5;
|
||||||
|
|
||||||
CPHTMLCanvasFeature = 1 << 6;
|
CPHTMLCanvasFeature = 1 << 6;
|
||||||
CPHTMLContentEditableFeature = 1 << 7;
|
CPHTMLContentEditableFeature = 1 << 7;
|
||||||
CPHTMLDragAndDropFeature = 1 << 8;
|
|
||||||
|
|
||||||
CPJavascriptInnerTextFeature = 1 << 9;
|
CPJavascriptInnerTextFeature = 1 << 8;
|
||||||
CPJavascriptTextContentFeature = 1 << 10;
|
CPJavascriptTextContentFeature = 1 << 9;
|
||||||
CPJavascriptClipboardEventsFeature = 1 << 11;
|
CPJavascriptClipboardEventsFeature = 1 << 10;
|
||||||
CPJavascriptClipboardAccessFeature = 1 << 12;
|
CPJavascriptClipboardAccessFeature = 1 << 11;
|
||||||
CPJavaScriptCanvasDrawFeature = 1 << 13;
|
CPJavaScriptCanvasDrawFeature = 1 << 12;
|
||||||
CPJavaScriptCanvasTransformFeature = 1 << 14;
|
CPJavaScriptCanvasTransformFeature = 1 << 13;
|
||||||
|
|
||||||
CPVMLFeature = 1 << 15;
|
CPVMLFeature = 1 << 14;
|
||||||
|
|
||||||
CPJavascriptRemedialKeySupport = 1 << 16;
|
CPJavascriptRemedialKeySupport = 1 << 15;
|
||||||
CPJavaScriptShadowFeature = 1 << 20;
|
CPJavaScriptShadowFeature = 1 << 20;
|
||||||
|
|
||||||
CPJavaScriptNegativeMouseWheelValues = 1 << 22;
|
CPJavaScriptNegativeMouseWheelValues = 1 << 22;
|
||||||
@@ -62,8 +54,8 @@ CPJavaScriptMouseWheelValues_8_15 = 1 << 23
|
|||||||
CPOpacityRequiresFilterFeature = 1 << 24;
|
CPOpacityRequiresFilterFeature = 1 << 24;
|
||||||
|
|
||||||
//Internet explorer does not allow dynamically changing the type of an input element
|
//Internet explorer does not allow dynamically changing the type of an input element
|
||||||
CPInputTypeCanBeChangedFeature = 1 << 25;
|
CPInputTypeCanBeChangedFeature = 1 << 25;
|
||||||
CPHTML5DragAndDropSourceYOffBy1 = 1 << 26;
|
|
||||||
|
|
||||||
|
|
||||||
var USER_AGENT = "",
|
var USER_AGENT = "",
|
||||||
@@ -74,27 +66,27 @@ var USER_AGENT = "",
|
|||||||
|
|
||||||
PLATFORM_FEATURES |= CPInputTypeCanBeChangedFeature;
|
PLATFORM_FEATURES |= CPInputTypeCanBeChangedFeature;
|
||||||
|
|
||||||
if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
|
if (typeof window != "undfined" && typeof window.navigator != "undefined")
|
||||||
USER_AGENT = window.navigator.userAgent;
|
USER_AGENT = window.navigator.userAgent;
|
||||||
|
|
||||||
// Opera
|
// Opera
|
||||||
if (typeof window !== "undefined" && window.opera)
|
if (window.opera)
|
||||||
{
|
{
|
||||||
PLATFORM_ENGINE = CPOperaBrowserEngine;
|
PLATFORM_ENGINE = CPOperaBrowserEngine;
|
||||||
|
|
||||||
PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
|
PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internet Explorer
|
// Internet Explorer
|
||||||
else if (typeof window !== "undefined" && window.attachEvent) // Must follow Opera check.
|
else if (window.attachEvent) // Must follow Opera check.
|
||||||
{
|
{
|
||||||
PLATFORM_ENGINE = CPInternetExplorerBrowserEngine;
|
PLATFORM_ENGINE = CPInternetExplorerBrowserEngine;
|
||||||
|
|
||||||
// Features we can only be sure of with IE (no known independent tests)
|
// Features we can only be sure of with IE (no known independent tests)
|
||||||
PLATFORM_FEATURES |= CPVMLFeature;
|
PLATFORM_FEATURES |= CPVMLFeature;
|
||||||
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||||
|
|
||||||
PLATFORM_FEATURES |= CPOpacityRequiresFilterFeature;
|
PLATFORM_FEATURES |= CPOpacityRequiresFilterFeature;
|
||||||
|
|
||||||
PLATFORM_FEATURES &= ~CPInputTypeCanBeChangedFeature;
|
PLATFORM_FEATURES &= ~CPInputTypeCanBeChangedFeature;
|
||||||
@@ -104,35 +96,20 @@ else if (typeof window !== "undefined" && window.attachEvent) // Must follow Ope
|
|||||||
else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||||
{
|
{
|
||||||
PLATFORM_ENGINE = CPWebKitBrowserEngine;
|
PLATFORM_ENGINE = CPWebKitBrowserEngine;
|
||||||
|
|
||||||
// Features we can only be sure of with WebKit (no known independent tests)
|
// Features we can only be sure of with WebKit (no known independent tests)
|
||||||
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||||
PLATFORM_FEATURES |= CPHTMLContentEditableFeature;
|
PLATFORM_FEATURES |= CPHTMLContentEditableFeature;
|
||||||
|
|
||||||
if (USER_AGENT.indexOf("Chrome") === -1)
|
|
||||||
PLATFORM_FEATURES |= CPHTMLDragAndDropFeature;
|
|
||||||
|
|
||||||
PLATFORM_FEATURES |= CPJavascriptClipboardEventsFeature;
|
PLATFORM_FEATURES |= CPJavascriptClipboardEventsFeature;
|
||||||
PLATFORM_FEATURES |= CPJavascriptClipboardAccessFeature;
|
PLATFORM_FEATURES |= CPJavascriptClipboardAccessFeature;
|
||||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||||
|
|
||||||
var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
|
var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
|
||||||
versionEnd = USER_AGENT.indexOf(" ", versionStart),
|
versionEnd = USER_AGENT.indexOf(" ", versionStart),
|
||||||
versionString = USER_AGENT.substring(versionStart, versionEnd),
|
version = parseFloat(USER_AGENT.substring(versionStart, versionEnd), 10);
|
||||||
versionDivision = versionString.indexOf('.'),
|
|
||||||
majorVersion = parseInt(versionString.substring(0, versionDivision)),
|
|
||||||
minorVersion = parseInt(versionString.substr(versionDivision + 1));
|
|
||||||
|
|
||||||
if ((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
|
if(USER_AGENT.indexOf("Plainview") == -1 && version >= 525.14 || USER_AGENT.indexOf("Chrome") != -1)
|
||||||
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||||
|
|
||||||
// FIXME this is a terrible hack to get around this bug:
|
|
||||||
// https://bugs.webkit.org/show_bug.cgi?id=21548
|
|
||||||
if (![CPPlatform isBrowser])
|
|
||||||
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
|
||||||
|
|
||||||
if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
|
|
||||||
PLATFORM_FEATURES |= CPHTML5DragAndDropSourceYOffBy1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// KHTML
|
// KHTML
|
||||||
@@ -145,12 +122,12 @@ else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
|
|||||||
else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
|
else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
|
||||||
{
|
{
|
||||||
PLATFORM_ENGINE = CPGeckoBrowserEngine;
|
PLATFORM_ENGINE = CPGeckoBrowserEngine;
|
||||||
|
|
||||||
PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
|
PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
|
||||||
|
|
||||||
var index = USER_AGENT.indexOf("Firefox"),
|
var index = USER_AGENT.indexOf("Firefox"),
|
||||||
version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
|
version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
|
||||||
|
|
||||||
if (version >= 3.0)
|
if (version >= 3.0)
|
||||||
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||||
|
|
||||||
@@ -166,16 +143,16 @@ if (typeof document != "undefined")
|
|||||||
if (canvasElement && canvasElement.getContext)
|
if (canvasElement && canvasElement.getContext)
|
||||||
{
|
{
|
||||||
PLATFORM_FEATURES |= CPHTMLCanvasFeature;
|
PLATFORM_FEATURES |= CPHTMLCanvasFeature;
|
||||||
|
|
||||||
// Detect Canvas setTransform/transform support
|
// Detect Canvas setTransform/transform support
|
||||||
var context = document.createElement("canvas").getContext("2d");
|
var context = document.createElement("canvas").getContext("2d");
|
||||||
|
|
||||||
if (context && context.setTransform && context.transform)
|
if (context && context.setTransform && context.transform)
|
||||||
PLATFORM_FEATURES |= CPJavaScriptCanvasTransformFeature;
|
PLATFORM_FEATURES |= CPJavaScriptCanvasTransformFeature;
|
||||||
}
|
}
|
||||||
|
|
||||||
var DOMElement = document.createElement("div");
|
var DOMElement = document.createElement("div");
|
||||||
|
|
||||||
// Detect whether we have innerText or textContent (or neither)
|
// Detect whether we have innerText or textContent (or neither)
|
||||||
if (DOMElement.innerText != undefined)
|
if (DOMElement.innerText != undefined)
|
||||||
PLATFORM_FEATURES |= CPJavascriptInnerTextFeature;
|
PLATFORM_FEATURES |= CPJavascriptInnerTextFeature;
|
||||||
@@ -190,38 +167,26 @@ function CPFeatureIsCompatible(aFeature)
|
|||||||
|
|
||||||
function CPBrowserIsEngine(anEngine)
|
function CPBrowserIsEngine(anEngine)
|
||||||
{
|
{
|
||||||
return PLATFORM_ENGINE === anEngine;
|
return PLATFORM_ENGINE == anEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CPBrowserIsOperatingSystem(anOperatingSystem)
|
if (USER_AGENT.indexOf("Mac") != -1)
|
||||||
{
|
{
|
||||||
return OPERATING_SYSTEM === anOperatingSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
OPERATING_SYSTEM = CPOtherOperatingSystem;
|
|
||||||
|
|
||||||
if (USER_AGENT.indexOf("Mac") !== -1)
|
|
||||||
{
|
|
||||||
OPERATING_SYSTEM = CPMacOperatingSystem;
|
|
||||||
|
|
||||||
CPPlatformActionKeyMask = CPCommandKeyMask;
|
CPPlatformActionKeyMask = CPCommandKeyMask;
|
||||||
|
|
||||||
CPUndoKeyEquivalent = @"z";
|
CPUndoKeyEquivalent = @"Z";
|
||||||
CPRedoKeyEquivalent = @"Z";
|
CPRedoKeyEquivalent = @"Z";
|
||||||
|
|
||||||
CPUndoKeyEquivalentModifierMask = CPCommandKeyMask;
|
CPUndoKeyEquivalentModifierMask = CPCommandKeyMask;
|
||||||
CPRedoKeyEquivalentModifierMask = CPCommandKeyMask;
|
CPRedoKeyEquivalentModifierMask = CPCommandKeyMask | CPShiftKeyMask;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (USER_AGENT.indexOf("Windows") !== -1)
|
|
||||||
OPERATING_SYSTEM = CPWindowsOperatingSystem;
|
|
||||||
|
|
||||||
CPPlatformActionKeyMask = CPControlKeyMask;
|
CPPlatformActionKeyMask = CPControlKeyMask;
|
||||||
|
|
||||||
CPUndoKeyEquivalent = @"z";
|
CPUndoKeyEquivalent = @"Z";
|
||||||
CPRedoKeyEquivalent = @"y";
|
CPRedoKeyEquivalent = @"Y";
|
||||||
|
|
||||||
CPUndoKeyEquivalentModifierMask = CPControlKeyMask;
|
CPUndoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||||
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
|
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||||
}
|
}
|
||||||
|
|||||||
+172
-234
@@ -23,45 +23,51 @@
|
|||||||
@import "CPFont.j"
|
@import "CPFont.j"
|
||||||
@import "CPShadow.j"
|
@import "CPShadow.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
@import "CPKeyValueBinding.j"
|
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
#include "Platform/Platform.h"
|
||||||
|
|
||||||
CPLeftTextAlignment = 0;
|
/*
|
||||||
CPRightTextAlignment = 1;
|
@global
|
||||||
CPCenterTextAlignment = 2;
|
@group CPTextAlignment
|
||||||
CPJustifiedTextAlignment = 3;
|
*/
|
||||||
CPNaturalTextAlignment = 4;
|
CPLeftTextAlignment = 0;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPTextAlignment
|
||||||
|
*/
|
||||||
|
CPRightTextAlignment = 1;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPTextAlignment
|
||||||
|
*/
|
||||||
|
CPCenterTextAlignment = 2;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPTextAlignment
|
||||||
|
*/
|
||||||
|
CPJustifiedTextAlignment = 3;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPTextAlignment
|
||||||
|
*/
|
||||||
|
CPNaturalTextAlignment = 4;
|
||||||
|
|
||||||
CPRegularControlSize = 0;
|
/*
|
||||||
CPSmallControlSize = 1;
|
@global
|
||||||
CPMiniControlSize = 2;
|
@group CPControlSize
|
||||||
|
*/
|
||||||
CPLineBreakByWordWrapping = 0;
|
CPRegularControlSize = 0;
|
||||||
CPLineBreakByCharWrapping = 1;
|
/*
|
||||||
CPLineBreakByClipping = 2;
|
@global
|
||||||
CPLineBreakByTruncatingHead = 3;
|
@group CPControlSize
|
||||||
CPLineBreakByTruncatingTail = 4;
|
*/
|
||||||
CPLineBreakByTruncatingMiddle = 5;
|
CPSmallControlSize = 1;
|
||||||
|
/*
|
||||||
CPTopVerticalTextAlignment = 1,
|
@global
|
||||||
CPCenterVerticalTextAlignment = 2,
|
@group CPControlSize
|
||||||
CPBottomVerticalTextAlignment = 3;
|
*/
|
||||||
|
CPMiniControlSize = 2;
|
||||||
CPScaleProportionally = 0;
|
|
||||||
CPScaleToFit = 1;
|
|
||||||
CPScaleNone = 2;
|
|
||||||
|
|
||||||
CPNoImage = 0;
|
|
||||||
CPImageOnly = 1;
|
|
||||||
CPImageLeft = 2;
|
|
||||||
CPImageRight = 3;
|
|
||||||
CPImageBelow = 4;
|
|
||||||
CPImageAbove = 5;
|
|
||||||
CPImageOverlaps = 6;
|
|
||||||
|
|
||||||
CPOnState = 1;
|
|
||||||
CPOffState = 0;
|
|
||||||
CPMixedState = -1;
|
|
||||||
|
|
||||||
CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
|
CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
|
||||||
CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
|
CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
|
||||||
@@ -74,7 +80,7 @@ CPControlTextDidEndEditingNotification = "CPControlTextDidEndEditingNotific
|
|||||||
|
|
||||||
var CPControlBlackColor = [CPColor blackColor];
|
var CPControlBlackColor = [CPColor blackColor];
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPControl
|
@class CPControl
|
||||||
|
|
||||||
@@ -83,18 +89,20 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
@implementation CPControl : CPView
|
@implementation CPControl : CPView
|
||||||
{
|
{
|
||||||
id _value;
|
id _value;
|
||||||
|
|
||||||
// Target-Action Support
|
// Target-Action Support
|
||||||
id _target;
|
id _target;
|
||||||
SEL _action;
|
SEL _action;
|
||||||
int _sendActionOn;
|
int _sendActionOn;
|
||||||
BOOL _sendsActionOnEndEditing @accessors(property=sendsActionOnEndEditing);
|
|
||||||
|
|
||||||
// Mouse Tracking Support
|
// Mouse Tracking Support
|
||||||
BOOL _continuousTracking;
|
BOOL _continuousTracking;
|
||||||
BOOL _trackingWasWithinFrame;
|
BOOL _trackingWasWithinFrame;
|
||||||
unsigned _trackingMouseDownFlags;
|
unsigned _trackingMouseDownFlags;
|
||||||
CGPoint _previousTrackingLocation;
|
CGPoint _previousTrackingLocation;
|
||||||
|
|
||||||
|
JSObject _ephemeralSubviewsForNames;
|
||||||
|
CPSet _ephereralSubviews;
|
||||||
|
|
||||||
CPString _toolTip;
|
CPString _toolTip;
|
||||||
}
|
}
|
||||||
@@ -106,7 +114,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
CPLineBreakByClipping,
|
CPLineBreakByClipping,
|
||||||
[CPColor blackColor],
|
[CPColor blackColor],
|
||||||
[CPFont systemFontOfSize:12.0],
|
[CPFont systemFontOfSize:12.0],
|
||||||
[CPNull null],
|
nil,
|
||||||
_CGSizeMakeZero(),
|
_CGSizeMakeZero(),
|
||||||
CPImageLeft,
|
CPImageLeft,
|
||||||
CPScaleToFit,
|
CPScaleToFit,
|
||||||
@@ -125,46 +133,16 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
@"max-size"]];
|
@"max-size"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self === [CPControl class])
|
|
||||||
{
|
|
||||||
[self exposeBinding:@"value"];
|
|
||||||
[self exposeBinding:@"objectValue"];
|
|
||||||
[self exposeBinding:@"stringValue"];
|
|
||||||
[self exposeBinding:@"integerValue"];
|
|
||||||
[self exposeBinding:@"intValue"];
|
|
||||||
[self exposeBinding:@"doubleValue"];
|
|
||||||
[self exposeBinding:@"floatValue"];
|
|
||||||
|
|
||||||
[self exposeBinding:@"enabled"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_reverseSetBinding
|
|
||||||
{
|
|
||||||
var theBinding = [CPKeyValueBinding getBinding:CPValueBinding forObject:self];
|
|
||||||
[theBinding reverseSetValueFor:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_replacementKeyPathForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding === @"value")
|
|
||||||
return @"objectValue";
|
|
||||||
|
|
||||||
return [super _replacementKeyPathForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_sendActionOn = CPLeftMouseUpMask;
|
_sendActionOn = CPLeftMouseUpMask;
|
||||||
_trackingMouseDownFlags = 0;
|
_trackingMouseDownFlags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,22 +181,21 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Causes \c anAction to be sent to \c anObject.
|
Causes <code>anAction</code> to be sent to <code>anObject</code>.
|
||||||
@param anAction the action to send
|
@param anAction the action to send
|
||||||
@param anObject the object to which the action will be sent
|
@param anObject the object to which the action will be sent
|
||||||
*/
|
*/
|
||||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
- (void)sendAction:(SEL)anAction to:(id)anObject
|
||||||
{
|
{
|
||||||
[self _reverseSetBinding];
|
|
||||||
[CPApp sendAction:anAction to:anObject from:self];
|
[CPApp sendAction:anAction to:anObject from:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)sendActionOn:(int)mask
|
- (int)sendActionOn:(int)mask
|
||||||
{
|
{
|
||||||
var previousMask = _sendActionOn;
|
var previousMask = _sendActionOn;
|
||||||
|
|
||||||
_sendActionOn = mask;
|
_sendActionOn = mask;
|
||||||
|
|
||||||
return previousMask;
|
return previousMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +208,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
{
|
{
|
||||||
if (_toolTip == aToolTip)
|
if (_toolTip == aToolTip)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_toolTip = aToolTip;
|
_toolTip = aToolTip;
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
@@ -266,7 +243,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
// Some subclasses should redefine this with CPLeftMouseDraggedMask
|
// Some subclasses should redefine this with CPLeftMouseDraggedMask
|
||||||
if (flag)
|
if (flag)
|
||||||
_sendActionOn |= CPPeriodicMask;
|
_sendActionOn |= CPPeriodicMask;
|
||||||
else
|
else
|
||||||
_sendActionOn &= ~CPPeriodicMask;
|
_sendActionOn &= ~CPPeriodicMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,15 +255,16 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
- (void)trackMouse:(CPEvent)anEvent
|
- (void)trackMouse:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var type = [anEvent type],
|
var type = [anEvent type],
|
||||||
currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
|
isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
|
||||||
|
|
||||||
if (type === CPLeftMouseUp)
|
if (type === CPLeftMouseUp)
|
||||||
{
|
{
|
||||||
[self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:YES];
|
[self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:YES];
|
||||||
|
|
||||||
_trackingMouseDownFlags = 0;
|
_trackingMouseDownFlags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (type === CPLeftMouseDown)
|
if (type === CPLeftMouseDown)
|
||||||
@@ -300,57 +278,29 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
{
|
{
|
||||||
if (!_trackingWasWithinFrame)
|
if (!_trackingWasWithinFrame)
|
||||||
_continuousTracking = [self startTrackingAt:currentLocation];
|
_continuousTracking = [self startTrackingAt:currentLocation];
|
||||||
|
|
||||||
else if (_continuousTracking)
|
else if (_continuousTracking)
|
||||||
_continuousTracking = [self continueTracking:_previousTrackingLocation at:currentLocation];
|
_continuousTracking = [self continueTracking:_previousTrackingLocation at:currentLocation];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:NO];
|
[self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
[CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((_sendActionOn & (1 << type)) && isWithinFrame)
|
if ((_sendActionOn & (1 << type)) && isWithinFrame)
|
||||||
[self sendAction:_action to:_target];
|
[self sendAction:_action to:_target];
|
||||||
|
|
||||||
_trackingWasWithinFrame = isWithinFrame;
|
_trackingWasWithinFrame = isWithinFrame;
|
||||||
_previousTrackingLocation = currentLocation;
|
_previousTrackingLocation = currentLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setState:(int)state
|
- (void)performClick:(id)sender
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
- (int)nextState
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)performClick:(id)sender
|
|
||||||
{
|
|
||||||
if (![self isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self highlight:YES];
|
[self highlight:YES];
|
||||||
[self setState:[self nextState]];
|
[self setState:[self nextState]];
|
||||||
|
[self sendAction:[self action] to:[self target]];
|
||||||
try
|
|
||||||
{
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
[CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unhighlightButtonTimerDidFinish:) userInfo:nil repeats:NO];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)unhighlightButtonTimerDidFinish:(id)sender
|
|
||||||
{
|
|
||||||
[self highlight:NO];
|
[self highlight:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +312,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
[self highlight:YES];
|
[self highlight:YES];
|
||||||
|
|
||||||
return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
|
return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +330,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
{
|
{
|
||||||
if (![self isEnabled])
|
if (![self isEnabled])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self trackMouse:anEvent];
|
[self trackMouse:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,7 +348,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
- (void)setObjectValue:(id)anObject
|
- (void)setObjectValue:(id)anObject
|
||||||
{
|
{
|
||||||
_value = anObject;
|
_value = anObject;
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
@@ -472,7 +422,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's string value
|
Returns the receiver's int value
|
||||||
*/
|
*/
|
||||||
- (CPString)stringValue
|
- (CPString)stringValue
|
||||||
{
|
{
|
||||||
@@ -480,7 +430,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Set's the receiver's string value
|
Set's the receiver's int value
|
||||||
*/
|
*/
|
||||||
- (void)setStringValue:(CPString)anObject
|
- (void)setStringValue:(CPString)anObject
|
||||||
{
|
{
|
||||||
@@ -527,124 +477,54 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
[self setStringValue:[sender stringValue]];
|
[self setStringValue:[sender stringValue]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)textDidBeginEditing:(CPNotification)note
|
- (void)textDidBeginEditing:(CPNotification)note
|
||||||
{
|
{
|
||||||
//this looks to prevent false propagation of notifications for other objects
|
//this looks to prevent false propagation of notifications for other objects
|
||||||
if ([note object] != self)
|
if([note object] != self)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)textDidChange:(CPNotification)note
|
- (void)textDidChange:(CPNotification)note
|
||||||
{
|
{
|
||||||
//this looks to prevent false propagation of notifications for other objects
|
//this looks to prevent false propagation of notifications for other objects
|
||||||
if ([note object] != self)
|
if([note object] != self)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)textDidEndEditing:(CPNotification)note
|
- (void)textDidEndEditing:(CPNotification)note
|
||||||
{
|
{
|
||||||
//this looks to prevent false propagation of notifications for other objects
|
//this looks to prevent false propagation of notifications for other objects
|
||||||
if ([note object] != self)
|
if([note object] != self)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self _reverseSetBinding];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setAlignment:(CPTextAlignment)alignment
|
#define BRIDGE(UPPERCASE, LOWERCASE, ATTRIBUTENAME) \
|
||||||
{
|
/*! Sets the value for ATTRIBUTENAME */\
|
||||||
[self setValue:alignment forThemeAttribute:@"alignment"];
|
- (void)set##UPPERCASE:(id)aValue\
|
||||||
|
{\
|
||||||
|
[self setValue:aValue forThemeAttribute:ATTRIBUTENAME];\
|
||||||
|
}\
|
||||||
|
/*! Returns the current value for ATTRIBUTENAME */\
|
||||||
|
- (id)LOWERCASE\
|
||||||
|
{\
|
||||||
|
return [self valueForThemeAttribute:ATTRIBUTENAME];\
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPTextAlignment)alignment
|
BRIDGE(Alignment, alignment, "alignment")
|
||||||
{
|
BRIDGE(VerticalAlignment, verticalAlignment, "vertical-alignment")
|
||||||
return [self valueForThemeAttribute:@"alignment"];
|
BRIDGE(LineBreakMode, lineBreakMode, "line-break-mode")
|
||||||
}
|
BRIDGE(TextColor, textColor, "text-color")
|
||||||
|
BRIDGE(Font, font, "font")
|
||||||
- (void)setVerticalAlignment:(CPTextVerticalAlignment)alignment
|
BRIDGE(TextShadowColor, textShadowColor, "text-shadow-color")
|
||||||
{
|
BRIDGE(TextShadowOffset, textShadowOffset, "text-shadow-offset")
|
||||||
[self setValue:alignment forThemeAttribute:@"vertical-alignment"];
|
BRIDGE(ImagePosition, imagePosition, "image-position")
|
||||||
}
|
BRIDGE(ImageScaling, imageScaling, "image-scaling")
|
||||||
|
|
||||||
- (CPTextVerticalAlignment)verticalAlignment
|
|
||||||
{
|
|
||||||
return [self valueForThemeAttribute:@"vertical-alignment"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setLineBreakMode:(CPLineBreakMode)mode
|
|
||||||
{
|
|
||||||
[self setValue:mode forThemeAttribute:@"line-break-mode"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPLineBreakMode)lineBreakMode
|
|
||||||
{
|
|
||||||
return [self valueForThemeAttribute:@"line-break-mode"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTextColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
[self setValue:aColor forThemeAttribute:@"text-color"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textColor
|
|
||||||
{
|
|
||||||
return [self valueForThemeAttribute:@"text-color"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTextShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
[self setValue:aColor forThemeAttribute:@"text-shadow-color"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textShadowColor
|
|
||||||
{
|
|
||||||
return [self valueForThemeAttribute:@"text-shadow-color"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTextShadowOffset:(float)offset
|
|
||||||
{
|
|
||||||
[self setValue:offset forThemeAttribute:@"text-shadow-offset"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)textShadowOffset
|
|
||||||
{
|
|
||||||
return [self valueForThemeAttribute:@"text-shadow-offset"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
[self setValue:aFont forThemeAttribute:@"font"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFont)font
|
|
||||||
{
|
|
||||||
return [self valueForThemeAttribute:@"font"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setImagePosition:(CPCellImagePosition)position
|
|
||||||
{
|
|
||||||
[self setValue:position forThemeAttribute:@"image-position"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPCellImagePosition)imagePosition
|
|
||||||
{
|
|
||||||
return [self valueForThemeAttribute:@"image-position"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setImageScaling:(CPImageScaling)scaling
|
|
||||||
{
|
|
||||||
[self setValue:scaling forThemeAttribute:@"image-scaling"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPImageScaling)imageScaling
|
|
||||||
{
|
|
||||||
return [self valueForThemeAttribute:@"image-scaling"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setEnabled:(BOOL)isEnabled
|
- (void)setEnabled:(BOOL)isEnabled
|
||||||
{
|
{
|
||||||
@@ -677,17 +557,63 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
return [self hasThemeState:CPThemeStateHighlighted];
|
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
|
@end
|
||||||
|
|
||||||
var CPControlValueKey = "CPControlValueKey",
|
var CPControlValueKey = "CPControlValueKey",
|
||||||
CPControlControlStateKey = @"CPControlControlStateKey",
|
CPControlControlStateKey = @"CPControlControlStateKey",
|
||||||
CPControlIsEnabledKey = "CPControlIsEnabledKey",
|
CPControlIsEnabledKey = "CPControlIsEnabledKey",
|
||||||
|
|
||||||
CPControlTargetKey = "CPControlTargetKey",
|
CPControlTargetKey = "CPControlTargetKey",
|
||||||
CPControlActionKey = "CPControlActionKey",
|
CPControlActionKey = "CPControlActionKey",
|
||||||
CPControlSendActionOnKey = "CPControlSendActionOnKey",
|
CPControlSendActionOnKey = "CPControlSendActionOnKey";
|
||||||
|
|
||||||
CPControlSendsActionOnEndEditingKey = "CPControlSendsActionOnEndEditingKey";
|
|
||||||
|
|
||||||
var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||||
|
|
||||||
@@ -710,9 +636,8 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
|||||||
[self setAction:[aCoder decodeObjectForKey:CPControlActionKey]];
|
[self setAction:[aCoder decodeObjectForKey:CPControlActionKey]];
|
||||||
|
|
||||||
[self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
|
[self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
|
||||||
[self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -722,10 +647,23 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
|||||||
*/
|
*/
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (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];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
if (_sendsActionOnEndEditing)
|
if (subviews)
|
||||||
[aCoder encodeBool:_sendsActionOnEndEditing forKey:CPControlSendsActionOnEndEditingKey];
|
_subviews = subviews;
|
||||||
|
|
||||||
if (_value !== nil)
|
if (_value !== nil)
|
||||||
[aCoder encodeObject:_value forKey:CPControlValueKey];
|
[aCoder encodeObject:_value forKey:CPControlValueKey];
|
||||||
@@ -748,7 +686,7 @@ var _CPControlSizeIdentifiers = [],
|
|||||||
_CPControlSizeIdentifiers[CPRegularControlSize] = "Regular";
|
_CPControlSizeIdentifiers[CPRegularControlSize] = "Regular";
|
||||||
_CPControlSizeIdentifiers[CPSmallControlSize] = "Small";
|
_CPControlSizeIdentifiers[CPSmallControlSize] = "Small";
|
||||||
_CPControlSizeIdentifiers[CPMiniControlSize] = "Mini";
|
_CPControlSizeIdentifiers[CPMiniControlSize] = "Mini";
|
||||||
|
|
||||||
function _CPControlIdentifierForControlSize(aControlSize)
|
function _CPControlIdentifierForControlSize(aControlSize)
|
||||||
{
|
{
|
||||||
return _CPControlSizeIdentifiers[aControlSize];
|
return _CPControlSizeIdentifiers[aControlSize];
|
||||||
@@ -759,21 +697,21 @@ function _CPControlColorWithPatternImage(sizes, aClassName)
|
|||||||
var index = 1,
|
var index = 1,
|
||||||
count = arguments.length,
|
count = arguments.length,
|
||||||
identifier = "";
|
identifier = "";
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
identifier += arguments[index];
|
identifier += arguments[index];
|
||||||
|
|
||||||
var color = _CPControlCachedColorWithPatternImages[identifier];
|
var color = _CPControlCachedColorWithPatternImages[identifier];
|
||||||
|
|
||||||
if (!color)
|
if (!color)
|
||||||
{
|
{
|
||||||
var bundle = [CPBundle bundleForClass:[CPControl class]];
|
var bundle = [CPBundle bundleForClass:[CPControl class]];
|
||||||
|
|
||||||
color = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:aClassName + "/" + identifier + ".png"] size:sizes[identifier]]];
|
color = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:aClassName + "/" + identifier + ".png"] size:sizes[identifier]]];
|
||||||
|
|
||||||
_CPControlCachedColorWithPatternImages[identifier] = color;
|
_CPControlCachedColorWithPatternImages[identifier] = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,17 +720,17 @@ function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
|
|||||||
var index = 2,
|
var index = 2,
|
||||||
count = arguments.length,
|
count = arguments.length,
|
||||||
identifier = "";
|
identifier = "";
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
identifier += arguments[index];
|
identifier += arguments[index];
|
||||||
|
|
||||||
var color = _CPControlCachedThreePartImagePattern[identifier];
|
var color = _CPControlCachedThreePartImagePattern[identifier];
|
||||||
|
|
||||||
if (!color)
|
if (!color)
|
||||||
{
|
{
|
||||||
var bundle = [CPBundle bundleForClass:[CPControl class]],
|
var bundle = [CPBundle bundleForClass:[CPControl class]],
|
||||||
path = aClassName + "/" + identifier;
|
path = aClassName + "/" + identifier;
|
||||||
|
|
||||||
sizes = sizes[identifier];
|
sizes = sizes[identifier];
|
||||||
|
|
||||||
color = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:[
|
color = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:[
|
||||||
@@ -800,9 +738,9 @@ function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
|
|||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "1.png"] size:sizes[1]],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "1.png"] size:sizes[1]],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "2.png"] size:sizes[2]]
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "2.png"] size:sizes[2]]
|
||||||
] isVertical:isVertical]];
|
] isVertical:isVertical]];
|
||||||
|
|
||||||
_CPControlCachedThreePartImagePattern[identifier] = color;
|
_CPControlCachedThreePartImagePattern[identifier] = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
//
|
|
||||||
// CPController.j
|
|
||||||
// AppKit
|
|
||||||
//
|
|
||||||
// Created by Ross Boucher 1/15/09
|
|
||||||
// Copyright 280 North
|
|
||||||
//
|
|
||||||
// Adapted from GNUStep
|
|
||||||
// Copyright (C) 2007 Free Software Foundation, Inc
|
|
||||||
// Released under the LGPL.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
|
|
||||||
var CPControllerDeclaredKeysKey = @"CPControllerDeclaredKeysKey";
|
|
||||||
|
|
||||||
@implementation CPController : CPObject
|
|
||||||
{
|
|
||||||
CPArray _editors;
|
|
||||||
CPArray _declaredKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_editors = [];
|
|
||||||
_declaredKeys = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if ([_declaredKeys count] > 0)
|
|
||||||
[aCoder encodeObject:_declaredKeys forKey:CPControllerDeclaredKeysKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aDecoder
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_editors = [];
|
|
||||||
_declaredKeys = [aDecoder decodeObjectForKey:CPControllerDeclaredKeysKey] || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEditing
|
|
||||||
{
|
|
||||||
return [_editors count] > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)commitEditing
|
|
||||||
{
|
|
||||||
var index = 0,
|
|
||||||
count = _editors.length;
|
|
||||||
|
|
||||||
for (; index < count; ++index)
|
|
||||||
if (![[_editors objectAtIndex:i] commitEditing])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)discardEditing
|
|
||||||
{
|
|
||||||
[_editors makeObjectsPerformSelector:@selector(discardEditing)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)objectDidBeginEditing:(id)anEditor
|
|
||||||
{
|
|
||||||
[_editors addObject:anEditor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)objectDidEndEditing:(id)anEditor
|
|
||||||
{
|
|
||||||
[_editors removeObject:anEditor];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+21
-30
@@ -24,7 +24,7 @@
|
|||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPCookie
|
@class CPCookie
|
||||||
CPCookie is the Cappuccino interface to a web browser cookie. You can set the name
|
CPCookie is the Cappuccino interface to a web browser cookie. You can set the name
|
||||||
@@ -33,18 +33,18 @@
|
|||||||
{
|
{
|
||||||
CPString _cookieName;
|
CPString _cookieName;
|
||||||
CPString _cookieValue;
|
CPString _cookieValue;
|
||||||
|
|
||||||
CPString _expires;
|
CPString _expires;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Initializes a cookie with a given name \c aName.
|
Initializes a cookie with a given name <code>aName</code>.
|
||||||
@param the name for the cookie
|
@param the name for the cookie
|
||||||
*/
|
*/
|
||||||
- (id)initWithName:(CPString)aName
|
- (id)initWithName:(CPString)aName
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
_cookieName = aName;
|
_cookieName = aName;
|
||||||
_cookieValue = [self _readCookieValue];
|
_cookieValue = [self _readCookieValue];
|
||||||
|
|
||||||
@@ -83,39 +83,30 @@
|
|||||||
*/
|
*/
|
||||||
- (void)setValue:(CPString)value expires:(CPDate)date domain:(CPString)domain
|
- (void)setValue:(CPString)value expires:(CPDate)date domain:(CPString)domain
|
||||||
{
|
{
|
||||||
if (date)
|
if(date)
|
||||||
var expires = "; expires=" + date.toGMTString();
|
var expires = "; expires="+date.toGMTString();
|
||||||
else
|
else
|
||||||
var expires = "";
|
var expires = "";
|
||||||
|
|
||||||
if (domain)
|
if(domain)
|
||||||
domain = "; domain=" + domain;
|
domain = "; domain="+domain;
|
||||||
else
|
else
|
||||||
domain = "";
|
domain = "";
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
document.cookie = _cookieName+"="+value+expires+"; path=/"+domain;
|
||||||
document.cookie = _cookieName+"="+value+expires+"; path=/"+domain;
|
|
||||||
#else
|
|
||||||
_cookieValue = value;
|
|
||||||
_expires = expires;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (CPString)_readCookieValue
|
- (CPString)_readCookieValue
|
||||||
{
|
{
|
||||||
#if PLATFORM(DOM)
|
var nameEQ = _cookieName + "=";
|
||||||
var nameEQ = _cookieName + "=",
|
var ca = document.cookie.split(';');
|
||||||
ca = document.cookie.split(';');
|
for(var i=0;i < ca.length;i++) {
|
||||||
|
var c = ca[i];
|
||||||
for (var i = 0; i < ca.length; i++)
|
while (c.charAt(0)==' ') c = c.substring(1,c.length);
|
||||||
{
|
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
|
||||||
var c = ca[i];
|
}
|
||||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
return "";
|
||||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,277 +0,0 @@
|
|||||||
/*
|
|
||||||
Testing browsers:
|
|
||||||
WebKit r50235 (Safari 4.0.3+)
|
|
||||||
FireFox 3.5
|
|
||||||
Opera 10.0.0
|
|
||||||
|
|
||||||
Implemented class methods:
|
|
||||||
Webkit : All
|
|
||||||
IE : All
|
|
||||||
FireFox Win : All
|
|
||||||
FireFox Mac : All except closedHandCursor disappearingItemCursor // Firefox mac does not support url cursors
|
|
||||||
Opera : All except resizeLeftRightCursor resizeUpDownCursor operationNotAllowedCursor dragCopyCursor dragLinkCursor contextualMenuCursor openHandCursor closedHandCursor disappearingItemCursor // Opera does not support url cursors so these won't work with images
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
|
|
||||||
var currentCursor = nil,
|
|
||||||
cursorStack = [],
|
|
||||||
cursors = {},
|
|
||||||
cursorURLFormat = nil;
|
|
||||||
|
|
||||||
@implementation CPCursor : CPObject
|
|
||||||
{
|
|
||||||
CPString _cssString;
|
|
||||||
BOOL _isSetOnMouseEntered @accessors(readwrite, getter=isSetOnMouseEntered, setter=setOnMouseEntered:);
|
|
||||||
BOOL _isSetOnMouseExited @accessors(readwrite, getter=isSetOnMouseExited, setter=setOnMouseExited:);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)currentCursor
|
|
||||||
{
|
|
||||||
return currentCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCSSString:(CPString)aString
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
_cssString = aString;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)cursorWithCSSString:(CPString)cssString
|
|
||||||
{
|
|
||||||
var cursor = cursors[cssString];
|
|
||||||
|
|
||||||
if (typeof cursor == 'undefined')
|
|
||||||
{
|
|
||||||
cursor = [[CPCursor alloc] initWithCSSString:cssString];
|
|
||||||
cursors[cssString] = cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)cursorWithImageNamed:(CPString)imageName
|
|
||||||
{
|
|
||||||
if (!cursorURLFormat)
|
|
||||||
{
|
|
||||||
cursorURLFormat = @"url(" + [[CPBundle bundleForClass:self] resourcePath] + @"/CPCursor/%@.cur)";
|
|
||||||
|
|
||||||
if (CPBrowserIsEngine(CPGeckoBrowserEngine))
|
|
||||||
cursorURLFormat += ", default";
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = [CPString stringWithFormat:cursorURLFormat, imageName];
|
|
||||||
|
|
||||||
return [[CPCursor alloc] initWithCSSString:url];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)_cssString
|
|
||||||
{
|
|
||||||
return _cssString;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)arrowCursor
|
|
||||||
{
|
|
||||||
return [CPCursor cursorWithCSSString:@"default"]; // WebKit | FF | opera | IE
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)crosshairCursor
|
|
||||||
{
|
|
||||||
return [CPCursor cursorWithCSSString:@"crosshair"]; // WebKit | FF | opera | IE
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)IBeamCursor
|
|
||||||
{
|
|
||||||
return [CPCursor cursorWithCSSString:@"text"]; // WebKit | FF | opera | IE
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)pointingHandCursor
|
|
||||||
{
|
|
||||||
return [CPCursor cursorWithCSSString:@"pointer"]; // WebKit | FF | opera | IE
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeDownCursor
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
|
||||||
|
|
||||||
return [CPCursor cursorWithCSSString:"s-resize"]; // WebKit | FF | opera
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeUpCursor
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
|
||||||
|
|
||||||
return [CPCursor cursorWithCSSString:@"n-resize"]; // WebKit | FF | opera
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeLeftCursor
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
|
||||||
|
|
||||||
return [CPCursor cursorWithCSSString:@"w-resize"]; // WebKit | FF | opera
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeRightCursor
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
|
||||||
|
|
||||||
return [CPCursor cursorWithCSSString:@"e-resize"]; // WebKit | FF | opera
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeLeftRightCursor
|
|
||||||
{
|
|
||||||
return [CPCursor cursorWithCSSString:@"col-resize"]; // WebKit | FF | IE
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeUpDownCursor
|
|
||||||
{
|
|
||||||
return [CPCursor cursorWithCSSString:@"row-resize"]; // WebKit | FF | IE
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)operationNotAllowedCursor
|
|
||||||
{
|
|
||||||
return [CPCursor cursorWithCSSString:@"not-allowed"]; // WebKit | FF | IE
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)dragCopyCursor
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
|
||||||
|
|
||||||
return [CPCursor cursorWithCSSString:@"copy"]; // WebKit | FF
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)dragLinkCursor
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
|
||||||
|
|
||||||
return [CPCursor cursorWithCSSString:@"alias"]; // WebKit | FF
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)contextualMenuCursor
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
|
||||||
|
|
||||||
return [CPCursor cursorWithCSSString:@"context-menu"]; // WebKit | FF Mac . Not impl FF Win.
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)openHandCursor
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPWebKitBrowserEngine))
|
|
||||||
return [CPCursor cursorWithCSSString:@"-webkit-grab"];
|
|
||||||
else if (CPBrowserIsEngine(CPGeckoBrowserEngine))
|
|
||||||
return [CPCursor cursorWithCSSString:@"-moz-grab"];
|
|
||||||
else if (CPBrowserIsEngine(CPOperaBrowserEngine))
|
|
||||||
return [CPCursor cursorWithCSSString:@"move"];
|
|
||||||
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // WebKit only. move in FFMac|Opera
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)closedHandCursor
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPWebKitBrowserEngine))
|
|
||||||
return [CPCursor cursorWithCSSString:@"-webkit-grabbing"];
|
|
||||||
else if (CPBrowserIsEngine(CPGeckoBrowserEngine))
|
|
||||||
return [CPCursor cursorWithCSSString:@"-moz-grabbing"];
|
|
||||||
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // WebKit || FF
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)disappearingItemCursor
|
|
||||||
{
|
|
||||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // None
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)hide
|
|
||||||
{
|
|
||||||
[self _setCursorCSS:"none"]; // Not supported in IE
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)unhide
|
|
||||||
{
|
|
||||||
[self _setCursorCSS:[currentCursor _cssString]];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)setHiddenUntilMouseMoves:(BOOL)flag
|
|
||||||
{
|
|
||||||
if (flag)
|
|
||||||
[CPCursor hide];
|
|
||||||
else
|
|
||||||
[CPCursor unhide];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithImage:(CPImage)image hotSpot:(CPPoint)hotSpot
|
|
||||||
{
|
|
||||||
return [self initWithCSSString:"url(" + [image filename] + "), auto"];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)mouseEntered:(CPEvent)event
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseExited:(CPEvent)event
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)set
|
|
||||||
{
|
|
||||||
currentCursor = self;
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
[[self class] _setCursorCSS:_cssString];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)_setCursorCSS:(CPString)aString
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var platformWindows = [[CPPlatformWindow visiblePlatformWindows] allObjects];
|
|
||||||
for (var i = 0, count = [platformWindows count]; i < count; i++)
|
|
||||||
platformWindows[i]._DOMBodyElement.style.cursor = aString;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)push
|
|
||||||
{
|
|
||||||
currentCursor = cursorStack.push(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)pop
|
|
||||||
{
|
|
||||||
[CPCursor pop];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)pop
|
|
||||||
{
|
|
||||||
if (cursorStack.length > 1)
|
|
||||||
{
|
|
||||||
cursorStack.pop();
|
|
||||||
currentCursor = cursorStack[cursorStack.length - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)coder
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
_cssString = [coder decodeObjectForKey:@"CPCursorNameKey"];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)coder
|
|
||||||
{
|
|
||||||
[coder encodeObject:_cssString forKey:@"CPCursorNameKey"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
+103
-303
@@ -23,10 +23,7 @@
|
|||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
@import <Foundation/CPArray.j>
|
@import <Foundation/CPArray.j>
|
||||||
|
|
||||||
@import "CPApplication.j"
|
|
||||||
@import "CPResponder.j"
|
@import "CPResponder.j"
|
||||||
@import "CPSavePanel.j"
|
|
||||||
@import "CPViewController.j"
|
|
||||||
@import "CPWindowController.j"
|
@import "CPWindowController.j"
|
||||||
|
|
||||||
|
|
||||||
@@ -83,7 +80,7 @@ CPDocumentDidFailToSaveNotification = @"CPDocumentDidFailToSaveNotification";
|
|||||||
|
|
||||||
var CPDocumentUntitledCount = 0;
|
var CPDocumentUntitledCount = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPDocument
|
@class CPDocument
|
||||||
|
|
||||||
@@ -93,25 +90,19 @@ var CPDocumentUntitledCount = 0;
|
|||||||
CPDocuments should be used to represent this.
|
CPDocuments should be used to represent this.
|
||||||
*/
|
*/
|
||||||
@implementation CPDocument : CPResponder
|
@implementation CPDocument : CPResponder
|
||||||
{
|
{
|
||||||
CPWindow _window; // For outlet purposes.
|
CPURL _fileURL;
|
||||||
CPView _view; // For outlet purposes
|
CPString _fileType;
|
||||||
CPDictionary _viewControllersForWindowControllers;
|
CPArray _windowControllers;
|
||||||
|
unsigned _untitledDocumentIndex;
|
||||||
|
|
||||||
CPURL _fileURL;
|
BOOL _hasUndoManager;
|
||||||
CPString _fileType;
|
CPUndoManager _undoManager;
|
||||||
CPArray _windowControllers;
|
|
||||||
unsigned _untitledDocumentIndex;
|
int _changeCount;
|
||||||
|
|
||||||
BOOL _hasUndoManager;
|
CPURLConnection _readConnection;
|
||||||
CPUndoManager _undoManager;
|
CPURLRequest _writeRequest;
|
||||||
|
|
||||||
int _changeCount;
|
|
||||||
|
|
||||||
CPURLConnection _readConnection;
|
|
||||||
CPURLRequest _writeRequest;
|
|
||||||
|
|
||||||
CPAlert _canCloseAlert;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -121,18 +112,17 @@ var CPDocumentUntitledCount = 0;
|
|||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_windowControllers = [];
|
_windowControllers = [];
|
||||||
_viewControllersForWindowControllers = [CPDictionary dictionary];
|
|
||||||
|
|
||||||
_hasUndoManager = YES;
|
_hasUndoManager = YES;
|
||||||
_changeCount = 0;
|
_changeCount = 0;
|
||||||
|
|
||||||
[self setNextResponder:CPApp];
|
[self setNextResponder:CPApp];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,10 +135,10 @@ var CPDocumentUntitledCount = 0;
|
|||||||
- (id)initWithType:(CPString)aType error:({CPError})anError
|
- (id)initWithType:(CPString)aType error:({CPError})anError
|
||||||
{
|
{
|
||||||
self = [self init];
|
self = [self init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
[self setFileType:aType];
|
[self setFileType:aType];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,15 +156,15 @@ var CPDocumentUntitledCount = 0;
|
|||||||
- (id)initWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
|
- (id)initWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
|
||||||
{
|
{
|
||||||
self = [self init];
|
self = [self init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
|
[self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||||
|
|
||||||
[self setFileURL:anAbsoluteURL];
|
[self setFileURL:anAbsoluteURL];
|
||||||
[self setFileType:aType];
|
[self setFileType:aType];
|
||||||
|
|
||||||
[self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,22 +174,22 @@ var CPDocumentUntitledCount = 0;
|
|||||||
@param absoluteContentsURL the location of the document's contents
|
@param absoluteContentsURL the location of the document's contents
|
||||||
@param aType the type of the contents
|
@param aType the type of the contents
|
||||||
@param aDelegate this object will receive a callback after the document's contents are loaded
|
@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 \c aDelegate
|
@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 \c aDelegate
|
@param aContextInfo passed as the argument to the message sent to the <code>aDelegate</code>
|
||||||
@return the initialized document
|
@return the initialized document
|
||||||
*/
|
*/
|
||||||
- (id)initForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
|
- (id)initForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
|
||||||
{
|
{
|
||||||
self = [self init];
|
self = [self init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
|
[self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||||
|
|
||||||
[self setFileURL:anAbsoluteURL];
|
[self setFileURL:anAbsoluteURL];
|
||||||
[self setFileType:aType];
|
[self setFileType:aType];
|
||||||
|
|
||||||
[self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,73 +222,15 @@ var CPDocumentUntitledCount = 0;
|
|||||||
reason:"readFromData:ofType: must be overridden by the document subclass."];
|
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
|
// Creating and managing window controllers
|
||||||
/*!
|
/*!
|
||||||
Creates the window controller for this document.
|
Creates the window controller for this document.
|
||||||
*/
|
*/
|
||||||
- (void)makeWindowControllers
|
- (void)makeWindowControllers
|
||||||
{
|
{
|
||||||
[self makeViewAndWindowControllers];
|
var controller = [[CPWindowController alloc] initWithWindowCibName:nil];
|
||||||
}
|
|
||||||
|
[self addWindowController:controller];
|
||||||
- (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],
|
|
||||||
viewFrame = [view frame];
|
|
||||||
|
|
||||||
viewFrame.origin = CGPointMake(50, 50);
|
|
||||||
|
|
||||||
var theWindow = [[CPWindow alloc] initWithContentRect:viewFrame styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask];
|
|
||||||
|
|
||||||
windowController = [[CPWindowController alloc] initWithWindow:theWindow];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowController && viewController)
|
|
||||||
[windowController setSupportsMultipleDocuments:YES];
|
|
||||||
|
|
||||||
if (windowController)
|
|
||||||
[self addWindowController:windowController];
|
|
||||||
|
|
||||||
if (viewController)
|
|
||||||
[self addViewController:viewController forWindowController:windowController];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -317,52 +249,12 @@ var CPDocumentUntitledCount = 0;
|
|||||||
- (void)addWindowController:(CPWindowController)aWindowController
|
- (void)addWindowController:(CPWindowController)aWindowController
|
||||||
{
|
{
|
||||||
[_windowControllers addObject:aWindowController];
|
[_windowControllers addObject:aWindowController];
|
||||||
|
|
||||||
if ([aWindowController document] !== self)
|
if ([aWindowController document] != self)
|
||||||
|
{
|
||||||
|
[aWindowController setNextResponder:self];
|
||||||
[aWindowController setDocument:self];
|
[aWindowController setDocument:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Remove a controller to the document's list of controllers. This should
|
|
||||||
be called after closing the controller's window.
|
|
||||||
@param aWindowController the controller to remove
|
|
||||||
*/
|
|
||||||
- (void)removeWindowController:(CPWindowController)aWindowController
|
|
||||||
{
|
|
||||||
if (aWindowController)
|
|
||||||
[_windowControllers removeObject:aWindowController];
|
|
||||||
|
|
||||||
if ([aWindowController document] === self)
|
|
||||||
[aWindowController setDocument:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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
|
// Managing Document Windows
|
||||||
@@ -371,7 +263,6 @@ var CPDocumentUntitledCount = 0;
|
|||||||
*/
|
*/
|
||||||
- (void)showWindows
|
- (void)showWindows
|
||||||
{
|
{
|
||||||
[_windowControllers makeObjectsPerformSelector:@selector(setDocument:) withObject:self];
|
|
||||||
[_windowControllers makeObjectsPerformSelector:@selector(showWindow:) withObject:self];
|
[_windowControllers makeObjectsPerformSelector:@selector(showWindow:) withObject:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,19 +273,14 @@ var CPDocumentUntitledCount = 0;
|
|||||||
{
|
{
|
||||||
if (_fileURL)
|
if (_fileURL)
|
||||||
return [_fileURL lastPathComponent];
|
return [_fileURL lastPathComponent];
|
||||||
|
|
||||||
if (!_untitledDocumentIndex)
|
if (!_untitledDocumentIndex)
|
||||||
_untitledDocumentIndex = ++CPDocumentUntitledCount;
|
_untitledDocumentIndex = ++CPDocumentUntitledCount;
|
||||||
|
|
||||||
|
if (_untitledDocumentIndex == 1)
|
||||||
|
return @"Untitled";
|
||||||
|
|
||||||
if (_untitledDocumentIndex == 1)
|
return @"Untitled " + _untitledDocumentIndex;
|
||||||
return @"Untitled";
|
|
||||||
|
|
||||||
return @"Untitled " + _untitledDocumentIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)viewCibName
|
|
||||||
{
|
|
||||||
return nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -406,18 +292,18 @@ var CPDocumentUntitledCount = 0;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Called after \c aWindowController loads the document's Nib file.
|
Called after <code>aWindowController<code> loads the document's Nib file.
|
||||||
@param aWindowController the controller that loaded the Nib file
|
@param aWindowController the controller that loaded the Nib file
|
||||||
*/
|
*/
|
||||||
- (void)windowControllerDidLoadCib:(CPWindowController)aWindowController
|
- (void)windowControllerDidLoadNib:(CPWindowController)aWindowController
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Called before \c aWindowController will load the document's Nib file.
|
Called before <code>aWindowController</code> will load the document's Nib file.
|
||||||
@param aWindowController the controller that will load the Nib file
|
@param aWindowController the controller that will load the Nib file
|
||||||
*/
|
*/
|
||||||
- (void)windowControllerWillLoadCib:(CPWindowController)aWindowController
|
- (void)windowControllerWillLoadNib:(CPWindowController)aWindowController
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +322,7 @@ var CPDocumentUntitledCount = 0;
|
|||||||
|
|
||||||
// FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
|
// FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
|
||||||
_readConnection = [CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:anAbsoluteURL] delegate:self];
|
_readConnection = [CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:anAbsoluteURL] delegate:self];
|
||||||
|
|
||||||
_readConnection.session = _CPReadSessionMake(aType, aDelegate, aDidReadSelector, aContextInfo);
|
_readConnection.session = _CPReadSessionMake(aType, aDelegate, aDidReadSelector, aContextInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,11 +340,11 @@ var CPDocumentUntitledCount = 0;
|
|||||||
*/
|
*/
|
||||||
- (void)setFileURL:(CPURL)aFileURL
|
- (void)setFileURL:(CPURL)aFileURL
|
||||||
{
|
{
|
||||||
if (_fileURL === aFileURL)
|
if (_fileURL == aFileURL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_fileURL = aFileURL;
|
_fileURL = aFileURL;
|
||||||
|
|
||||||
[_windowControllers makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)];
|
[_windowControllers makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,22 +365,17 @@ var CPDocumentUntitledCount = 0;
|
|||||||
|
|
||||||
_writeRequest = [CPURLRequest requestWithURL:anAbsoluteURL];
|
_writeRequest = [CPURLRequest requestWithURL:anAbsoluteURL];
|
||||||
|
|
||||||
// FIXME: THIS IS WRONG! We need a way to decide
|
[_writeRequest setHTTPMethod:@"POST"];
|
||||||
if ([CPPlatform isBrowser])
|
[_writeRequest setHTTPBody:[data string]];
|
||||||
[_writeRequest setHTTPMethod:@"POST"];
|
|
||||||
else
|
|
||||||
[_writeRequest setHTTPMethod:@"PUT"];
|
|
||||||
|
|
||||||
[_writeRequest setHTTPBody:[data rawString]];
|
|
||||||
|
|
||||||
[_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"];
|
[_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"];
|
||||||
|
|
||||||
if (aSaveOperation === CPSaveOperation)
|
if (aSaveOperation == CPSaveOperation)
|
||||||
[_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
|
[_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
|
||||||
|
|
||||||
if (aSaveOperation !== CPSaveToOperation)
|
if (aSaveOperation != CPSaveToOperation)
|
||||||
[self updateChangeCount:CPChangeCleared];
|
[self updateChangeCount:CPChangeCleared];
|
||||||
|
|
||||||
// FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
|
// FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
|
||||||
var connection = [CPURLConnection connectionWithRequest:_writeRequest delegate:self];
|
var connection = [CPURLConnection connectionWithRequest:_writeRequest delegate:self];
|
||||||
|
|
||||||
@@ -510,21 +391,21 @@ var CPDocumentUntitledCount = 0;
|
|||||||
// If we got this far and it wasn't an HTTP request, then everything is fine.
|
// If we got this far and it wasn't an HTTP request, then everything is fine.
|
||||||
if (![aResponse isKindOfClass:[CPHTTPURLResponse class]])
|
if (![aResponse isKindOfClass:[CPHTTPURLResponse class]])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var statusCode = [aResponse statusCode];
|
var statusCode = [aResponse statusCode];
|
||||||
|
|
||||||
// Nothing to do if everything is hunky dory.
|
// Nothing to do if everything is hunky dory.
|
||||||
if (statusCode === 200)
|
if (statusCode === 200)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var session = aConnection.session;
|
var session = aConnection.session;
|
||||||
|
|
||||||
if (aConnection == _readConnection)
|
if (aConnection == _readConnection)
|
||||||
{
|
{
|
||||||
[aConnection cancel];
|
[aConnection cancel];
|
||||||
|
|
||||||
alert("There was an error retrieving the document.");
|
alert("There was an error retrieving the document.");
|
||||||
|
|
||||||
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -533,25 +414,24 @@ var CPDocumentUntitledCount = 0;
|
|||||||
if (statusCode == 409)
|
if (statusCode == 409)
|
||||||
{
|
{
|
||||||
[aConnection cancel];
|
[aConnection cancel];
|
||||||
|
|
||||||
if (confirm("There already exists a file with that name, would you like to overwrite it?"))
|
if (confirm("There already exists a file with that name, would you like to overwrite it?"))
|
||||||
{
|
{
|
||||||
[_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
|
[_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
|
||||||
|
|
||||||
[aConnection start];
|
[aConnection start];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (session.saveOperation != CPSaveToOperation)
|
if (session.saveOperation != CPSaveToOperation)
|
||||||
{
|
{
|
||||||
_changeCount += session.changeCount;
|
_changeCount += session.changeCount;
|
||||||
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
|
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
|
||||||
}
|
}
|
||||||
|
|
||||||
_writeRequest = nil;
|
_writeRequest = nil;
|
||||||
|
|
||||||
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||||
[self _sendDocumentSavedNotification:NO];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -564,11 +444,11 @@ var CPDocumentUntitledCount = 0;
|
|||||||
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
|
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
|
||||||
{
|
{
|
||||||
var session = aConnection.session;
|
var session = aConnection.session;
|
||||||
|
|
||||||
// READ
|
// READ
|
||||||
if (aConnection == _readConnection)
|
if (aConnection == _readConnection)
|
||||||
{
|
{
|
||||||
[self readFromData:[CPData dataWithRawString:aData] ofType:session.fileType error:nil];
|
[self readFromData:[CPData dataWithString:aData] ofType:session.fileType error:nil];
|
||||||
|
|
||||||
objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
|
objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
|
||||||
}
|
}
|
||||||
@@ -576,11 +456,10 @@ var CPDocumentUntitledCount = 0;
|
|||||||
{
|
{
|
||||||
if (session.saveOperation != CPSaveToOperation)
|
if (session.saveOperation != CPSaveToOperation)
|
||||||
[self setFileURL:session.absoluteURL];
|
[self setFileURL:session.absoluteURL];
|
||||||
|
|
||||||
_writeRequest = nil;
|
_writeRequest = nil;
|
||||||
|
|
||||||
objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
|
objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
|
||||||
[self _sendDocumentSavedNotification:YES];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,24 +470,23 @@ var CPDocumentUntitledCount = 0;
|
|||||||
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
||||||
{
|
{
|
||||||
var session = aConnection.session;
|
var session = aConnection.session;
|
||||||
|
|
||||||
if (_readConnection == aConnection)
|
if (_readConnection == aConnection)
|
||||||
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (session.saveOperation != CPSaveToOperation)
|
if (session.saveOperation != CPSaveToOperation)
|
||||||
{
|
{
|
||||||
_changeCount += session.changeCount;
|
_changeCount += session.changeCount;
|
||||||
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
|
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
|
||||||
}
|
}
|
||||||
|
|
||||||
_writeRequest = nil;
|
_writeRequest = nil;
|
||||||
|
|
||||||
alert("There was an error saving the document.");
|
alert("There was an error saving the document.");
|
||||||
|
|
||||||
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||||
[self _sendDocumentSavedNotification:NO];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +502,7 @@ var CPDocumentUntitledCount = 0;
|
|||||||
|
|
||||||
// Managing Document Status
|
// Managing Document Status
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if there are any unsaved changes.
|
Returns <code>YES</code> if there are any unsaved changes.
|
||||||
*/
|
*/
|
||||||
- (BOOL)isDocumentEdited
|
- (BOOL)isDocumentEdited
|
||||||
{
|
{
|
||||||
@@ -644,9 +522,9 @@ var CPDocumentUntitledCount = 0;
|
|||||||
else if (aChangeType == CPChangeCleared)
|
else if (aChangeType == CPChangeCleared)
|
||||||
_changeCount = 0;
|
_changeCount = 0;
|
||||||
/*else if (aChangeType == CPCHangeReadOtherContents)
|
/*else if (aChangeType == CPCHangeReadOtherContents)
|
||||||
|
|
||||||
else if (aChangeType == CPChangeAutosaved)*/
|
else if (aChangeType == CPChangeAutosaved)*/
|
||||||
|
|
||||||
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
|
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,7 +548,7 @@ var CPDocumentUntitledCount = 0;
|
|||||||
|
|
||||||
// Working with Undo Manager
|
// Working with Undo Manager
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the document has a
|
Returns <code>YES</code> if the document has a
|
||||||
CPUndoManager.
|
CPUndoManager.
|
||||||
*/
|
*/
|
||||||
- (BOOL)hasUndoManager
|
- (BOOL)hasUndoManager
|
||||||
@@ -680,15 +558,15 @@ var CPDocumentUntitledCount = 0;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets whether the document should have a CPUndoManager.
|
Sets whether the document should have a CPUndoManager.
|
||||||
@param aFlag \c YES makes the document have an undo manager
|
@param aFlag <code>YES</code> makes the document have an undo manager
|
||||||
*/
|
*/
|
||||||
- (void)setHasUndoManager:(BOOL)aFlag
|
- (void)setHasUndoManager:(BOOL)aFlag
|
||||||
{
|
{
|
||||||
if (_hasUndoManager == aFlag)
|
if (_hasUndoManager == aFlag)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_hasUndoManager = aFlag;
|
_hasUndoManager = aFlag;
|
||||||
|
|
||||||
if (!_hasUndoManager)
|
if (!_hasUndoManager)
|
||||||
[self setUndoManager:nil];
|
[self setUndoManager:nil];
|
||||||
}
|
}
|
||||||
@@ -697,7 +575,7 @@ var CPDocumentUntitledCount = 0;
|
|||||||
- (void)_undoManagerWillCloseGroup:(CPNotification)aNotification
|
- (void)_undoManagerWillCloseGroup:(CPNotification)aNotification
|
||||||
{
|
{
|
||||||
var undoManager = [aNotification object];
|
var undoManager = [aNotification object];
|
||||||
|
|
||||||
if ([undoManager isUndoing] || [undoManager isRedoing])
|
if ([undoManager isUndoing] || [undoManager isRedoing])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -724,13 +602,13 @@ var CPDocumentUntitledCount = 0;
|
|||||||
- (void)setUndoManager:(CPUndoManager)anUndoManager
|
- (void)setUndoManager:(CPUndoManager)anUndoManager
|
||||||
{
|
{
|
||||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||||
|
|
||||||
if (_undoManager)
|
if (_undoManager)
|
||||||
{
|
{
|
||||||
[defaultCenter removeObserver:self
|
[defaultCenter removeObserver:self
|
||||||
name:CPUndoManagerDidUndoChangeNotification
|
name:CPUndoManagerDidUndoChangeNotification
|
||||||
object:_undoManager];
|
object:_undoManager];
|
||||||
|
|
||||||
[defaultCenter removeObserver:self
|
[defaultCenter removeObserver:self
|
||||||
name:CPUndoManagerDidRedoChangeNotification
|
name:CPUndoManagerDidRedoChangeNotification
|
||||||
object:_undoManager];
|
object:_undoManager];
|
||||||
@@ -739,17 +617,17 @@ var CPDocumentUntitledCount = 0;
|
|||||||
name:CPUndoManagerWillCloseUndoGroupNotification
|
name:CPUndoManagerWillCloseUndoGroupNotification
|
||||||
object:_undoManager];
|
object:_undoManager];
|
||||||
}
|
}
|
||||||
|
|
||||||
_undoManager = anUndoManager;
|
_undoManager = anUndoManager;
|
||||||
|
|
||||||
if (_undoManager)
|
if (_undoManager)
|
||||||
{
|
{
|
||||||
|
|
||||||
[defaultCenter addObserver:self
|
[defaultCenter addObserver:self
|
||||||
selector:@selector(_undoManagerDidUndoChange:)
|
selector:@selector(_undoManagerDidUndoChange:)
|
||||||
name:CPUndoManagerDidUndoChangeNotification
|
name:CPUndoManagerDidUndoChangeNotification
|
||||||
object:_undoManager];
|
object:_undoManager];
|
||||||
|
|
||||||
[defaultCenter addObserver:self
|
[defaultCenter addObserver:self
|
||||||
selector:@selector(_undoManagerDidRedoChange:)
|
selector:@selector(_undoManagerDidRedoChange:)
|
||||||
name:CPUndoManagerDidRedoChangeNotification
|
name:CPUndoManagerDidRedoChangeNotification
|
||||||
@@ -764,7 +642,7 @@ var CPDocumentUntitledCount = 0;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the document's undo manager. If the document
|
Returns the document's undo manager. If the document
|
||||||
should have one, but the manager is \c nil, it
|
should have one, but the manager is <code>nil</code>, it
|
||||||
will be created and then returned.
|
will be created and then returned.
|
||||||
@return the document's undo manager
|
@return the document's undo manager
|
||||||
*/
|
*/
|
||||||
@@ -775,7 +653,7 @@ var CPDocumentUntitledCount = 0;
|
|||||||
|
|
||||||
return _undoManager;
|
return _undoManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Implemented as a delegate of a CPWindow
|
Implemented as a delegate of a CPWindow
|
||||||
@ignore
|
@ignore
|
||||||
@@ -788,27 +666,22 @@ var CPDocumentUntitledCount = 0;
|
|||||||
// Handling User Actions
|
// Handling User Actions
|
||||||
/*!
|
/*!
|
||||||
Saves the document. If the document does not
|
Saves the document. If the document does not
|
||||||
have a file path to save to (\c fileURL)
|
have a file path to save to (<code>fileURL</code>)
|
||||||
then \c -saveDocumentAs: will be called.
|
then <code>saveDocumentAs:</code> will be called.
|
||||||
@param aSender the object requesting the save
|
@param aSender the object requesting the save
|
||||||
*/
|
*/
|
||||||
- (void)saveDocument:(id)aSender
|
- (void)saveDocument:(id)aSender
|
||||||
{
|
|
||||||
[self saveDocumentWithDelegate:nil didSaveSelector:nil contextInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)saveDocumentWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo
|
|
||||||
{
|
{
|
||||||
if (_fileURL)
|
if (_fileURL)
|
||||||
{
|
{
|
||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
postNotificationName:CPDocumentWillSaveNotification
|
postNotificationName:CPDocumentWillSaveNotification
|
||||||
object:self];
|
object:self];
|
||||||
|
|
||||||
[self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
|
[self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[self _saveDocumentAsWithDelegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
|
[self saveDocumentAs:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -817,30 +690,22 @@ var CPDocumentUntitledCount = 0;
|
|||||||
*/
|
*/
|
||||||
- (void)saveDocumentAs:(id)aSender
|
- (void)saveDocumentAs:(id)aSender
|
||||||
{
|
{
|
||||||
[self _saveDocumentAsWithDelegate:nil didSaveSelector:nil contextInfo:nil];
|
_documentName = window.prompt("Document Name:");
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_saveDocumentAsWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo
|
if (!_documentName)
|
||||||
{
|
|
||||||
var savePanel = [CPSavePanel savePanel],
|
|
||||||
response = [savePanel runModal];
|
|
||||||
|
|
||||||
if (!response)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var saveURL = [savePanel URL];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
postNotificationName:CPDocumentWillSaveNotification
|
postNotificationName:CPDocumentWillSaveNotification
|
||||||
object:self];
|
object:self];
|
||||||
|
|
||||||
[self saveToURL:saveURL ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
|
[self saveToURL:[self proposedFileURL] ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ignore
|
@ignore
|
||||||
*/
|
*/
|
||||||
- (void)_sendDocumentSavedNotification:(BOOL)didSave
|
- (void)document:(id)aDocument didSave:(BOOL)didSave contextInfo:(id)aContextInfo
|
||||||
{
|
{
|
||||||
if (didSave)
|
if (didSave)
|
||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
@@ -854,71 +719,6 @@ var CPDocumentUntitledCount = 0;
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPDocument (ClosingDocuments)
|
|
||||||
|
|
||||||
- (void)close
|
|
||||||
{
|
|
||||||
[_windowControllers makeObjectsPerformSelector:@selector(removeDocumentAndCloseIfNecessary:) withObject:self];
|
|
||||||
[[CPDocumentController sharedDocumentController] removeDocument:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)shouldCloseWindowController:(CPWindowController)controller delegate:(id)delegate shouldCloseSelector:(SEL)selector contextInfo:(Object)info
|
|
||||||
{
|
|
||||||
if ([controller shouldCloseDocument] || ([_windowControllers count] < 2 && [_windowControllers indexOfObject:controller] !== CPNotFound))
|
|
||||||
[self canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_document:shouldClose:context:) contextInfo:{delegate:delegate, selector:selector, context:info}];
|
|
||||||
|
|
||||||
else if ([delegate respondsToSelector:selector])
|
|
||||||
objj_msgSend(delegate, selector, self, YES, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
|
|
||||||
{
|
|
||||||
if (aDocument === self && shouldClose)
|
|
||||||
[self close];
|
|
||||||
|
|
||||||
objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.context);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context
|
|
||||||
{
|
|
||||||
if (![self isDocumentEdited])
|
|
||||||
return [aDelegate respondsToSelector:aSelector] && objj_msgSend(aDelegate, aSelector, self, YES, context);
|
|
||||||
|
|
||||||
_canCloseAlert = [[CPAlert alloc] init];
|
|
||||||
|
|
||||||
[_canCloseAlert setDelegate:self];
|
|
||||||
[_canCloseAlert setAlertStyle:CPWarningAlertStyle];
|
|
||||||
[_canCloseAlert setTitle:@"Unsaved Document"];
|
|
||||||
[_canCloseAlert setMessageText:@"Do you want to save the changes you've made to the document \"" + ([self displayName] || [self fileName]) + "\"?"];
|
|
||||||
|
|
||||||
[_canCloseAlert addButtonWithTitle:@"Save"];
|
|
||||||
[_canCloseAlert addButtonWithTitle:@"Cancel"];
|
|
||||||
[_canCloseAlert addButtonWithTitle:@"Don't Save"];
|
|
||||||
|
|
||||||
_canCloseAlert._context = {delegate:aDelegate, selector:aSelector, context:context};
|
|
||||||
|
|
||||||
[_canCloseAlert runModal];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)alertDidEnd:(CPAlert)alert returnCode:(int)returnCode
|
|
||||||
{
|
|
||||||
if (alert !== _canCloseAlert)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var delegate = alert._context.delegate,
|
|
||||||
selector = alert._context.selector,
|
|
||||||
context = alert._context.context;
|
|
||||||
|
|
||||||
if (returnCode === 0)
|
|
||||||
[self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context];
|
|
||||||
else
|
|
||||||
objj_msgSend(delegate, selector, self, returnCode === 2, context);
|
|
||||||
|
|
||||||
_canCloseAlert = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo)
|
var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo)
|
||||||
{
|
{
|
||||||
return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo };
|
return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo };
|
||||||
|
|||||||
+36
-183
@@ -24,12 +24,11 @@
|
|||||||
@import <Foundation/CPBundle.j>
|
@import <Foundation/CPBundle.j>
|
||||||
|
|
||||||
@import "CPDocument.j"
|
@import "CPDocument.j"
|
||||||
@import "CPOpenPanel.j";
|
|
||||||
|
|
||||||
|
|
||||||
var CPSharedDocumentController = nil;
|
var CPSharedDocumentController = nil;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPDocumentController
|
@class CPDocumentController
|
||||||
This class is responsible for managing an application's open documents.
|
This class is responsible for managing an application's open documents.
|
||||||
@@ -41,7 +40,7 @@ var CPSharedDocumentController = nil;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the singleton instance of the application's document controller. If it has not
|
Returns the singleton instance of the application's document controller. If it has not
|
||||||
been created yet, it will be created then returned.
|
been created yet, it will be created then returned.
|
||||||
@return a CPDocumentController
|
@return a CPDocumentController
|
||||||
*/
|
*/
|
||||||
@@ -49,7 +48,7 @@ var CPSharedDocumentController = nil;
|
|||||||
{
|
{
|
||||||
if (!CPSharedDocumentController)
|
if (!CPSharedDocumentController)
|
||||||
[[self alloc] init];
|
[[self alloc] init];
|
||||||
|
|
||||||
return CPSharedDocumentController;
|
return CPSharedDocumentController;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,11 +58,11 @@ var CPSharedDocumentController = nil;
|
|||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_documents = [[CPArray alloc] init];
|
_documents = [[CPArray alloc] init];
|
||||||
|
|
||||||
if (!CPSharedDocumentController)
|
if (!CPSharedDocumentController)
|
||||||
CPSharedDocumentController = self;
|
CPSharedDocumentController = self;
|
||||||
|
|
||||||
@@ -79,22 +78,22 @@ var CPSharedDocumentController = nil;
|
|||||||
method searches documents already open. It does not
|
method searches documents already open. It does not
|
||||||
open the document at the URL if it is not already open.
|
open the document at the URL if it is not already open.
|
||||||
@param aURL the url of the document
|
@param aURL the url of the document
|
||||||
@return the document, or \c nil if such a document is not open
|
@return the document, or <code>nil</code> if such a document is not open
|
||||||
*/
|
*/
|
||||||
- (CPDocument)documentForURL:(CPURL)aURL
|
- (CPDocument)documentForURL:(CPURL)aURL
|
||||||
{
|
{
|
||||||
var index = 0,
|
var index = 0,
|
||||||
count = [_documents count];
|
count = [_documents count];
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
{
|
{
|
||||||
var theDocument = _documents[index];
|
var theDocument = _documents[index];
|
||||||
|
|
||||||
if ([[theDocument fileURL] isEqual:aURL])
|
if ([[theDocument fileURL] isEqual:aURL])
|
||||||
return theDocument;
|
return theDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -105,16 +104,16 @@ var CPSharedDocumentController = nil;
|
|||||||
- (void)openUntitledDocumentOfType:(CPString)aType display:(BOOL)shouldDisplay
|
- (void)openUntitledDocumentOfType:(CPString)aType display:(BOOL)shouldDisplay
|
||||||
{
|
{
|
||||||
var theDocument = [self makeUntitledDocumentOfType:aType error:nil];
|
var theDocument = [self makeUntitledDocumentOfType:aType error:nil];
|
||||||
|
|
||||||
if (theDocument)
|
if (theDocument)
|
||||||
[self addDocument:theDocument];
|
[self addDocument:theDocument];
|
||||||
|
|
||||||
if (shouldDisplay)
|
if (shouldDisplay)
|
||||||
{
|
{
|
||||||
[theDocument makeWindowControllers];
|
[theDocument makeWindowControllers];
|
||||||
[theDocument showWindows];
|
[theDocument showWindows];
|
||||||
}
|
}
|
||||||
|
|
||||||
return theDocument;
|
return theDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,21 +138,16 @@ var CPSharedDocumentController = nil;
|
|||||||
- (CPDocument)openDocumentWithContentsOfURL:(CPURL)anAbsoluteURL display:(BOOL)shouldDisplay error:(CPError)anError
|
- (CPDocument)openDocumentWithContentsOfURL:(CPURL)anAbsoluteURL display:(BOOL)shouldDisplay error:(CPError)anError
|
||||||
{
|
{
|
||||||
var result = [self documentForURL:anAbsoluteURL];
|
var result = [self documentForURL:anAbsoluteURL];
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
var type = [self typeForContentsOfURL:anAbsoluteURL error:anError];
|
var type = [self typeForContentsOfURL:anAbsoluteURL error:anError];
|
||||||
|
|
||||||
result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:[CPDictionary dictionaryWithObject:shouldDisplay forKey:@"shouldDisplay"]];
|
result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:[CPDictionary dictionaryWithObject:shouldDisplay forKey:@"shouldDisplay"]];
|
||||||
|
|
||||||
[self addDocument:result];
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
[self noteNewRecentDocument:result];
|
|
||||||
}
|
}
|
||||||
else if (shouldDisplay)
|
else if (shouldDisplay)
|
||||||
[result showWindows];
|
[result showWindows];
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +157,7 @@ var CPSharedDocumentController = nil;
|
|||||||
@param anAbsoluteURL the document URL
|
@param anAbsoluteURL the document URL
|
||||||
@param absoluteContentsURL the location of the document's contents
|
@param absoluteContentsURL the location of the document's contents
|
||||||
@param anError not used
|
@param anError not used
|
||||||
@return the loaded document or \c nil if there was an error
|
@return the loaded document or <code>nil</code> if there was an error
|
||||||
*/
|
*/
|
||||||
- (CPDocument)reopenDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL error:(CPError)anError
|
- (CPDocument)reopenDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL error:(CPError)anError
|
||||||
{
|
{
|
||||||
@@ -193,7 +187,7 @@ var CPSharedDocumentController = nil;
|
|||||||
@param aDelegate receives a callback after the load has completed
|
@param aDelegate receives a callback after the load has completed
|
||||||
@param aSelector the selector to invoke for the callback
|
@param aSelector the selector to invoke for the callback
|
||||||
@param aContextInfo an object passed as an argument for the callback
|
@param aContextInfo an object passed as an argument for the callback
|
||||||
@return a new document or \c nil if there was an error
|
@return a new document or <code>nil</code> if there was an error
|
||||||
*/
|
*/
|
||||||
- (CPDocument)makeDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
|
- (CPDocument)makeDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
|
||||||
{
|
{
|
||||||
@@ -209,8 +203,9 @@ var CPSharedDocumentController = nil;
|
|||||||
if (!didRead)
|
if (!didRead)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
[self addDocument:aDocument];
|
||||||
[aDocument makeWindowControllers];
|
[aDocument makeWindowControllers];
|
||||||
|
|
||||||
if ([aContextInfo objectForKey:@"shouldDisplay"])
|
if ([aContextInfo objectForKey:@"shouldDisplay"])
|
||||||
[aDocument showWindows];
|
[aDocument showWindows];
|
||||||
}
|
}
|
||||||
@@ -224,20 +219,6 @@ var CPSharedDocumentController = nil;
|
|||||||
[self openUntitledDocumentOfType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] display:YES];
|
[self openUntitledDocumentOfType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] display:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)openDocument:(id)aSender
|
|
||||||
{
|
|
||||||
var openPanel = [CPOpenPanel openPanel];
|
|
||||||
|
|
||||||
[openPanel runModal];
|
|
||||||
|
|
||||||
var URLs = [openPanel URLs],
|
|
||||||
index = 0,
|
|
||||||
count = [URLs count];
|
|
||||||
|
|
||||||
for (; index < count; ++index)
|
|
||||||
[self openDocumentWithContentsOfURL:[CPURL URLWithString:URLs[index]] display:YES error:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Managing Documents
|
// Managing Documents
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -250,7 +231,7 @@ var CPSharedDocumentController = nil;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Adds \c aDocument under the control of the receiver.
|
Adds <code>aDocument</code> under the control of the receiver.
|
||||||
@param aDocument the document to add
|
@param aDocument the document to add
|
||||||
*/
|
*/
|
||||||
- (void)addDocument:(CPDocument)aDocument
|
- (void)addDocument:(CPDocument)aDocument
|
||||||
@@ -259,7 +240,7 @@ var CPSharedDocumentController = nil;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Removes \c aDocument from the control of the receiver.
|
Removes <code>aDocument</code> from the control of the receiver.
|
||||||
@param aDocument the document to remove
|
@param aDocument the document to remove
|
||||||
*/
|
*/
|
||||||
- (void)removeDocument:(CPDocument)aDocument
|
- (void)removeDocument:(CPDocument)aDocument
|
||||||
@@ -276,29 +257,23 @@ var CPSharedDocumentController = nil;
|
|||||||
{
|
{
|
||||||
var index = 0,
|
var index = 0,
|
||||||
count = _documentTypes.length,
|
count = _documentTypes.length,
|
||||||
|
|
||||||
extension = [[anAbsoluteURL pathExtension] lowercaseString],
|
extension = [[anAbsoluteURL pathExtension] lowercaseString];
|
||||||
starType = nil;
|
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
{
|
{
|
||||||
var documentType = _documentTypes[index],
|
var documentType = _documentTypes[index],
|
||||||
extensions = [documentType objectForKey:@"CFBundleTypeExtensions"],
|
extensions = [documentType objectForKey:@"CFBundleTypeExtensions"],
|
||||||
extensionIndex = 0,
|
extensionIndex = 0,
|
||||||
extensionCount = extensions.length;
|
extensionCount = extensions.length;
|
||||||
|
|
||||||
for (; extensionIndex < extensionCount; ++extensionIndex)
|
for (; extensionIndex < extensionCount; ++extensionIndex)
|
||||||
{
|
if ([extensions[extensionIndex] lowercaseString] == extension)
|
||||||
var thisExtension = [extensions[extensionIndex] lowercaseString];
|
|
||||||
if (thisExtension === extension)
|
|
||||||
return [documentType objectForKey:@"CPBundleTypeName"];
|
return [documentType objectForKey:@"CPBundleTypeName"];
|
||||||
|
|
||||||
if (thisExtension === "****")
|
|
||||||
starType = [documentType objectForKey:@"CPBundleTypeName"];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return starType || [self defaultType];
|
// FIXME?
|
||||||
|
return [self defaultType];//nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managing Document Types
|
// Managing Document Types
|
||||||
@@ -312,18 +287,18 @@ var CPSharedDocumentController = nil;
|
|||||||
for (;i < count; ++i)
|
for (;i < count; ++i)
|
||||||
{
|
{
|
||||||
var documentType = _documentTypes[i];
|
var documentType = _documentTypes[i];
|
||||||
|
|
||||||
if ([documentType objectForKey:@"CPBundleTypeName"] == aType)
|
if ([documentType objectForKey:@"CPBundleTypeName"] == aType)
|
||||||
return documentType;
|
return documentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the CPDocument subclass associated with \c aType.
|
Returns the CPDocument subclass associated with <code>aType</code>.
|
||||||
@param aType the type of document
|
@param aType the type of document
|
||||||
@return a Cappuccino Class object, or \c nil if no match was found
|
@return a Cappuccino Class object, or <code>nil</code> if no match was found
|
||||||
*/
|
*/
|
||||||
- (Class)documentClassForType:(CPString)aType
|
- (Class)documentClassForType:(CPString)aType
|
||||||
{
|
{
|
||||||
@@ -333,125 +308,3 @@ var CPSharedDocumentController = nil;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPDocumentController (Closing)
|
|
||||||
|
|
||||||
- (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info
|
|
||||||
{
|
|
||||||
var context = {
|
|
||||||
delegate: aDelegate,
|
|
||||||
selector: didCloseSelector,
|
|
||||||
context: info
|
|
||||||
};
|
|
||||||
|
|
||||||
[self _closeDocumentsStartingWith:nil shouldClose:YES context:context];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursive callback method. Start it by passing in a document of nil.
|
|
||||||
- (void)_closeDocumentsStartingWith:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
|
|
||||||
{
|
|
||||||
if (shouldClose)
|
|
||||||
{
|
|
||||||
[aDocument close];
|
|
||||||
|
|
||||||
if ([[self documents] count] > 0)
|
|
||||||
{
|
|
||||||
[[[self documents] lastObject] canCloseDocumentWithDelegate:self
|
|
||||||
shouldCloseSelector:@selector(_closeDocumentsStartingWith:shouldClose:context:)
|
|
||||||
contextInfo:context];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([context.delegate respondsToSelector:context.selector])
|
|
||||||
objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPDocumentController (Recents)
|
|
||||||
|
|
||||||
- (CPArray)recentDocumentURLs
|
|
||||||
{
|
|
||||||
// FIXME move this to CP land
|
|
||||||
if (typeof window["cpRecentDocumentURLs"] === 'function')
|
|
||||||
return window.cpRecentDocumentURLs();
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)clearRecentDocuments:(id)sender
|
|
||||||
{
|
|
||||||
if (typeof window["cpClearRecentDocuments"] === 'function')
|
|
||||||
window.cpClearRecentDocuments();
|
|
||||||
|
|
||||||
[self _updateRecentDocumentsMenu];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)noteNewRecentDocument:(CPDocument)aDocument
|
|
||||||
{
|
|
||||||
[self noteNewRecentDocumentURL:[[aDocument fileURL] absoluteString]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)noteNewRecentDocumentURL:(CPString)aURL
|
|
||||||
{
|
|
||||||
if (typeof window["cpNoteNewRecentDocumentPath"] === 'function')
|
|
||||||
window.cpNoteNewRecentDocumentPath(aURL);
|
|
||||||
|
|
||||||
[self _updateRecentDocumentsMenu];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_removeAllRecentDocumentsFromMenu:(CPMenu)aMenu
|
|
||||||
{
|
|
||||||
var items = [aMenu itemArray],
|
|
||||||
count = [items count];
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
var item = items[count];
|
|
||||||
|
|
||||||
if ([item action] === @selector(_openRecentDocument:))
|
|
||||||
[aMenu removeItemAtIndex:count];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateRecentDocumentsMenu
|
|
||||||
{
|
|
||||||
var menu = [[CPApp mainMenu] _menuWithName:@"_CPRecentDocumentsMenu"],
|
|
||||||
recentDocuments = [self recentDocumentURLs],
|
|
||||||
menuItems = [menu itemArray],
|
|
||||||
documentCount = [recentDocuments count],
|
|
||||||
menuItemCount = [menuItems count];
|
|
||||||
|
|
||||||
[self _removeAllRecentDocumentsFromMenu:menu];
|
|
||||||
|
|
||||||
if (menuItemCount)
|
|
||||||
{
|
|
||||||
if (!documentCount)
|
|
||||||
{
|
|
||||||
if ([menuItems[0] isSeparatorItem])
|
|
||||||
[menu removeItemAtIndex:0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (![menuItems[0] isSeparatorItem])
|
|
||||||
[menu insertItem:[CPMenuItem separatorItem] atIndex:0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (documentCount--)
|
|
||||||
{
|
|
||||||
var path = recentDocuments[documentCount],
|
|
||||||
item = [[CPMenuItem alloc] initWithTitle:[path lastPathComponent] action:@selector(_openRecentDocument:) keyEquivalent:nil];
|
|
||||||
|
|
||||||
[item setTag:path];
|
|
||||||
[menu insertItem:item atIndex:0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_openRecentDocument:(id)sender
|
|
||||||
{
|
|
||||||
[self openDocumentWithContentsOfURL:[sender tag] display:YES error:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|||||||
+173
-343
@@ -20,32 +20,124 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPApplication.j"
|
@import <AppKit/CPView.j>
|
||||||
@import "CPEvent.j"
|
@import <AppKit/CPEvent.j>
|
||||||
@import "CPImageView.j"
|
@import <AppKit/CPPasteboard.j>
|
||||||
@import "CPPasteboard.j"
|
@import <AppKit/CPImageView.j>
|
||||||
@import "CPView.j"
|
|
||||||
@import "CPWindow.j"
|
|
||||||
|
|
||||||
|
#import "CoreGraphics/CGGeometry.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])
|
#define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window])
|
||||||
|
|
||||||
var CPDragServerPreviousEvent = nil,
|
var CPSharedDragServer = nil;
|
||||||
CPDragServerPeriodicUpdateInterval = 0.05;
|
|
||||||
|
var CPDragServerView = nil,
|
||||||
|
CPDragServerSource = nil,
|
||||||
|
CPDragServerWindow = nil,
|
||||||
|
CPDragServerOffset = nil,
|
||||||
|
CPDragServerLocation = nil,
|
||||||
|
CPDragServerPasteboard = nil,
|
||||||
|
CPDragServerDestination = nil,
|
||||||
|
CPDragServerDraggingInfo = nil,
|
||||||
|
CPDragServerPreviousEvent = nil,
|
||||||
|
CPDragServerAutoscrollInterval = nil;
|
||||||
|
|
||||||
var CPSharedDragServer = nil;
|
var CPDragServerIsDraggingImage = NO,
|
||||||
|
|
||||||
var CPDragServerSource = nil;
|
CPDragServerShouldSendDraggedViewMovedTo = NO,
|
||||||
var CPDragServerDraggingInfo = nil;
|
CPDragServerShouldSendDraggedImageMovedTo = NO,
|
||||||
|
|
||||||
|
CPDragServerShouldSendDraggedViewEndedAtOperation = NO,
|
||||||
|
CPDragServerShouldSendDraggedImageEndedAtOperation = NO;
|
||||||
|
|
||||||
|
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:)])
|
||||||
|
CPDragServerAutoscrollInterval = setInterval(CPDragServerAutoscroll, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
var location = [anEvent locationInWindow],
|
||||||
|
operation =
|
||||||
|
bridgeLocation = [[anEvent window] convertBaseToBridge:location];
|
||||||
|
|
||||||
|
// We have to convert base to bridge since the drag event comes from the source window, not the drag window.
|
||||||
|
var draggingDestination = [[CPDOMWindowBridge sharedDOMWindowBridge] _dragHitTest:bridgeLocation pasteboard:CPDragServerPasteboard];
|
||||||
|
|
||||||
|
CPDragServerLocation = [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];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CPDraggingInfo is a container of information about a specific dragging session.
|
CPDraggingInfo is a container of information about a specific dragging session.
|
||||||
@@ -55,36 +147,24 @@ var CPDragServerDraggingInfo = nil;
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPPasteboard)draggingPasteboard
|
|
||||||
{
|
|
||||||
if ([CPPlatform supportsDragAndDrop])
|
|
||||||
return [_CPDOMDataTransferPasteboard DOMDataTransferPasteboard];
|
|
||||||
|
|
||||||
return [[CPDragServer sharedDragServer] draggingPasteboard];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)draggingSource
|
- (id)draggingSource
|
||||||
{
|
{
|
||||||
return [[CPDragServer sharedDragServer] draggingSource];
|
return CPDragServerSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
- (unsigned)draggingSourceOperationMask
|
|
||||||
*/
|
|
||||||
|
|
||||||
- (CPPoint)draggingLocation
|
- (CPPoint)draggingLocation
|
||||||
{
|
{
|
||||||
return [[CPDragServer sharedDragServer] draggingLocation];
|
return CPDragServerLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPWindow)draggingDestinationWindow
|
- (CPPasteboard)draggingPasteboard
|
||||||
{
|
{
|
||||||
return DRAGGING_WINDOW([[CPDragServer sharedDragServer] draggingDestination]);
|
return CPDragServerPasteboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPImage)draggedImage
|
- (CPImage)draggedImage
|
||||||
{
|
{
|
||||||
return [[self draggedView] image];
|
return [CPDragServerView image];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGPoint)draggedImageLocation
|
- (CGPoint)draggedImageLocation
|
||||||
@@ -92,51 +172,22 @@ var CPDragServerDraggingInfo = nil;
|
|||||||
return [self draggedViewLocation];
|
return [self draggedViewLocation];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPView)draggedView
|
|
||||||
{
|
|
||||||
return [[CPDragServer sharedDragServer] draggedView];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGPoint)draggedViewLocation
|
- (CGPoint)draggedViewLocation
|
||||||
{
|
{
|
||||||
var dragServer = [CPDragServer sharedDragServer];
|
return [DRAGGING_WINDOW(CPDragServerDestination) convertBridgeToBase:[CPDragServerView frame].origin];
|
||||||
|
}
|
||||||
|
|
||||||
return [DRAGGING_WINDOW([dragServer draggingDestination]) convertPlatformWindowToBase:[[dragServer draggedView] frame].origin];
|
- (CPView)draggedView
|
||||||
|
{
|
||||||
|
return CPDragServerView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|
||||||
CPDraggingSource_draggedImage_endedAt_operation_ = 1 << 1,
|
|
||||||
CPDraggingSource_draggedView_movedTo_ = 1 << 2,
|
|
||||||
CPDraggingSource_draggedView_endedAt_operation_ = 1 << 3;
|
|
||||||
|
|
||||||
@implementation CPDragServer : CPObject
|
@implementation CPDragServer : CPObject
|
||||||
{
|
{
|
||||||
BOOL _isDragging @accessors(readonly, getter=isDragging);
|
CPWindow _dragWindow;
|
||||||
|
CPImageView _imageView;
|
||||||
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;
|
|
||||||
BOOL _draggingDestinationWantsPeriodicUpdates;
|
|
||||||
|
|
||||||
CGPoint _startDragLocation;
|
|
||||||
BOOL _shouldSlideBack;
|
|
||||||
unsigned _dragOperation;
|
|
||||||
|
|
||||||
CPTimer _draggingUpdateTimer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -145,9 +196,9 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
*/
|
*/
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self !== [CPDragServer class])
|
if (self != [CPDragServer class])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CPDragServerDraggingInfo = [[CPDraggingInfo alloc] init];
|
CPDragServerDraggingInfo = [[CPDraggingInfo alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +206,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
{
|
{
|
||||||
if (!CPSharedDragServer)
|
if (!CPSharedDragServer)
|
||||||
CPSharedDragServer = [[CPDragServer alloc] init];
|
CPSharedDragServer = [[CPDragServer alloc] init];
|
||||||
|
|
||||||
return CPSharedDragServer;
|
return CPSharedDragServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,175 +216,16 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_draggedWindow = [[CPWindow alloc] initWithContentRect:_CGRectMakeZero() styleMask:CPBorderlessWindowMask];
|
_dragWindow = [[CPWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:CPBorderlessWindowMask];
|
||||||
|
[_dragWindow setLevel:CPDraggingWindowLevel];
|
||||||
[_draggedWindow setLevel:CPDraggingWindowLevel];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)draggingDestination
|
|
||||||
{
|
|
||||||
return _draggingDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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
|
|
||||||
{
|
|
||||||
[_draggingUpdateTimer invalidate];
|
|
||||||
_draggingUpdateTimer = nil;
|
|
||||||
|
|
||||||
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 respondsToSelector:@selector(draggingExited:)])
|
|
||||||
[_draggingDestination draggingExited:CPDragServerDraggingInfo];
|
|
||||||
|
|
||||||
_draggingDestination = draggingDestination;
|
|
||||||
|
|
||||||
if ([_draggingDestination respondsToSelector:@selector(wantsPeriodicDraggingUpdates)])
|
|
||||||
_draggingDestinationWantsPeriodicUpdates = [_draggingDestination wantsPeriodicDraggingUpdates];
|
|
||||||
else
|
|
||||||
_draggingDestinationWantsPeriodicUpdates = YES;
|
|
||||||
|
|
||||||
if ([_draggingDestination respondsToSelector:@selector(draggingEntered:)])
|
|
||||||
dragOperation = [_draggingDestination draggingEntered:CPDragServerDraggingInfo];
|
|
||||||
}
|
|
||||||
else if ([_draggingDestination respondsToSelector:@selector(draggingUpdated:)])
|
|
||||||
dragOperation = [_draggingDestination draggingUpdated:CPDragServerDraggingInfo];
|
|
||||||
|
|
||||||
if (!_draggingDestination)
|
|
||||||
dragOperation = CPDragOperationNone;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_draggingDestinationWantsPeriodicUpdates)
|
|
||||||
_draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval
|
|
||||||
target:self
|
|
||||||
selector:@selector(_sendPeriodicDraggingUpdate:)
|
|
||||||
userInfo:[CPDictionary dictionaryWithJSObject:{platformWindow:aPlatformWindow, location:aLocation}]
|
|
||||||
repeats:NO];
|
|
||||||
|
|
||||||
var scrollView = [_draggingDestination isKindOfClass:[CPView class]] ? [_draggingDestination enclosingScrollView] : nil;
|
|
||||||
if (scrollView)
|
|
||||||
{
|
|
||||||
var contentView = [scrollView contentView],
|
|
||||||
bounds = [contentView bounds],
|
|
||||||
insetBounds = CGRectInset(bounds, 10, 10),
|
|
||||||
eventLocation = [contentView convertPoint:_draggingLocation fromView:nil],
|
|
||||||
deltaX = 0,
|
|
||||||
deltaY = 0;
|
|
||||||
|
|
||||||
if (!CGRectContainsPoint(insetBounds, eventLocation))
|
|
||||||
{
|
|
||||||
if ([scrollView hasVerticalScroller])
|
|
||||||
{
|
|
||||||
if (eventLocation.y < CGRectGetMinY(insetBounds))
|
|
||||||
deltaY = CGRectGetMinY(insetBounds) - eventLocation.y;
|
|
||||||
else if (eventLocation.y > CGRectGetMaxY(insetBounds))
|
|
||||||
deltaY = CGRectGetMaxY(insetBounds) - eventLocation.y;
|
|
||||||
if (deltaY < -insetBounds.size.height)
|
|
||||||
deltaY = -insetBounds.size.height;
|
|
||||||
if (deltaY > insetBounds.size.height)
|
|
||||||
deltaY = insetBounds.size.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([scrollView hasHorizontalScroller])
|
|
||||||
{
|
|
||||||
if (eventLocation.x < CGRectGetMinX(insetBounds))
|
|
||||||
deltaX = CGRectGetMinX(insetBounds) - eventLocation.x;
|
|
||||||
else if (eventLocation.x > CGRectGetMaxX(insetBounds))
|
|
||||||
deltaX = CGRectGetMaxX(insetBounds) - eventLocation.x;
|
|
||||||
if (deltaX < -insetBounds.size.width)
|
|
||||||
deltaX = -insetBounds.size.width;
|
|
||||||
if (deltaX > insetBounds.size.width)
|
|
||||||
deltaX = insetBounds.size.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
[contentView scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dragOperation;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_sendPeriodicDraggingUpdate:(CPTimer)aTimer
|
|
||||||
{
|
|
||||||
var userInfo = [aTimer userInfo];
|
|
||||||
_dragOperation = [self draggingUpdatedInPlatformWindow:[userInfo objectForKey:@"platformWindow"]
|
|
||||||
location:[userInfo objectForKey:@"location"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)draggingEndedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation
|
|
||||||
{
|
|
||||||
[_draggingUpdateTimer invalidate];
|
|
||||||
_draggingUpdateTimer = nil;
|
|
||||||
|
|
||||||
[_draggedView removeFromSuperview];
|
|
||||||
|
|
||||||
if (![CPPlatform supportsDragAndDrop])
|
|
||||||
[_draggedWindow orderOut:self];
|
|
||||||
|
|
||||||
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_endedAt_operation_)
|
|
||||||
[_draggingSource draggedImage:[_draggedView image] endedAt:aLocation operation:anOperation];
|
|
||||||
else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_endedAt_operation_)
|
|
||||||
[_draggingSource draggedView:_draggedView endedAt:aLocation operation:anOperation];
|
|
||||||
|
|
||||||
_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.
|
Initiates a drag session.
|
||||||
@param aView the view being dragged
|
@param aView the view being dragged
|
||||||
@@ -343,72 +235,50 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
@param anEvent
|
@param anEvent
|
||||||
@param aPasteboard the pasteboard that contains the drag data
|
@param aPasteboard the pasteboard that contains the drag data
|
||||||
@param aSourceObject the object where the drag started
|
@param aSourceObject the object where the drag started
|
||||||
@param slideBack if \c YES, \c aView slides back to
|
@param slideBack if <code>YES</code>, <code>aView</code> slides back to
|
||||||
its origin on a failed drop
|
its origin on a failed drop
|
||||||
*/
|
*/
|
||||||
- (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
|
- (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
|
||||||
{
|
{
|
||||||
_isDragging = YES;
|
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;
|
||||||
|
|
||||||
_draggedView = aView;
|
[_dragWindow setFrameSize:CGSizeMakeCopy([[CPDOMWindowBridge sharedDOMWindowBridge] frame].size)];
|
||||||
_draggingPasteboard = aPasteboard || [CPPasteboard pasteboardWithName:CPDragPboard];
|
[_dragWindow orderFront:self];
|
||||||
_draggingSource = aSourceObject;
|
|
||||||
_draggingDestination = nil;
|
|
||||||
_shouldSlideBack = slideBack;
|
|
||||||
|
|
||||||
// The offset is based on the distance from where we want the view to be initially from where the mouse is initially
|
[aView setFrameOrigin:viewLocation];
|
||||||
// Hence the use of mouseDownEvent's location and view's location in global coordinates.
|
[[_dragWindow contentView] addSubview:aView];
|
||||||
var mouseDownWindow = [mouseDownEvent window],
|
|
||||||
mouseDownEventLocation = [mouseDownEvent locationInWindow];
|
|
||||||
|
|
||||||
if (mouseDownEventLocation)
|
if (CPDragServerIsDraggingImage)
|
||||||
{
|
{
|
||||||
if (mouseDownWindow)
|
if ([CPDragServerSource respondsToSelector:@selector(draggedImage:beganAt:)])
|
||||||
mouseDownEventLocation = [mouseDownWindow convertBaseToGlobal:mouseDownEventLocation];
|
[CPDragServerSource draggedImage:[aView image] beganAt:viewLocation];
|
||||||
|
|
||||||
_draggingOffset = _CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y);
|
CPDragServerShouldSendDraggedImageMovedTo = [CPDragServerSource respondsToSelector:@selector(draggedImage:movedTo:)];
|
||||||
}
|
CPDragServerShouldSendDraggedImageEndedAtOperation = [CPDragServerSource respondsToSelector:@selector(draggedImage:endAt:operation:)];
|
||||||
else
|
|
||||||
_draggingOffset = _CGSizeMakeZero();
|
CPDragServerShouldSendDraggedViewMovedTo = NO;
|
||||||
|
CPDragServerShouldSendDraggedViewEndedAtOperation = NO;
|
||||||
if ([CPPlatform isBrowser])
|
|
||||||
[_draggedWindow setPlatformWindow:[aWindow platformWindow]];
|
|
||||||
|
|
||||||
[aView setFrameOrigin:_CGPointMakeZero()];
|
|
||||||
|
|
||||||
var mouseLocation = [CPEvent mouseLocation];
|
|
||||||
|
|
||||||
// Place it where the mouse pointer is.
|
|
||||||
_startDragLocation = _CGPointMake(mouseLocation.x - _draggingOffset.width, mouseLocation.y - _draggingOffset.height);
|
|
||||||
[_draggedWindow setFrameOrigin:_startDragLocation];
|
|
||||||
[_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:endedAt:operation:)])
|
|
||||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_endedAt_operation_;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ([_draggingSource respondsToSelector:@selector(draggedView:movedTo:)])
|
if ([CPDragServerSource respondsToSelector:@selector(draggedView:beganAt:)])
|
||||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedView_movedTo_;
|
[CPDragServerSource draggedView:aView beganAt:viewLocation];
|
||||||
|
|
||||||
|
CPDragServerShouldSendDraggedViewMovedTo = [CPDragServerSource respondsToSelector:@selector(draggedView:movedTo:)];
|
||||||
|
CPDragServerShouldSendDraggedViewEndedAtOperation = [CPDragServerSource respondsToSelector:@selector(draggedView:endedAt:operation:)];
|
||||||
|
|
||||||
|
|
||||||
if ([_draggingSource respondsToSelector:@selector(draggedView:endedAt:operation:)])
|
CPDragServerShouldSendDraggedImageMovedTo = NO;
|
||||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedView_endedAt_operation_;
|
CPDragServerShouldSendDraggedImageEndedAtOperation = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![CPPlatform supportsDragAndDrop])
|
CPDragServerStartDragging(anEvent);
|
||||||
{
|
|
||||||
[self draggingStartedInPlatformWindow:[aWindow platformWindow] globalLocation:mouseLocation];
|
|
||||||
[self trackDragging:mouseDownEvent];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -420,62 +290,22 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
@param anEvent
|
@param anEvent
|
||||||
@param aPasteboard the pasteboard where the drag data is located
|
@param aPasteboard the pasteboard where the drag data is located
|
||||||
@param aSourceObject the object where the drag started
|
@param aSourceObject the object where the drag started
|
||||||
@param slideBack if \c YES, \c aView slides back to
|
@param slideBack if <code>YES</code>, <code>aView</code> slides back to
|
||||||
its origin on a failed drop
|
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
|
- (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
|
||||||
{
|
{
|
||||||
_isDraggingImage = YES;
|
CPDragServerIsDraggingImage = YES;
|
||||||
|
|
||||||
var imageSize = [anImage size];
|
|
||||||
|
|
||||||
if (!_imageView)
|
if (!_imageView)
|
||||||
_imageView = [[CPImageView alloc] initWithFrame:_CGRectMake(0.0, 0.0, imageSize.width, imageSize.height)];
|
_imageView = [[CPImageView alloc] initWithFrame:CPRectMakeZero()];
|
||||||
|
|
||||||
[_imageView setImage:anImage];
|
[_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];
|
[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)
|
|
||||||
{
|
|
||||||
// Make sure we do not finalize (cancel) the drag if the last drag update was disallowed
|
|
||||||
if (_dragOperation !== CPDragOperationNone)
|
|
||||||
[self performDragOperationInPlatformWindow:platformWindow];
|
|
||||||
|
|
||||||
[self draggingEndedInPlatformWindow:platformWindow globalLocation:platformWindowLocation operation:_dragOperation];
|
|
||||||
|
|
||||||
// Stop tracking events.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (type === CPKeyDown)
|
|
||||||
{
|
|
||||||
var keyCode = [anEvent keyCode];
|
|
||||||
if (keyCode === CPEscapeKeyCode)
|
|
||||||
{
|
|
||||||
_dragOperation = CPDragOperationNone;
|
|
||||||
[self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation];
|
|
||||||
_dragOperation = [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 | CPKeyDownMask
|
|
||||||
untilDate:nil inMode:0 dequeue:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPWindow (CPDraggingAdditions)
|
@implementation CPWindow (CPDraggingAdditions)
|
||||||
@@ -488,23 +318,23 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
// We don't need to do this because the only place this gets called
|
// We don't need to do this because the only place this gets called
|
||||||
// -_dragHitTest: in CPPlatformWindow does this already. Perhaps to
|
// -_dragHitTest: in CPDOMWindowBridge does this already. Perhaps to
|
||||||
// be safe?
|
// be safe?
|
||||||
// if (![self containsPoint:aPoint])
|
// if (![self containsPoint:aPoint])
|
||||||
// return nil;
|
// return nil;
|
||||||
|
|
||||||
var adjustedPoint = [self convertPlatformWindowToBase:aPoint],
|
var adjustedPoint = _CGPointMake(aPoint.x - _CGRectGetMinX(_frame), aPoint.y - _CGRectGetMinY(_frame)),
|
||||||
hitView = [_windowView hitTest:adjustedPoint];
|
hitView = [_windowView hitTest:adjustedPoint];
|
||||||
|
|
||||||
while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]])
|
while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]])
|
||||||
hitView = [hitView superview];
|
hitView = [hitView superview];
|
||||||
|
|
||||||
if (hitView)
|
if (hitView)
|
||||||
return hitView;
|
return hitView;
|
||||||
|
|
||||||
if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]])
|
if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]])
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+131
-208
@@ -21,29 +21,114 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import "CPText.j"
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPLeftMouseDown = 1;
|
CPLeftMouseDown = 1;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPLeftMouseUp = 2;
|
CPLeftMouseUp = 2;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPRightMouseDown = 3;
|
CPRightMouseDown = 3;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPRightMouseUp = 4;
|
CPRightMouseUp = 4;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPMouseMoved = 5;
|
CPMouseMoved = 5;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPLeftMouseDragged = 6;
|
CPLeftMouseDragged = 6;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPRightMouseDragged = 7;
|
CPRightMouseDragged = 7;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPMouseEntered = 8;
|
CPMouseEntered = 8;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPMouseExited = 9;
|
CPMouseExited = 9;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPKeyDown = 10;
|
CPKeyDown = 10;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPKeyUp = 11;
|
CPKeyUp = 11;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPFlagsChanged = 12;
|
CPFlagsChanged = 12;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPAppKitDefined = 13;
|
CPAppKitDefined = 13;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPSystemDefined = 14;
|
CPSystemDefined = 14;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPApplicationDefined = 15;
|
CPApplicationDefined = 15;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPPeriodic = 16;
|
CPPeriodic = 16;
|
||||||
CPCursorUpdate = 17;
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
|
CPCursorUpdate = 17;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPScrollWheel = 22;
|
CPScrollWheel = 22;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPOtherMouseDown = 25;
|
CPOtherMouseDown = 25;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPOtherMouseUp = 26;
|
CPOtherMouseUp = 26;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPEventType
|
||||||
|
*/
|
||||||
CPOtherMouseDragged = 27;
|
CPOtherMouseDragged = 27;
|
||||||
|
|
||||||
// iPhone Event Types
|
// iPhone Event Types
|
||||||
@@ -51,7 +136,8 @@ CPTouchStart = 28;
|
|||||||
CPTouchMove = 29;
|
CPTouchMove = 29;
|
||||||
CPTouchEnd = 30;
|
CPTouchEnd = 30;
|
||||||
CPTouchCancel = 31;
|
CPTouchCancel = 31;
|
||||||
|
|
||||||
|
|
||||||
CPAlphaShiftKeyMask = 1 << 16;
|
CPAlphaShiftKeyMask = 1 << 16;
|
||||||
CPShiftKeyMask = 1 << 17;
|
CPShiftKeyMask = 1 << 17;
|
||||||
CPControlKeyMask = 1 << 18;
|
CPControlKeyMask = 1 << 18;
|
||||||
@@ -85,102 +171,26 @@ CPPeriodicMask = 1 << CPPeriodic;
|
|||||||
CPScrollWheelMask = 1 << CPScrollWheel;
|
CPScrollWheelMask = 1 << CPScrollWheel;
|
||||||
CPAnyEventMask = 0xffffffff;
|
CPAnyEventMask = 0xffffffff;
|
||||||
|
|
||||||
CPUpArrowFunctionKey = "\uF700";
|
CPDOMEventDoubleClick = "dblclick",
|
||||||
CPDownArrowFunctionKey = "\uF701";
|
CPDOMEventMouseDown = "mousedown",
|
||||||
CPLeftArrowFunctionKey = "\uF702";
|
CPDOMEventMouseUp = "mouseup",
|
||||||
CPRightArrowFunctionKey = "\uF703";
|
CPDOMEventMouseMoved = "mousemove",
|
||||||
CPF1FunctionKey = "\uF704";
|
CPDOMEventMouseDragged = "mousedrag",
|
||||||
CPF2FunctionKey = "\uF705";
|
CPDOMEventKeyUp = "keyup",
|
||||||
CPF3FunctionKey = "\uF706";
|
CPDOMEventKeyDown = "keydown",
|
||||||
CPF4FunctionKey = "\uF707";
|
CPDOMEventKeyPress = "keypress";
|
||||||
CPF5FunctionKey = "\uF708";
|
CPDOMEventCopy = "copy";
|
||||||
CPF6FunctionKey = "\uF709";
|
CPDOMEventPaste = "paste";
|
||||||
CPF7FunctionKey = "\uF70A";
|
CPDOMEventScrollWheel = "mousewheel";
|
||||||
CPF8FunctionKey = "\uF70B";
|
CPDOMEventTouchStart = "touchstart";
|
||||||
CPF9FunctionKey = "\uF70C";
|
CPDOMEventTouchMove = "touchmove";
|
||||||
CPF10FunctionKey = "\uF70D";
|
CPDOMEventTouchEnd = "touchend";
|
||||||
CPF11FunctionKey = "\uF70E";
|
CPDOMEventTouchCancel = "touchcancel";
|
||||||
CPF12FunctionKey = "\uF70F";
|
|
||||||
CPF13FunctionKey = "\uF710";
|
|
||||||
CPF14FunctionKey = "\uF711";
|
|
||||||
CPF15FunctionKey = "\uF712";
|
|
||||||
CPF16FunctionKey = "\uF713";
|
|
||||||
CPF17FunctionKey = "\uF714";
|
|
||||||
CPF18FunctionKey = "\uF715";
|
|
||||||
CPF19FunctionKey = "\uF716";
|
|
||||||
CPF20FunctionKey = "\uF717";
|
|
||||||
CPF21FunctionKey = "\uF718";
|
|
||||||
CPF22FunctionKey = "\uF719";
|
|
||||||
CPF23FunctionKey = "\uF71A";
|
|
||||||
CPF24FunctionKey = "\uF71B";
|
|
||||||
CPF25FunctionKey = "\uF71C";
|
|
||||||
CPF26FunctionKey = "\uF71D";
|
|
||||||
CPF27FunctionKey = "\uF71E";
|
|
||||||
CPF28FunctionKey = "\uF71F";
|
|
||||||
CPF29FunctionKey = "\uF720";
|
|
||||||
CPF30FunctionKey = "\uF721";
|
|
||||||
CPF31FunctionKey = "\uF722";
|
|
||||||
CPF32FunctionKey = "\uF723";
|
|
||||||
CPF33FunctionKey = "\uF724";
|
|
||||||
CPF34FunctionKey = "\uF725";
|
|
||||||
CPF35FunctionKey = "\uF726";
|
|
||||||
CPInsertFunctionKey = "\uF727";
|
|
||||||
CPDeleteFunctionKey = "\uF728";
|
|
||||||
CPHomeFunctionKey = "\uF729";
|
|
||||||
CPBeginFunctionKey = "\uF72A";
|
|
||||||
CPEndFunctionKey = "\uF72B";
|
|
||||||
CPPageUpFunctionKey = "\uF72C";
|
|
||||||
CPPageDownFunctionKey = "\uF72D";
|
|
||||||
CPPrintScreenFunctionKey = "\uF72E";
|
|
||||||
CPScrollLockFunctionKey = "\uF72F";
|
|
||||||
CPPauseFunctionKey = "\uF730";
|
|
||||||
CPSysReqFunctionKey = "\uF731";
|
|
||||||
CPBreakFunctionKey = "\uF732";
|
|
||||||
CPResetFunctionKey = "\uF733";
|
|
||||||
CPStopFunctionKey = "\uF734";
|
|
||||||
CPMenuFunctionKey = "\uF735";
|
|
||||||
CPUserFunctionKey = "\uF736";
|
|
||||||
CPSystemFunctionKey = "\uF737";
|
|
||||||
CPPrintFunctionKey = "\uF738";
|
|
||||||
CPClearLineFunctionKey = "\uF739";
|
|
||||||
CPClearDisplayFunctionKey = "\uF73A";
|
|
||||||
CPInsertLineFunctionKey = "\uF73B";
|
|
||||||
CPDeleteLineFunctionKey = "\uF73C";
|
|
||||||
CPInsertCharFunctionKey = "\uF73D";
|
|
||||||
CPDeleteCharFunctionKey = "\uF73E";
|
|
||||||
CPPrevFunctionKey = "\uF73F";
|
|
||||||
CPNextFunctionKey = "\uF740";
|
|
||||||
CPSelectFunctionKey = "\uF741";
|
|
||||||
CPExecuteFunctionKey = "\uF742";
|
|
||||||
CPUndoFunctionKey = "\uF743";
|
|
||||||
CPRedoFunctionKey = "\uF744";
|
|
||||||
CPFindFunctionKey = "\uF745";
|
|
||||||
CPHelpFunctionKey = "\uF746";
|
|
||||||
CPModeSwitchFunctionKey = "\uF747";
|
|
||||||
CPEscapeFunctionKey = "\u001B";
|
|
||||||
|
|
||||||
|
|
||||||
CPDOMEventDoubleClick = "dblclick",
|
|
||||||
CPDOMEventMouseDown = "mousedown",
|
|
||||||
CPDOMEventMouseUp = "mouseup",
|
|
||||||
CPDOMEventMouseMoved = "mousemove",
|
|
||||||
CPDOMEventMouseDragged = "mousedrag",
|
|
||||||
CPDOMEventKeyUp = "keyup",
|
|
||||||
CPDOMEventKeyDown = "keydown",
|
|
||||||
CPDOMEventKeyPress = "keypress";
|
|
||||||
CPDOMEventCopy = "copy";
|
|
||||||
CPDOMEventPaste = "paste";
|
|
||||||
CPDOMEventScrollWheel = "mousewheel";
|
|
||||||
CPDOMEventTouchStart = "touchstart";
|
|
||||||
CPDOMEventTouchMove = "touchmove";
|
|
||||||
CPDOMEventTouchEnd = "touchend";
|
|
||||||
CPDOMEventTouchCancel = "touchcancel";
|
|
||||||
|
|
||||||
var _CPEventPeriodicEventPeriod = 0,
|
var _CPEventPeriodicEventPeriod = 0,
|
||||||
_CPEventPeriodicEventTimer = nil,
|
_CPEventPeriodicEventTimer = nil;
|
||||||
_CPEventUpperCaseRegex = new RegExp("[A-Z]");
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPEvent
|
@class CPEvent
|
||||||
CPEvent encapsulates the details of a Cappuccino keyboard or mouse event.
|
CPEvent encapsulates the details of a Cappuccino keyboard or mouse event.
|
||||||
@@ -202,7 +212,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
BOOL _isARepeat;
|
BOOL _isARepeat;
|
||||||
unsigned _keyCode;
|
unsigned _keyCode;
|
||||||
DOMEvent _DOMEvent;
|
DOMEvent _DOMEvent;
|
||||||
|
|
||||||
float _deltaX;
|
float _deltaX;
|
||||||
float _deltaY;
|
float _deltaY;
|
||||||
float _deltaZ;
|
float _deltaZ;
|
||||||
@@ -211,16 +221,16 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
/*!
|
/*!
|
||||||
Creates a new keyboard event.
|
Creates a new keyboard event.
|
||||||
@param anEventType the event type. Must be one of CPKeyDown, CPKeyUp or CPFlagsChanged
|
@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 \c aWindowNumber
|
@param aPoint the location of the cursor in the window specified by <code>aWindowNumber</code>
|
||||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||||
@param aTimestamp the time the event occurred
|
@param aTimestamp the time the event occurred
|
||||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||||
@param aGraphicsContext the graphics context where the event occurred
|
@param aGraphicsContext the graphics context where the event occurred
|
||||||
@param characters the characters associated with the event
|
@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 unmodCharacters the string of keys pressed without the presence of any modifiers other than Shift
|
||||||
@param repeatKey \c YES if this is caused by the system repeat as opposed to the user pressing the key again
|
@param repeatKey <code>YES</code> 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
|
@param code a number associated with the keyboard key of this event
|
||||||
@throws CPInternalInconsistencyException if \c anEventType is not a CPKeyDown,
|
@throws CPInternalInconsistencyException if <code>anEventType</code> is not a CPKeyDown,
|
||||||
CPKeyUp or CPFlagsChanged
|
CPKeyUp or CPFlagsChanged
|
||||||
@return the keyboard event
|
@return the keyboard event
|
||||||
*/
|
*/
|
||||||
@@ -229,14 +239,14 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code
|
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code
|
||||||
{
|
{
|
||||||
return [[self alloc] _initKeyEventWithType:anEventType location:aPoint modifierFlags:modifierFlags
|
return [[self alloc] _initKeyEventWithType:anEventType location:aPoint modifierFlags:modifierFlags
|
||||||
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext
|
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext
|
||||||
characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code];
|
characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a new mouse event
|
Creates a new mouse event
|
||||||
@param anEventType the event type
|
@param anEventType the event type
|
||||||
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
|
@param aPoint the location of the cursor in the window specified by <code>aWindowNumber</code>
|
||||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||||
@param aTimestamp the time the event occurred
|
@param aTimestamp the time the event occurred
|
||||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||||
@@ -247,7 +257,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
@throws CPInternalInconsistencyException if an invalid event type is provided
|
@throws CPInternalInconsistencyException if an invalid event type is provided
|
||||||
@return the new mouse event
|
@return the new mouse event
|
||||||
*/
|
*/
|
||||||
+ (id)mouseEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
+ (id)mouseEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||||
eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
|
eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
|
||||||
{
|
{
|
||||||
@@ -258,7 +268,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
/*!
|
/*!
|
||||||
Creates a new custom event
|
Creates a new custom event
|
||||||
@param anEventType the event type. Must be one of CPAppKitDefined, CPSystemDefined, CPApplicationDefined or CPPeriodic
|
@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 \c aWindowNumber
|
@param aLocation the location of the cursor in the window specified by <code>aWindowNumber</code>
|
||||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||||
@param aTimestamp the time the event occurred
|
@param aTimestamp the time the event occurred
|
||||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||||
@@ -278,12 +288,12 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||||
eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
|
eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_type = anEventType;
|
_type = anEventType;
|
||||||
@@ -296,7 +306,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
_pressure = aPressure;
|
_pressure = aPressure;
|
||||||
_window = [CPApp windowWithWindowNumber:aWindowNumber];
|
_window = [CPApp windowWithWindowNumber:aWindowNumber];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +316,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code
|
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_type = anEventType;
|
_type = anEventType;
|
||||||
@@ -320,7 +330,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
_keyCode = code;
|
_keyCode = code;
|
||||||
_windowNumber = aWindowNumber;
|
_windowNumber = aWindowNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,7 +340,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2
|
subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_type = anEventType;
|
_type = anEventType;
|
||||||
@@ -341,33 +351,22 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
_subtype = aSubtype;
|
_subtype = aSubtype;
|
||||||
_data1 = aData1;
|
_data1 = aData1;
|
||||||
_data2 = aData2;
|
_data2 = aData2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the location of the mouse (for mouse events).
|
Returns the location of the mouse (for mouse events).
|
||||||
If this is not a mouse event, it returns \c nil.
|
If this is not a mouse event, it returns <code>nil</code>.
|
||||||
If \c window returns \c nil, then
|
If <code>window</code> returns <code>nil</code>, then
|
||||||
the mouse coordinates will be based on the screen coordinates.
|
the mouse coordinates will be based on the screen coordinates.
|
||||||
Otherwise, the coordinates are relative to the window's coordinates.
|
Otherwise, the coordinates are relative to the window's coordinates.
|
||||||
@return the location of the mouse, or \c nil for non-mouse events.
|
@return the location of the mouse, or <code>nil</code> for non-mouse events.
|
||||||
*/
|
*/
|
||||||
- (CGPoint)locationInWindow
|
- (CGPoint)locationInWindow
|
||||||
{
|
{
|
||||||
return _CGPointMakeCopy(_location);
|
return _location;
|
||||||
}
|
|
||||||
|
|
||||||
- (CGPoint)globalLocation
|
|
||||||
{
|
|
||||||
var theWindow = [self window],
|
|
||||||
location = [self locationInWindow];
|
|
||||||
|
|
||||||
if (theWindow)
|
|
||||||
return [theWindow convertBaseToGlobal:location];
|
|
||||||
|
|
||||||
return location;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -419,10 +418,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
*/
|
*/
|
||||||
- (int)buttonNumber
|
- (int)buttonNumber
|
||||||
{
|
{
|
||||||
if (_type === CPRightMouseDown || _type === CPRightMouseUp || _type === CPRightMouseDragged)
|
return _buttonNumber;
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -452,7 +448,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the keyboard event was caused by the key being held down.
|
Returns <code>YES</code> if the keyboard event was caused by the key being held down.
|
||||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||||
*/
|
*/
|
||||||
- (BOOL)isARepeat
|
- (BOOL)isARepeat
|
||||||
@@ -469,18 +465,6 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
return _keyCode;
|
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
|
- (float)pressure
|
||||||
{
|
{
|
||||||
return _pressure;
|
return _pressure;
|
||||||
@@ -519,76 +503,15 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
return _deltaZ;
|
return _deltaZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_triggersKeyEquivalent:(CPString)aKeyEquivalent withModifierMask:aKeyEquivalentModifierMask
|
|
||||||
{
|
|
||||||
if (!aKeyEquivalent)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
if (_CPEventUpperCaseRegex.test(aKeyEquivalent))
|
|
||||||
aKeyEquivalentModifierMask |= CPShiftKeyMask;
|
|
||||||
|
|
||||||
if (CPBrowserIsOperatingSystem(CPWindowsOperatingSystem) && (aKeyEquivalentModifierMask & CPCommandKeyMask))
|
|
||||||
{
|
|
||||||
aKeyEquivalentModifierMask |= CPControlKeyMask;
|
|
||||||
aKeyEquivalentModifierMask &= ~CPCommandKeyMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((_modifierFlags & (CPShiftKeyMask | CPAlternateKeyMask | CPCommandKeyMask | CPControlKeyMask)) !== aKeyEquivalentModifierMask)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
// Treat \r and \n as the same key equivalent. See issue #710.
|
|
||||||
if (_characters === CPNewlineCharacter || _characters === CPCarriageReturnCharacter)
|
|
||||||
return CPNewlineCharacter === aKeyEquivalent || CPCarriageReturnCharacter === aKeyEquivalent;
|
|
||||||
|
|
||||||
return [_characters caseInsensitiveCompare:aKeyEquivalent] === CPOrderedSame;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_couldBeKeyEquivalent
|
|
||||||
{
|
|
||||||
if (_type !== CPKeyDown)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
var characterCount = _characters.length;
|
|
||||||
|
|
||||||
if (!characterCount)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
if (_modifierFlags & (CPCommandKeyMask | CPControlKeyMask))
|
|
||||||
return YES;
|
|
||||||
|
|
||||||
for(var i=0; i<characterCount; i++)
|
|
||||||
{
|
|
||||||
switch(_characters.charAt(i))
|
|
||||||
{
|
|
||||||
case CPBackspaceCharacter:
|
|
||||||
case CPDeleteCharacter:
|
|
||||||
case CPDeleteFunctionKey:
|
|
||||||
case CPTabCharacter:
|
|
||||||
case CPCarriageReturnCharacter:
|
|
||||||
case CPNewlineCharacter:
|
|
||||||
case CPEscapeFunctionKey:
|
|
||||||
case CPPageUpFunctionKey:
|
|
||||||
case CPPageDownFunctionKey:
|
|
||||||
case CPLeftArrowFunctionKey:
|
|
||||||
case CPUpArrowFunctionKey:
|
|
||||||
case CPRightArrowFunctionKey:
|
|
||||||
case CPDownArrowFunctionKey:
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// FIXME: More cases? Space?
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Gene rates periodic events every \c aPeriod seconds.
|
Generates periodic events every <code>aPeriod</code> seconds.
|
||||||
@param aDelay the number of seconds before the first event
|
@param aDelay the number of seconds before the first event
|
||||||
@param aPeriod the length of time in seconds between successive events
|
@param aPeriod the length of time in seconds between successive events
|
||||||
*/
|
*/
|
||||||
+ (void)startPeriodicEventsAfterDelay:(CPTimeInterval)aDelay withPeriod:(CPTimeInterval)aPeriod
|
+ (void)startPeriodicEventsAfterDelay:(CPTimeInterval)aDelay withPeriod:(CPTimeInterval)aPeriod
|
||||||
{
|
{
|
||||||
_CPEventPeriodicEventPeriod = aPeriod;
|
_CPEventPeriodicEventPeriod = aPeriod;
|
||||||
|
|
||||||
// FIXME: OH TIMERS!!!
|
// FIXME: OH TIMERS!!!
|
||||||
_CPEventPeriodicEventTimer = window.setTimeout(function() { _CPEventPeriodicEventTimer = window.setInterval(_CPEventFirePeriodEvent, aPeriod * 1000.0); }, aDelay * 1000.0);
|
_CPEventPeriodicEventTimer = window.setTimeout(function() { _CPEventPeriodicEventTimer = window.setInterval(_CPEventFirePeriodEvent, aPeriod * 1000.0); }, aDelay * 1000.0);
|
||||||
}
|
}
|
||||||
@@ -600,9 +523,9 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
{
|
{
|
||||||
if (_CPEventPeriodicEventTimer === nil)
|
if (_CPEventPeriodicEventTimer === nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
window.clearTimeout(_CPEventPeriodicEventTimer);
|
window.clearTimeout(_CPEventPeriodicEventTimer);
|
||||||
|
|
||||||
_CPEventPeriodicEventTimer = nil;
|
_CPEventPeriodicEventTimer = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-30
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPFlashMovie
|
@class CPFlashMovie
|
||||||
@@ -31,17 +30,17 @@
|
|||||||
*/
|
*/
|
||||||
@implementation CPFlashMovie : CPObject
|
@implementation CPFlashMovie : CPObject
|
||||||
{
|
{
|
||||||
CPString _filename;
|
CPString _fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a new Flash movie with the swf at \c aFileName.
|
Creates a new Flash movie with the swf at <code>aFileName</code>.
|
||||||
@param aFilename the swf to load
|
@param aFilename the swf to load
|
||||||
@return the initialized CPFlashMovie
|
@return the initialized CPFlashMovie
|
||||||
*/
|
*/
|
||||||
+ (id)flashMovieWithFile:(CPString)aFilename
|
+ (id)flashMovieWithFile:(CPString)aFileName
|
||||||
{
|
{
|
||||||
return [[self alloc] initWithFile:aFilename];
|
return [[self alloc] initWithFile:aFileName];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -49,37 +48,14 @@
|
|||||||
@param aFilename the swf to load
|
@param aFilename the swf to load
|
||||||
@return the initialized CPFlashMovie
|
@return the initialized CPFlashMovie
|
||||||
*/
|
*/
|
||||||
- (id)initWithFile:(CPString)aFilename
|
- (id)initWithFile:(CPString)aFileName
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
_filename = aFilename;
|
_fileName = aFileName;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPString)filename
|
|
||||||
{
|
|
||||||
return _filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPFlashMovieFilenameKey = "CPFlashMovieFilenameKey";
|
|
||||||
|
|
||||||
@implementation CPFlashMovie (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
_filename = [aCoder decodeObjectForKey:CPFlashMovieFilenameKey];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[aCoder encodeObject:_filename forKey:CPFlashMovieFilenameKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
+40
-131
@@ -20,12 +20,10 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@import "CPDOMWindowBridge.j"
|
||||||
@import "CPFlashMovie.j"
|
@import "CPFlashMovie.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
|
|
||||||
var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
*/
|
*/
|
||||||
@@ -33,13 +31,9 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
{
|
{
|
||||||
CPFlashMovie _flashMovie;
|
CPFlashMovie _flashMovie;
|
||||||
|
|
||||||
CPDictionary _params;
|
DOMElement _DOMEmbedElement;
|
||||||
CPDictionary _paramElements;
|
DOMElement _DOMMParamElement;
|
||||||
#if PLATFORM(DOM)
|
|
||||||
DOMElement _DOMParamElement;
|
|
||||||
DOMElement _DOMObjectElement;
|
DOMElement _DOMObjectElement;
|
||||||
DOMElement _DOMInnerObjectElement;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
@@ -48,32 +42,35 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
#if PLATFORM(DOM)
|
_DOMObjectElement = document.createElement("object");
|
||||||
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
_DOMObjectElement.width = "100%";
|
||||||
{
|
_DOMObjectElement.height = "100%";
|
||||||
_DOMObjectElement = document.createElement(@"object");
|
_DOMObjectElement.style.top = "0px";
|
||||||
_DOMObjectElement.width = @"100%";
|
_DOMObjectElement.style.left = "0px";
|
||||||
_DOMObjectElement.height = @"100%";
|
|
||||||
_DOMObjectElement.style.top = @"0px";
|
_DOMParamElement = document.createElement("param");
|
||||||
_DOMObjectElement.style.left = @"0px";
|
_DOMParamElement.name = "movie";
|
||||||
_DOMObjectElement.type = @"application/x-shockwave-flash";
|
|
||||||
_DOMObjectElement.setAttribute(@"classid", IEFlashCLSID);
|
_DOMObjectElement.appendChild(_DOMParamElement);
|
||||||
|
|
||||||
_DOMParamElement = document.createElement(@"param");
|
var param = document.createElement("param");
|
||||||
_DOMParamElement.name = @"movie";
|
|
||||||
|
param.name = "wmode";
|
||||||
_DOMInnerObjectElement = document.createElement(@"object");
|
param.value = "transparent";
|
||||||
_DOMInnerObjectElement.width = @"100%";
|
|
||||||
_DOMInnerObjectElement.height = @"100%";
|
_DOMObjectElement.appendChild(param);
|
||||||
|
|
||||||
_DOMObjectElement.appendChild(_DOMParamElement);
|
_DOMEmbedElement = document.createElement("embed");
|
||||||
_DOMObjectElement.appendChild(_DOMInnerObjectElement);
|
|
||||||
|
_DOMEmbedElement.type = "application/x-shockwave-flash";
|
||||||
_DOMElement.appendChild(_DOMObjectElement);
|
_DOMEmbedElement.setAttribute("wmode", "transparent");
|
||||||
}
|
_DOMEmbedElement.width = "100%";
|
||||||
else
|
_DOMEmbedElement.height = "100%";
|
||||||
[self _rebuildIEObjects];
|
|
||||||
#endif
|
// IE requires this thing to be in the _DOMElement and not the _DOMObjectElement.
|
||||||
|
_DOMElement.appendChild(_DOMEmbedElement);
|
||||||
|
|
||||||
|
_DOMElement.appendChild(_DOMObjectElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -85,15 +82,11 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_flashMovie = aFlashMovie;
|
_flashMovie = aFlashMovie;
|
||||||
#if PLATFORM(DOM)
|
|
||||||
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
_DOMParamElement.value = aFlashMovie._fileName;
|
||||||
{
|
|
||||||
_DOMParamElement.value = [aFlashMovie filename];
|
if (_DOMEmbedElement)
|
||||||
_DOMInnerObjectElement.data = [aFlashMovie filename];
|
_DOMEmbedElement.src = aFlashMovie._fileName;
|
||||||
}
|
|
||||||
else
|
|
||||||
[self _rebuildIEObjects];
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPFlashMovie)flashMovie
|
- (CPFlashMovie)flashMovie
|
||||||
@@ -101,103 +94,19 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
return _flashMovie;
|
return _flashMovie;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFlashVars:(CPDictionary)aDictionary
|
|
||||||
{
|
|
||||||
var varString = @"",
|
|
||||||
enumerator = [aDictionary keyEnumerator];
|
|
||||||
|
|
||||||
var key;
|
|
||||||
while (key = [enumerator nextObject])
|
|
||||||
varString = [varString stringByAppendingFormat:@"&%@=%@", key, [aDictionary objectForKey:key]];
|
|
||||||
|
|
||||||
if (!_params)
|
|
||||||
_params = [CPDictionary dictionary];
|
|
||||||
|
|
||||||
[_params setObject:varString forKey:@"flashvars"];
|
|
||||||
[self setParameters:_params];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPDictionary)flashVars
|
|
||||||
{
|
|
||||||
return [_params objectForKey:@"flashvars"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setParameters:(CPDictionary)aDictionary
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
if (_paramElements && !CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
{
|
|
||||||
var elements = [_paramElements allValues],
|
|
||||||
count = [elements count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
_DOMObjectElement.removeChild([elements objectAtIndex:i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
_params = aDictionary;
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
{
|
|
||||||
_paramElements = [CPDictionary dictionary];
|
|
||||||
|
|
||||||
var enumerator = [_params keyEnumerator],
|
|
||||||
key;
|
|
||||||
|
|
||||||
while (key = [enumerator nextObject] && _DOMObjectElement)
|
|
||||||
{
|
|
||||||
var param = document.createElement(@"param");
|
|
||||||
param.name = key;
|
|
||||||
param.value = [_params objectForKey:key];
|
|
||||||
|
|
||||||
_DOMObjectElement.appendChild(param);
|
|
||||||
|
|
||||||
[_paramElements setObject:param forKey:key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[self _rebuildIEObjects];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPDictionary)parameters
|
|
||||||
{
|
|
||||||
return _params;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
- (void)_rebuildIEObjects
|
|
||||||
{
|
|
||||||
_DOMElement.innerHTML = @"";
|
|
||||||
if (![_flashMovie filename])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var paramString = [CPString stringWithFormat:@"<param name='movie' value='%@' />", [_flashMovie filename]],
|
|
||||||
paramEnumerator = [_params keyEnumerator],
|
|
||||||
key;
|
|
||||||
|
|
||||||
while (key = [paramEnumerator nextObject])
|
|
||||||
paramString = [paramString stringByAppendingFormat:@"<param name='%@' value='%@' />", key, [_params objectForKey:key]];
|
|
||||||
|
|
||||||
_DOMObjectElement = document.createElement(@"object");
|
|
||||||
_DOMElement.appendChild(_DOMObjectElement);
|
|
||||||
|
|
||||||
_DOMObjectElement.outerHTML = [CPString stringWithFormat:@"<object classid=%@ width=%@ height=%@>%@</object>", IEFlashCLSID, CGRectGetWidth([self bounds]), CGRectGetHeight([self bounds]), paramString];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
- (void)mouseDragged:(CPEvent)anEvent
|
- (void)mouseDragged:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseUp:(CPEvent)anEvent
|
- (void)mouseUp:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+12
-90
@@ -20,21 +20,12 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
var _CPFonts = {};
|
||||||
@import <Foundation/CPBundle.j>
|
_CPFontSystemFontFace = @"Arial";
|
||||||
|
|
||||||
@import "CPView.j"
|
#define _CPCachedFont(aName, aSize, isBold) _CPFonts[(isBold ? @"bold " : @"") + ROUND(aSize) + @"px '" + aName + @"'"]
|
||||||
|
|
||||||
|
/*!
|
||||||
var _CPFonts = {},
|
|
||||||
_CPFontSystemFontFace = @"Arial, sans-serif",
|
|
||||||
_CPWrapRegExp = new RegExp("\\s*,\\s*", "g");
|
|
||||||
|
|
||||||
|
|
||||||
#define _CPCreateCSSString(aName, aSize, isBold) (isBold ? @"bold " : @"") + ROUND(aSize) + @"px " + ((aName === _CPFontSystemFontFace) ? aName : (@"\"" + aName.replace(_CPWrapRegExp, '", "') + @"\", " + _CPFontSystemFontFace))
|
|
||||||
#define _CPCachedFont(aName, aSize, isBold) _CPFonts[_CPCreateCSSString(aName, aSize, isBold)]
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPFont
|
@class CPFont
|
||||||
|
|
||||||
@@ -44,22 +35,11 @@ var _CPFonts = {},
|
|||||||
{
|
{
|
||||||
CPString _name;
|
CPString _name;
|
||||||
float _size;
|
float _size;
|
||||||
float _ascender;
|
|
||||||
float _descender;
|
|
||||||
float _lineHeight;
|
|
||||||
BOOL _isBold;
|
BOOL _isBold;
|
||||||
|
|
||||||
CPString _cssString;
|
CPString _cssString;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
var systemFont = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:"CPSystemFontFace"];
|
|
||||||
|
|
||||||
if (systemFont)
|
|
||||||
_CPFontSystemFontFace = systemFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a font with the specified name and size.
|
Returns a font with the specified name and size.
|
||||||
@param aName the name of the font
|
@param aName the name of the font
|
||||||
@@ -106,64 +86,25 @@ var _CPFonts = {},
|
|||||||
@ignore
|
@ignore
|
||||||
*/
|
*/
|
||||||
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold
|
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_name = aName;
|
_name = aName;
|
||||||
_size = aSize;
|
_size = aSize;
|
||||||
_ascender = 0;
|
|
||||||
_descender = 0;
|
|
||||||
_lineHeight = 0;
|
|
||||||
_isBold = isBold;
|
_isBold = isBold;
|
||||||
|
|
||||||
_cssString = _CPCreateCSSString(_name, _size, _isBold);
|
_cssString = (_isBold ? @"bold " : @"") + ROUND(aSize) + @"px '" + aName + @"'";
|
||||||
|
|
||||||
_CPFonts[_cssString] = self;
|
_CPFonts[_cssString] = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the distance of the longest ascender's top y-coordinate from the baseline (in CSS px)
|
Returns the font size (in points)
|
||||||
*/
|
|
||||||
- (float)ascender
|
|
||||||
{
|
|
||||||
if (!_ascender)
|
|
||||||
[self _getMetrics];
|
|
||||||
|
|
||||||
return _ascender;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the bottom y coordinate (in CSS px), offset from the baseline, of the receiver's longest descender.
|
|
||||||
Thus, if the longest descender extends 2 points below the baseline, descender will return –2.
|
|
||||||
*/
|
|
||||||
- (float)descender
|
|
||||||
{
|
|
||||||
if (!_descender)
|
|
||||||
[self _getMetrics];
|
|
||||||
|
|
||||||
return _descender;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the default line height.
|
|
||||||
|
|
||||||
NOTE: This was moved from NSFont to NSLayoutManager in Cocoa, but since there is no CPLayoutManager, it has been kept here.
|
|
||||||
*/
|
|
||||||
- (float)defaultLineHeightForFont
|
|
||||||
{
|
|
||||||
if (!_lineHeight)
|
|
||||||
[self _getMetrics];
|
|
||||||
|
|
||||||
return _lineHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the font size (in CSS px)
|
|
||||||
*/
|
*/
|
||||||
- (float)size
|
- (float)size
|
||||||
{
|
{
|
||||||
@@ -186,25 +127,6 @@ var _CPFonts = {},
|
|||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)anObject
|
|
||||||
{
|
|
||||||
return [anObject isKindOfClass:[CPFont class]] && [anObject cssString] === [self cssString];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
return [CPString stringWithFormat:@"%@ %@ %f pt.", [super description], [self familyName], [self size]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_getMetrics
|
|
||||||
{
|
|
||||||
var metrics = [CPString metricsOfFont:self];
|
|
||||||
|
|
||||||
_ascender = [metrics objectForKey:@"ascender"];
|
|
||||||
_descender = [metrics objectForKey:@"descender"];
|
|
||||||
_lineHeight = [metrics objectForKey:@"lineHeight"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPFontNameKey = @"CPFontNameKey",
|
var CPFontNameKey = @"CPFontNameKey",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
|
||||||
@import "CPFont.j"
|
@import <AppKit/CPFont.j>
|
||||||
|
|
||||||
|
|
||||||
var CPSharedFontManager = nil,
|
var CPSharedFontManager = nil,
|
||||||
@@ -51,7 +51,7 @@ var CPSharedFontManager = nil,
|
|||||||
|
|
||||||
// Changing the Default Font Conversion Classes
|
// Changing the Default Font Conversion Classes
|
||||||
/*!
|
/*!
|
||||||
Sets the class that will be used to create the application's
|
Sets the class that will be used to create the applcation's
|
||||||
font manager.
|
font manager.
|
||||||
*/
|
*/
|
||||||
+ (void)setFontManagerFactory:(Class)aClass
|
+ (void)setFontManagerFactory:(Class)aClass
|
||||||
|
|||||||
+45
-59
@@ -62,15 +62,15 @@ function CPPointMake(x, y)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Makes a CGRect with an origin and size equal to \c aRect less the \c dX/dY insets specified.
|
Makes a CGRect with an origin and size equal to <code>aRect</code> less the <code>dX/dY</code> insets specified.
|
||||||
@param dX the size of the inset in the x-axis
|
@param dX the size of the inset in the x-axis
|
||||||
@param dY the size of the inset in the y-axis
|
@param dY the size of the inset in the y-axis
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@return CGRect a rectangle like \c aRect with an inset
|
@return CGRect a rectangle like <code>aRect</code> with an inset
|
||||||
*/
|
*/
|
||||||
function CPRectInset(aRect, dX, dY)
|
function CPRectInset(aRect, dX, dY)
|
||||||
{
|
{
|
||||||
return CPRectMake( aRect.origin.x + dX, aRect.origin.y + dY,
|
return CPRectMake( aRect.origin.x + dX, aRect.origin.y + dY,
|
||||||
aRect.size.width - 2 * dX, aRect.size.height - 2*dY);
|
aRect.size.width - 2 * dX, aRect.size.height - 2*dY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,13 +96,13 @@ function CPRectIntegral(aRect)
|
|||||||
function CPRectIntersection(lhsRect, rhsRect)
|
function CPRectIntersection(lhsRect, rhsRect)
|
||||||
{
|
{
|
||||||
var intersection = CPRectMake(
|
var intersection = CPRectMake(
|
||||||
Math.max(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)),
|
Math.max(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)),
|
||||||
Math.max(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
|
Math.max(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
|
||||||
0, 0);
|
0, 0);
|
||||||
|
|
||||||
intersection.size.width = Math.min(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)) - CPRectGetMinX(intersection);
|
intersection.size.width = Math.min(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)) - CPRectGetMinX(intersection);
|
||||||
intersection.size.height = Math.min(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect)) - CPRectGetMinY(intersection);
|
intersection.size.height = Math.min(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect)) - CPRectGetMinY(intersection);
|
||||||
|
|
||||||
return CPRectIsEmpty(intersection) ? CPRectMakeZero() : intersection;
|
return CPRectIsEmpty(intersection) ? CPRectMakeZero() : intersection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ function CPRectMake(x, y, width, height)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a new rectangle with its origin offset by \c dX and \c dY.
|
Creates a new rectangle with its origin offset by <code>dX</code> and <code>dY</code>.
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect the rectangle to copy the origin and size from
|
@param aRect the rectangle to copy the origin and size from
|
||||||
@param dX the amount added to the x-size of the new rectangle
|
@param dX the amount added to the x-size of the new rectangle
|
||||||
@@ -160,18 +160,18 @@ function CPRectStandardize(aRect)
|
|||||||
standardized.origin.x += width;
|
standardized.origin.x += width;
|
||||||
standardized.size.width = -width;
|
standardized.size.width = -width;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height < 0.0)
|
if (height < 0.0)
|
||||||
{
|
{
|
||||||
standardized.origin.y += height;
|
standardized.origin.y += height;
|
||||||
standardized.size.height = -height;
|
standardized.size.height = -height;
|
||||||
}
|
}
|
||||||
|
|
||||||
return standardized;
|
return standardized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the smallest rectangle that can contain the two argument \c CGRects.
|
Returns the smallest rectangle that can contain the two argument <code>CGRect</code>s.
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param lhsRect the first CGRect to use for the union calculation
|
@param lhsRect the first CGRect to use for the union calculation
|
||||||
@param rhsRect the second CGRect to use for the union calculation
|
@param rhsRect the second CGRect to use for the union calculation
|
||||||
@@ -183,7 +183,7 @@ function CPRectUnion(lhsRect, rhsRect)
|
|||||||
minY = Math.min(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
|
minY = Math.min(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
|
||||||
maxX = Math.max(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)),
|
maxX = Math.max(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)),
|
||||||
maxY = Math.max(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect));
|
maxY = Math.max(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect));
|
||||||
|
|
||||||
return CPRectMake(minX, minY, maxX - minX, maxY - minY);
|
return CPRectMake(minX, minY, maxX - minX, maxY - minY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,41 +211,41 @@ function CPSizeMake(width, height)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the CGRect, \c aRect, contains
|
Returns <code>YES</code> if the CGRect, <code>aRect</code>, contains
|
||||||
the CGPoint, \c aPoint.
|
the CGPoint, <code>aPoint</code>.
|
||||||
@param aRect the rectangle to test with
|
@param aRect the rectangle to test with
|
||||||
@param aPoint the point to test with
|
@param aPoint the point to test with
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@return BOOL \c YES if the rectangle contains the point, \c NO otherwise.
|
@return BOOL <code>YES</code> if the rectangle contains the point, <code>NO</code> otherwise.
|
||||||
*/
|
*/
|
||||||
function CPRectContainsPoint(aRect, aPoint)
|
function CPRectContainsPoint(aRect, aPoint)
|
||||||
{
|
{
|
||||||
return aPoint.x >= CPRectGetMinX(aRect) &&
|
return aPoint.x >= CPRectGetMinX(aRect) &&
|
||||||
aPoint.y >= CPRectGetMinY(aRect) &&
|
aPoint.y >= CPRectGetMinY(aRect) &&
|
||||||
aPoint.x < CPRectGetMaxX(aRect) &&
|
aPoint.x < CPRectGetMaxX(aRect) &&
|
||||||
aPoint.y < CPRectGetMaxY(aRect);
|
aPoint.y < CPRectGetMaxY(aRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a \c BOOL indicating whether CGRect \c possibleOuter
|
Returns a <code>BOOL</code> indicating whether CGRect <code>possibleOuter</code>
|
||||||
contains CGRect \c possibleInner.
|
contains CGRect <code>possibleInner</code>.
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param possibleOuter the CGRect to test if \c possibleInner is inside of
|
@param possibleOuter the CGRect to test if <code>possibleInner</code> is inside of
|
||||||
@param possibleInner the CGRect to test if it fits inside \c possibleOuter.
|
@param possibleInner the CGRect to test if it fits inside <code>possibleOuter</code>.
|
||||||
@return BOOL \c YES if \c possibleInner fits inside \c possibleOuter.
|
@return BOOL <code>YES</code> if <code>possibleInner</code> fits inside <code>possibleOuter</code>.
|
||||||
*/
|
*/
|
||||||
function CPRectContainsRect(possibleOuter, possibleInner)
|
function CPRectContainsRect(lhsRect, rhsRect)
|
||||||
{
|
{
|
||||||
return CGRectContainsRect(possibleOuter, possibleInner);
|
return CPRectEqualToRect(CPUnionRect(lhsRect, rhsRect), rhsRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Tests whether the two CGPoints are equal to each other by comparing their
|
Tests whether the two CGPoints are equal to each other by comparing their
|
||||||
\c x and \c y members.
|
<code>x</code> and <code>y</code> members.
|
||||||
@group @CGPoint
|
@group @CGPoint
|
||||||
@param lhsPoint the first CGPoint to check
|
@param lhsPoint the first CGPoint to check
|
||||||
@param rhsPoint the second CGPoint to check
|
@param rhsPoint the second CGPoint to check
|
||||||
@return BOOL \c YES if the two points have the same x's, and the same y's.
|
@return BOOL <code>YES</code> if the two points have the same x's, and the same y's.
|
||||||
*/
|
*/
|
||||||
function CPPointEqualToPoint(lhsPoint, rhsPoint)
|
function CPPointEqualToPoint(lhsPoint, rhsPoint)
|
||||||
{
|
{
|
||||||
@@ -257,11 +257,11 @@ function CPPointEqualToPoint(lhsPoint, rhsPoint)
|
|||||||
@group CGRect
|
@group CGRect
|
||||||
@param lhsRect the first CGRect to compare
|
@param lhsRect the first CGRect to compare
|
||||||
@param rhsRect the second CGRect to compare
|
@param rhsRect the second CGRect to compare
|
||||||
@return BOOL \c YES if the two rectangles have the same origin and size. \c NO, otherwise.
|
@return BOOL <code>YES</code> if the two rectangles have the same origin and size. <code>NO</code>, otherwise.
|
||||||
*/
|
*/
|
||||||
function CPRectEqualToRect(lhsRect, rhsRect)
|
function CPRectEqualToRect(lhsRect, rhsRect)
|
||||||
{
|
{
|
||||||
return CPPointEqualToPoint(lhsRect.origin, rhsRect.origin) &&
|
return CPPointEqualToPoint(lhsRect.origin, rhsRect.origin) &&
|
||||||
CPSizeEqualToSize(lhsRect.size, rhsRect.size);
|
CPSizeEqualToSize(lhsRect.size, rhsRect.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,11 +346,11 @@ function CPRectGetWidth(aRect)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the two rectangles intersect
|
Returns <code>YES</code> if the two rectangles intersect
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param lhsRect the first CGRect
|
@param lhsRect the first CGRect
|
||||||
@param rhsRect the second CGRect
|
@param rhsRect the second CGRect
|
||||||
@return BOOL \c YES if the two rectangles have any common spaces, and \c NO, otherwise.
|
@return BOOL <code>YES</code> if the two rectangles have any common spaces, and <code>NO</code>, otherwise.
|
||||||
*/
|
*/
|
||||||
function CPRectIntersectsRect(lhsRect, rhsRect)
|
function CPRectIntersectsRect(lhsRect, rhsRect)
|
||||||
{
|
{
|
||||||
@@ -358,11 +358,11 @@ function CPRectIntersectsRect(lhsRect, rhsRect)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the CGRect has no area.
|
Returns <code>YES</code> if the CGRect has no area.
|
||||||
The test is performed by checking if the width and height are both zero.
|
The test is performed by checking if the width and height are both zero.
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect the CGRect to test
|
@param aRect the CGRect to test
|
||||||
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
@return BOOL <code>YES</code> if the CGRect has no area, and <code>NO</code>, otherwise.
|
||||||
*/
|
*/
|
||||||
function CPRectIsEmpty(aRect)
|
function CPRectIsEmpty(aRect)
|
||||||
{
|
{
|
||||||
@@ -370,10 +370,10 @@ function CPRectIsEmpty(aRect)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the CGRect has no area.
|
Returns <code>YES</code> if the CGRect has no area.
|
||||||
The test is performed by checking if the width and height are both zero.
|
The test is performed by checking if the width and height are both zero.
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
@return BOOL <code>YES</code> if the CGRect has no area, and <code>NO</code>, otherwise.
|
||||||
*/
|
*/
|
||||||
function CPRectIsNull(aRect)
|
function CPRectIsNull(aRect)
|
||||||
{
|
{
|
||||||
@@ -381,25 +381,11 @@ function CPRectIsNull(aRect)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates two rectangles -- slice and rem -- from inRect, by dividing inRect
|
Returns <code>YES</code> if the two CGSizes are identical.
|
||||||
with a line that's parallel to the side of inRect specified by edge.
|
|
||||||
The size of slice is determined by amount, which specifies the distance from edge.
|
|
||||||
|
|
||||||
slice and rem must not be NULL.
|
|
||||||
|
|
||||||
@group CGRect
|
|
||||||
*/
|
|
||||||
function CPDivideRect(inRect, slice, rem, amount, edge)
|
|
||||||
{
|
|
||||||
CGRectDivide(inRect, slice, rem, amount, edge);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c YES if the two CGSizes are identical.
|
|
||||||
@group CGSize
|
@group CGSize
|
||||||
@param lhsSize the first CGSize to compare
|
@param lhsSize the first CGSize to compare
|
||||||
@param rhsSize the second CGSize to compare
|
@param rhsSize the second CGSize to compare
|
||||||
@return BOOL \c YES if the two sizes are identical. \c NO, otherwise.
|
@return BOOL <code>YES</code> if the two sizes are identical. <code>NO</code>, otherwise.
|
||||||
*/
|
*/
|
||||||
function CPSizeEqualToSize(lhsSize, rhsSize)
|
function CPSizeEqualToSize(lhsSize, rhsSize)
|
||||||
{
|
{
|
||||||
@@ -448,7 +434,7 @@ function CPStringFromRect(aRect)
|
|||||||
function CPPointFromString(aString)
|
function CPPointFromString(aString)
|
||||||
{
|
{
|
||||||
var comma = aString.indexOf(',');
|
var comma = aString.indexOf(',');
|
||||||
|
|
||||||
return { x:parseFloat(aString.substr(1, comma - 1), 10), y:parseFloat(aString.substring(comma + 1, aString.length), 10) };
|
return { x:parseFloat(aString.substr(1, comma - 1), 10), y:parseFloat(aString.substring(comma + 1, aString.length), 10) };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,20 +447,20 @@ function CPPointFromString(aString)
|
|||||||
function CPSizeFromString(aString)
|
function CPSizeFromString(aString)
|
||||||
{
|
{
|
||||||
var comma = aString.indexOf(',');
|
var comma = aString.indexOf(',');
|
||||||
|
|
||||||
return { width:parseFloat(aString.substr(1, comma - 1), 10), height:parseFloat(aString.substring(comma + 1, aString.length), 10) };
|
return { width:parseFloat(aString.substr(1, comma - 1), 10), height:parseFloat(aString.substring(comma + 1, aString.length), 10) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a CGRect created from a string.
|
Returns a CGRect created from a string.
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aString a string in the form generated by \c CPStringFromRect
|
@param aString a string in the form generated by <code>CPStringFromRect</code>
|
||||||
@return CGRect the rectangle created from the string
|
@return CGRect the rectangle created from the string
|
||||||
*/
|
*/
|
||||||
function CPRectFromString(aString)
|
function CPRectFromString(aString)
|
||||||
{
|
{
|
||||||
var comma = aString.indexOf(',', aString.indexOf(',') + 1);
|
var comma = aString.indexOf(',', aString.indexOf(',') + 1);
|
||||||
|
|
||||||
return { origin:CPPointFromString(aString.substr(1, comma - 1)), size:CPSizeFromString(aString.substring(comma + 2, aString.length)) };
|
return { origin:CPPointFromString(aString.substr(1, comma - 1)), size:CPSizeFromString(aString.substring(comma + 2, aString.length)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,7 +477,7 @@ function CPPointFromEvent(anEvent)
|
|||||||
/*!
|
/*!
|
||||||
Returns a zero sized CGSize.
|
Returns a zero sized CGSize.
|
||||||
@group CGSize
|
@group CGSize
|
||||||
@return CGSize a size object with zeros for \c width and \c height
|
@return CGSize a size object with zeros for <code>width</code> and <code>height</code>
|
||||||
*/
|
*/
|
||||||
function CPSizeMakeZero()
|
function CPSizeMakeZero()
|
||||||
{
|
{
|
||||||
@@ -499,7 +485,7 @@ function CPSizeMakeZero()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a rectangle at origin \c (0,0) and size of \c (0,0).
|
Returns a rectangle at origin <code>(0,0)</code> and size of <code>(0,0)</code>.
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@return CGRect a zeroed out CGRect
|
@return CGRect a zeroed out CGRect
|
||||||
*/
|
*/
|
||||||
@@ -509,15 +495,15 @@ function CPRectMakeZero()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a point located at \c (0, 0).
|
Returns a point located at <code>(0, 0)</code>.
|
||||||
@group CGPoint
|
@group CGPoint
|
||||||
@return CGPoint a point located at \c (0, 0)
|
@return CGPoint a point located at <code>(0, 0)</code>
|
||||||
*/
|
*/
|
||||||
function CPPointMakeZero()
|
function CPPointMakeZero()
|
||||||
{
|
{
|
||||||
return CPPointMake(0, 0, 0);
|
return CPPointMake(0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@}
|
@}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPGraphics.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Francisco Tolmasky.
|
|
||||||
* Copyright 2010, 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 "CPColor.j"
|
|
||||||
@import "CPGraphicsContext.j"
|
|
||||||
|
|
||||||
|
|
||||||
function CPDrawTiledRects(
|
|
||||||
/* CGRect */ boundsRect,
|
|
||||||
/* CGRect */ clipRect,
|
|
||||||
/* CPRectEdge[] */ sides,
|
|
||||||
/* float[] */ grays)
|
|
||||||
{
|
|
||||||
if (sides.length != grays.length)
|
|
||||||
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and grays (length: " + grays.length + ") must have the same length."];
|
|
||||||
|
|
||||||
var colors = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < grays.length; ++i)
|
|
||||||
colors.push([CPColor colorWithCalibratedWhite:grays[i] alpha:1.0]);
|
|
||||||
|
|
||||||
return CPDrawColorTiledRects(boundsRect, clipRect, sides, colors);
|
|
||||||
}
|
|
||||||
|
|
||||||
function CPDrawColorTiledRects(
|
|
||||||
/* CGRect */ boundsRect,
|
|
||||||
/* CGRect */ clipRect,
|
|
||||||
/* CPRectEdge[] */ sides,
|
|
||||||
/* CPColor[] */ colors)
|
|
||||||
{
|
|
||||||
if (sides.length != colors.length)
|
|
||||||
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and colors (length: " + colors.length + ") must have the same length."];
|
|
||||||
|
|
||||||
var resultRect = _CGRectMakeCopy(boundsRect),
|
|
||||||
slice = _CGRectMakeZero(),
|
|
||||||
remainder = _CGRectMakeZero(),
|
|
||||||
context = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
CGContextSaveGState(context);
|
|
||||||
CGContextSetLineWidth(context, 1.0);
|
|
||||||
|
|
||||||
for (var sideIndex = 0; sideIndex < sides.length; ++sideIndex)
|
|
||||||
{
|
|
||||||
var side = sides[sideIndex];
|
|
||||||
|
|
||||||
CGRectDivide(resultRect, slice, remainder, 1.0, side);
|
|
||||||
resultRect = remainder;
|
|
||||||
slice = CGRectIntersection(slice, clipRect);
|
|
||||||
|
|
||||||
// Cocoa docs say that only slices that are within the clipRect are actually drawn
|
|
||||||
if (_CGRectIsEmpty(slice))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var minX, maxX, minY, maxY;
|
|
||||||
|
|
||||||
if (side == CPMinXEdge || side == CPMaxXEdge)
|
|
||||||
{
|
|
||||||
// Make sure we have at least 1 pixel to draw a line
|
|
||||||
if (_CGRectGetWidth(slice) < 1.0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
minX = _CGRectGetMinX(slice) + 0.5;
|
|
||||||
maxX = minX;
|
|
||||||
minY = _CGRectGetMinY(slice);
|
|
||||||
maxY = _CGRectGetMaxY(slice);
|
|
||||||
}
|
|
||||||
else // CPMinYEdge || CPMaxYEdge
|
|
||||||
{
|
|
||||||
// Make sure we have at least 1 pixel to draw a line
|
|
||||||
if (_CGRectGetHeight(slice) < 1.0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
minX = _CGRectGetMinX(slice);
|
|
||||||
maxX = _CGRectGetMaxX(slice);
|
|
||||||
minY = _CGRectGetMinY(slice) + 0.5;
|
|
||||||
maxY = minY;
|
|
||||||
}
|
|
||||||
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
CGContextMoveToPoint(context, minX, minY);
|
|
||||||
CGContextAddLineToPoint(context, maxX, maxY);
|
|
||||||
CGContextSetStrokeColor(context, colors[sideIndex]);
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
CGContextRestoreGState(context);
|
|
||||||
|
|
||||||
return resultRect;
|
|
||||||
}
|
|
||||||
@@ -20,11 +20,6 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
|
|
||||||
@import "CGContext.j"
|
|
||||||
|
|
||||||
|
|
||||||
var CPGraphicsContextCurrent = nil;
|
var CPGraphicsContextCurrent = nil;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
+40
-101
@@ -19,7 +19,7 @@
|
|||||||
* License along with this library; if not, write to the Free Software
|
* License along with this library; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPBundle.j>
|
@import <Foundation/CPBundle.j>
|
||||||
@import <Foundation/CPNotificationCenter.j>
|
@import <Foundation/CPNotificationCenter.j>
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
@import "CPGeometry.j"
|
@import "CPGeometry.j"
|
||||||
|
|
||||||
|
|
||||||
CPImageLoadStatusInitialized = 0;
|
CPImageLoadStatusInitialized = 0;
|
||||||
CPImageLoadStatusLoading = 1;
|
CPImageLoadStatusLoading = 1;
|
||||||
CPImageLoadStatusCompleted = 2;
|
CPImageLoadStatusCompleted = 2;
|
||||||
@@ -39,32 +38,26 @@ CPImageLoadStatusReadError = 6;
|
|||||||
|
|
||||||
CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||||
|
|
||||||
// Image Names
|
|
||||||
CPImageNameColorPanel = @"CPImageNameColorPanel";
|
|
||||||
CPImageNameColorPanelHighlighted = @"CPImageNameColorPanelHighlighted";
|
|
||||||
|
|
||||||
var imagesForNames = { },
|
|
||||||
AppKitImageForNames = { };
|
|
||||||
|
|
||||||
AppKitImageForNames[CPImageNameColorPanel] = CGSizeMake(26.0, 29.0);
|
|
||||||
AppKitImageForNames[CPImageNameColorPanelHighlighted] = CGSizeMake(26.0, 29.0);
|
|
||||||
|
|
||||||
function CPImageInBundle(aFilename, aSize, aBundle)
|
function CPImageInBundle(aFilename, aSize, aBundle)
|
||||||
{
|
{
|
||||||
if (!aBundle)
|
if (!aBundle)
|
||||||
aBundle = [CPBundle mainBundle];
|
aBundle = [CPBundle mainBundle];
|
||||||
|
|
||||||
if (aSize)
|
if (aSize)
|
||||||
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename] size:aSize];
|
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename] size:aSize];
|
||||||
|
|
||||||
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename]];
|
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename]];
|
||||||
}
|
}
|
||||||
|
|
||||||
function CPAppKitImage(aFilename, aSize)
|
@implementation CPBundle (CPImageAdditions)
|
||||||
|
|
||||||
|
- (CPString)pathForResource:(CPString)aFilename
|
||||||
{
|
{
|
||||||
return CPImageInBundle(aFilename, aSize, [CPBundle bundleForClass:[CPView class]]);
|
return [self resourcePath] + '/' + aFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPImage
|
@class CPImage
|
||||||
@@ -90,38 +83,32 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
{
|
{
|
||||||
CGSize _size;
|
CGSize _size;
|
||||||
CPString _filename;
|
CPString _filename;
|
||||||
CPString _name;
|
|
||||||
|
|
||||||
id _delegate;
|
id _delegate;
|
||||||
unsigned _loadStatus;
|
unsigned _loadStatus;
|
||||||
|
|
||||||
Image _image;
|
Image _image;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
return [self initByReferencingFile:@"" size:CGSizeMake(-1, -1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Initializes the image, by associating it with a filename. The image
|
Initializes the image, by associating it with a filename. The image
|
||||||
denoted in \c aFilename is not actually loaded. It will
|
denoted in <code>aFilename</code> is not actually loaded. It will
|
||||||
be loaded once needed.
|
be loaded once needed.
|
||||||
@param aFilename the file containing the image
|
@param aFilename the file containing the image
|
||||||
@param aSize the image's size
|
@param aSize the image's size
|
||||||
@return the initialized image
|
@return the initialized image
|
||||||
*/
|
*/
|
||||||
- (id)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize
|
- (CPImage)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_size = CPSizeCreateCopy(aSize);
|
_size = CPSizeCreateCopy(aSize);
|
||||||
_filename = aFilename;
|
_filename = aFilename;
|
||||||
_loadStatus = CPImageLoadStatusInitialized;
|
_loadStatus = CPImageLoadStatusInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,13 +118,13 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
@param aSize the size of the image
|
@param aSize the size of the image
|
||||||
@return the initialized image.
|
@return the initialized image.
|
||||||
*/
|
*/
|
||||||
- (id)initWithContentsOfFile:(CPString)aFilename size:(CGSize)aSize
|
- (CPImage)initWithContentsOfFile:(CPString)aFilename size:(CGSize)aSize
|
||||||
{
|
{
|
||||||
self = [self initByReferencingFile:aFilename size:aSize];
|
self = [self initByReferencingFile:aFilename size:aSize];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
[self load];
|
[self load];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,13 +134,13 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
@param aFilename the file name of the image
|
@param aFilename the file name of the image
|
||||||
@return the initialized image
|
@return the initialized image
|
||||||
*/
|
*/
|
||||||
- (id)initWithContentsOfFile:(CPString)aFilename
|
- (CPImage)initWithContentsOfFile:(CPString)aFilename
|
||||||
{
|
{
|
||||||
self = [self initByReferencingFile:aFilename size:CGSizeMake(-1, -1)];
|
self = [self initByReferencingFile:aFilename size: CGSizeMake(-1, -1)];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
[self load];
|
[self load];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,50 +169,6 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)imageNamed:(CPString)aName
|
|
||||||
{
|
|
||||||
var image = imagesForNames[aName];
|
|
||||||
|
|
||||||
if (image)
|
|
||||||
return image;
|
|
||||||
|
|
||||||
var imageOrSize = AppKitImageForNames[aName];
|
|
||||||
|
|
||||||
if (!imageOrSize)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
if (!imageOrSize.isa)
|
|
||||||
{
|
|
||||||
imageOrSize = CPAppKitImage("CPImage/" + aName + ".png", imageOrSize);
|
|
||||||
|
|
||||||
[imageOrSize setName:aName];
|
|
||||||
|
|
||||||
AppKitImageForNames[aName] = imageOrSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return imageOrSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)setName:(CPString)aName
|
|
||||||
{
|
|
||||||
if (_name === aName)
|
|
||||||
return YES;
|
|
||||||
|
|
||||||
if (imagesForNames[aName])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
_name = aName;
|
|
||||||
|
|
||||||
imagesForNames[aName] = self;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)name
|
|
||||||
{
|
|
||||||
return _name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's delegate.
|
Sets the receiver's delegate.
|
||||||
@param the delegate
|
@param the delegate
|
||||||
@@ -244,9 +187,9 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the load status, which will be CPImageLoadStatusCompleted if the image data has already been loaded.
|
Returns <code>YES</code> if the image data has already been loaded.
|
||||||
*/
|
*/
|
||||||
- (unsigned)loadStatus
|
- (BOOL)loadStatus
|
||||||
{
|
{
|
||||||
return _loadStatus;
|
return _loadStatus;
|
||||||
}
|
}
|
||||||
@@ -261,10 +204,9 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
{
|
{
|
||||||
if (_loadStatus == CPImageLoadStatusLoading || _loadStatus == CPImageLoadStatusCompleted)
|
if (_loadStatus == CPImageLoadStatusLoading || _loadStatus == CPImageLoadStatusCompleted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_loadStatus = CPImageLoadStatusLoading;
|
_loadStatus = CPImageLoadStatusLoading;
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
_image = new Image();
|
_image = new Image();
|
||||||
|
|
||||||
var isSynchronous = YES;
|
var isSynchronous = YES;
|
||||||
@@ -281,7 +223,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
}
|
}
|
||||||
[self _derefFromImage];
|
[self _derefFromImage];
|
||||||
}
|
}
|
||||||
|
|
||||||
_image.onerror = function ()
|
_image.onerror = function ()
|
||||||
{
|
{
|
||||||
if (isSynchronous)
|
if (isSynchronous)
|
||||||
@@ -293,7 +235,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
}
|
}
|
||||||
[self _derefFromImage];
|
[self _derefFromImage];
|
||||||
}
|
}
|
||||||
|
|
||||||
_image.onabort = function ()
|
_image.onabort = function ()
|
||||||
{
|
{
|
||||||
if (isSynchronous)
|
if (isSynchronous)
|
||||||
@@ -305,13 +247,10 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
}
|
}
|
||||||
[self _derefFromImage];
|
[self _derefFromImage];
|
||||||
}
|
}
|
||||||
|
|
||||||
_image.src = _filename;
|
_image.src = _filename;
|
||||||
|
|
||||||
// onload and friends may fire after this point but BEFORE the end of the run loop,
|
isSynchronous = NO;
|
||||||
// crazy, I know. So don't set isSynchronous here, rather wait a bit longer.
|
|
||||||
window.setTimeout(function() { isSynchronous = NO; }, 0);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isThreePartImage
|
- (BOOL)isThreePartImage
|
||||||
@@ -344,7 +283,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
postNotificationName:CPImageDidLoadNotification
|
postNotificationName:CPImageDidLoadNotification
|
||||||
object:self];
|
object:self];
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(imageDidLoad:)])
|
if ([_delegate respondsToSelector:@selector(imageDidLoad:)])
|
||||||
[_delegate imageDidLoad:self];
|
[_delegate imageDidLoad:self];
|
||||||
}
|
}
|
||||||
@@ -353,7 +292,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
- (void)_imageDidError
|
- (void)_imageDidError
|
||||||
{
|
{
|
||||||
_loadStatus = CPImageLoadStatusReadError;
|
_loadStatus = CPImageLoadStatusReadError;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(imageDidError:)])
|
if ([_delegate respondsToSelector:@selector(imageDidError:)])
|
||||||
[_delegate imageDidError:self];
|
[_delegate imageDidError:self];
|
||||||
}
|
}
|
||||||
@@ -362,7 +301,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
- (void)_imageDidAbort
|
- (void)_imageDidAbort
|
||||||
{
|
{
|
||||||
_loadStatus = CPImageLoadStatusCancelled;
|
_loadStatus = CPImageLoadStatusCancelled;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(imageDidAbort:)])
|
if ([_delegate respondsToSelector:@selector(imageDidAbort:)])
|
||||||
[_delegate imageDidAbort:self];
|
[_delegate imageDidAbort:self];
|
||||||
}
|
}
|
||||||
@@ -402,7 +341,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
- (id)initWithImageSlices:(CPArray)imageSlices isVertical:(BOOL)isVertical
|
- (id)initWithImageSlices:(CPArray)imageSlices isVertical:(BOOL)isVertical
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_imageSlices = imageSlices;
|
_imageSlices = imageSlices;
|
||||||
@@ -447,13 +386,13 @@ var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey",
|
|||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_imageSlices = [aCoder decodeObjectForKey:CPThreePartImageImageSlicesKey];
|
_imageSlices = [aCoder decodeObjectForKey:CPThreePartImageImageSlicesKey];
|
||||||
_isVertical = [aCoder decodeBoolForKey:CPThreePartImageIsVerticalKey];
|
_isVertical = [aCoder decodeBoolForKey:CPThreePartImageIsVerticalKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,10 +413,10 @@ var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey",
|
|||||||
- (id)initWithImageSlices:(CPArray)imageSlices
|
- (id)initWithImageSlices:(CPArray)imageSlices
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
_imageSlices = imageSlices;
|
_imageSlices = imageSlices;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,10 +449,10 @@ var CPNinePartImageImageSlicesKey = @"CPNinePartImageImageSlicesKey";
|
|||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
_imageSlices = [aCoder decodeObjectForKey:CPNinePartImageImageSlicesKey];
|
_imageSlices = [aCoder decodeObjectForKey:CPNinePartImageImageSlicesKey];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+34
-120
@@ -26,23 +26,30 @@
|
|||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
@import "CPShadowView.j"
|
@import "CPShadowView.j"
|
||||||
|
|
||||||
|
#include "Platform/Platform.h"
|
||||||
|
#include "Platform/DOM/CPDOMDisplayServer.h"
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPImageScaling
|
||||||
|
*/
|
||||||
CPScaleProportionally = 0;
|
CPScaleProportionally = 0;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPImageScaling
|
||||||
|
*/
|
||||||
CPScaleToFit = 1;
|
CPScaleToFit = 1;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPImageScaling
|
||||||
|
*/
|
||||||
CPScaleNone = 2;
|
CPScaleNone = 2;
|
||||||
|
|
||||||
CPImageAlignCenter = 0;
|
|
||||||
CPImageAlignTop = 1;
|
|
||||||
CPImageAlignTopLeft = 2;
|
|
||||||
CPImageAlignTopRight = 3;
|
|
||||||
CPImageAlignLeft = 4;
|
|
||||||
CPImageAlignBottom = 5;
|
|
||||||
CPImageAlignBottomLeft = 6;
|
|
||||||
CPImageAlignBottomRight = 7;
|
|
||||||
CPImageAlignRight = 8;
|
|
||||||
|
|
||||||
var CPImageViewShadowBackgroundColor = nil,
|
var CPImageViewShadowBackgroundColor = nil;
|
||||||
CPImageViewEmptyPlaceholderImage = nil;
|
|
||||||
|
|
||||||
var LEFT_SHADOW_INSET = 3.0,
|
var LEFT_SHADOW_INSET = 3.0,
|
||||||
RIGHT_SHADOW_INSET = 3.0,
|
RIGHT_SHADOW_INSET = 3.0,
|
||||||
@@ -59,22 +66,14 @@ var LEFT_SHADOW_INSET = 3.0,
|
|||||||
*/
|
*/
|
||||||
@implementation CPImageView : CPControl
|
@implementation CPImageView : CPControl
|
||||||
{
|
{
|
||||||
DOMElement _DOMImageElement;
|
DOMElement _DOMImageElement;
|
||||||
|
|
||||||
BOOL _hasShadow;
|
BOOL _hasShadow;
|
||||||
CPView _shadowView;
|
CPView _shadowView;
|
||||||
|
|
||||||
BOOL _isEditable;
|
BOOL _isEditable;
|
||||||
|
|
||||||
CGRect _imageRect;
|
CGRect _imageRect;
|
||||||
CPImageAlignment _imageAlignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
var bundle = [CPBundle bundleForClass:[CPView class]];
|
|
||||||
|
|
||||||
CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
@@ -88,13 +87,7 @@ var LEFT_SHADOW_INSET = 3.0,
|
|||||||
_DOMImageElement.style.position = "absolute";
|
_DOMImageElement.style.position = "absolute";
|
||||||
_DOMImageElement.style.left = "0px";
|
_DOMImageElement.style.left = "0px";
|
||||||
_DOMImageElement.style.top = "0px";
|
_DOMImageElement.style.top = "0px";
|
||||||
|
|
||||||
if ([CPPlatform supportsDragAndDrop])
|
|
||||||
{
|
|
||||||
_DOMImageElement.setAttribute("draggable", "true");
|
|
||||||
_DOMImageElement.style["-khtml-user-drag"] = "element";
|
|
||||||
}
|
|
||||||
|
|
||||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement);
|
CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement);
|
||||||
|
|
||||||
_DOMImageElement.style.visibility = "hidden";
|
_DOMImageElement.style.visibility = "hidden";
|
||||||
@@ -135,7 +128,7 @@ var LEFT_SHADOW_INSET = 3.0,
|
|||||||
var newImage = [self objectValue];
|
var newImage = [self objectValue];
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
_DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
|
_DOMImageElement.src = newImage ? [newImage filename] : "";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var size = [newImage size];
|
var size = [newImage size];
|
||||||
@@ -168,8 +161,8 @@ var LEFT_SHADOW_INSET = 3.0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the image view draws with
|
Returns <code>YES</code> if the image view draws with
|
||||||
a drop shadow. The default is \c NO.
|
a drop shadow. The default is <code>NO</code>.
|
||||||
*/
|
*/
|
||||||
- (BOOL)hasShadow
|
- (BOOL)hasShadow
|
||||||
{
|
{
|
||||||
@@ -206,30 +199,6 @@ var LEFT_SHADOW_INSET = 3.0,
|
|||||||
[self hideOrDisplayContents];
|
[self hideOrDisplayContents];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the type of image alignment that should be used to
|
|
||||||
render the image.
|
|
||||||
@param anImageAlignment the type of scaling to use
|
|
||||||
*/
|
|
||||||
- (void)setImageAlignment:(CPImageAlignment)anImageAlignment
|
|
||||||
{
|
|
||||||
if (_imageAlignment == anImageAlignment)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_imageAlignment = anImageAlignment;
|
|
||||||
|
|
||||||
if (![self image])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)imageAlignment
|
|
||||||
{
|
|
||||||
return _imageAlignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the type of image scaling that should be used to
|
Sets the type of image scaling that should be used to
|
||||||
render the image.
|
render the image.
|
||||||
@@ -250,11 +219,6 @@ var LEFT_SHADOW_INSET = 3.0,
|
|||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned)imageScaling
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"image-scaling"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Toggles the display of the image view.
|
Toggles the display of the image view.
|
||||||
*/
|
*/
|
||||||
@@ -357,45 +321,8 @@ var LEFT_SHADOW_INSET = 3.0,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var x, y;
|
var x = (boundsWidth - width) / 2.0,
|
||||||
|
y = (boundsHeight - height) / 2.0;
|
||||||
switch (_imageAlignment)
|
|
||||||
{
|
|
||||||
case CPImageAlignLeft:
|
|
||||||
case CPImageAlignTopLeft:
|
|
||||||
case CPImageAlignBottomLeft:
|
|
||||||
x = 0.0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPImageAlignRight:
|
|
||||||
case CPImageAlignTopRight:
|
|
||||||
case CPImageAlignBottomRight:
|
|
||||||
x = boundsWidth - width;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
x = (boundsWidth - width) / 2.0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_imageAlignment)
|
|
||||||
{
|
|
||||||
case CPImageAlignTop:
|
|
||||||
case CPImageAlignTopLeft:
|
|
||||||
case CPImageAlignTopRight:
|
|
||||||
y = 0.0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPImageAlignBottom:
|
|
||||||
case CPImageAlignBottomLeft:
|
|
||||||
case CPImageAlignBottomRight:
|
|
||||||
y = boundsHeight - height;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
y = (boundsHeight - height) / 2.0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, x, y);
|
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, x, y);
|
||||||
@@ -441,26 +368,20 @@ var LEFT_SHADOW_INSET = 3.0,
|
|||||||
return _isEditable;
|
return _isEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)performDragOperation:(CPDraggingInfo)aSender
|
- (void)performDragOperation:(CPDraggingInfo)aSender
|
||||||
{
|
{
|
||||||
var images = [CPKeyedUnarchiver unarchiveObjectWithData:[[aSender draggingPasteboard] dataForType:CPImagesPboardType]];
|
var images = [CPKeyedUnarchiver unarchiveObjectWithData:[[aSender draggingPasteboard] dataForType:CPImagesPboardType]];
|
||||||
|
|
||||||
if ([images count])
|
if ([images count])
|
||||||
{
|
|
||||||
[self setImage:images[0]];
|
[self setImage:images[0]];
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPImageViewImageKey = @"CPImageViewImageKey",
|
var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||||
CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
|
CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
|
||||||
CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey",
|
CPImageViewHasShadowKey = @"CPImageViewHasShadowKey",
|
||||||
CPImageViewHasShadowKey = @"CPImageViewHasShadowKey",
|
CPImageViewIsEditableKey = @"CPImageViewIsEditableKey";
|
||||||
CPImageViewIsEditableKey = @"CPImageViewIsEditableKey";
|
|
||||||
|
|
||||||
@implementation CPImageView (CPCoding)
|
@implementation CPImageView (CPCoding)
|
||||||
|
|
||||||
@@ -477,11 +398,6 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
|||||||
_DOMImageElement.style.left = "0px";
|
_DOMImageElement.style.left = "0px";
|
||||||
_DOMImageElement.style.top = "0px";
|
_DOMImageElement.style.top = "0px";
|
||||||
_DOMImageElement.style.visibility = "hidden";
|
_DOMImageElement.style.visibility = "hidden";
|
||||||
if ([CPPlatform supportsDragAndDrop])
|
|
||||||
{
|
|
||||||
_DOMImageElement.setAttribute("draggable", "true");
|
|
||||||
_DOMImageElement.style["-khtml-user-drag"] = "element";
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
self = [super initWithCoder:aCoder];
|
self = [super initWithCoder:aCoder];
|
||||||
@@ -493,7 +409,6 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
[self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
|
[self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
|
||||||
[self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]];
|
|
||||||
|
|
||||||
if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO)
|
if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO)
|
||||||
[self setEditable:YES];
|
[self setEditable:YES];
|
||||||
@@ -528,7 +443,6 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
|||||||
_subviews = actualSubviews;
|
_subviews = actualSubviews;
|
||||||
|
|
||||||
[aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey];
|
[aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey];
|
||||||
[aCoder encodeInt:_imageAlignment forKey:CPImageViewImageAlignmentKey];
|
|
||||||
|
|
||||||
if (_isEditable)
|
if (_isEditable)
|
||||||
[aCoder encodeBool:_isEditable forKey:CPImageViewIsEditableKey];
|
[aCoder encodeBool:_isEditable forKey:CPImageViewIsEditableKey];
|
||||||
|
|||||||
@@ -1,263 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPKeyBinding.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Nicholas Small.
|
|
||||||
* Copyright 2010, 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 "CPEvent.j"
|
|
||||||
|
|
||||||
|
|
||||||
CPStandardKeyBindings = {
|
|
||||||
@"@.": @"cancelOperation:",
|
|
||||||
|
|
||||||
@"^a": @"moveToBeginningOfParagraph:",
|
|
||||||
@"^$a": @"moveToBeginningOfParagraphAndModifySelection:",
|
|
||||||
@"^b": @"moveBackward:",
|
|
||||||
@"^$b": @"moveBackwardAndModifySelection:",
|
|
||||||
@"^~b": @"moveWordBackward:",
|
|
||||||
@"^~$b": @"moveWordBackwardAndModifySelection:",
|
|
||||||
@"^d": @"deleteForward:",
|
|
||||||
@"^e": @"moveToEndOfParagraph:",
|
|
||||||
@"^$e": @"moveToEndOfParagraphAndModifySelection:",
|
|
||||||
@"^f": @"moveForward:",
|
|
||||||
@"^$f": @"moveForwardAndModifySelection:",
|
|
||||||
@"^~f": @"moveWordForward:",
|
|
||||||
@"^~$f": @"moveWordForwardAndModifySelection:",
|
|
||||||
@"^h": @"deleteBackward:",
|
|
||||||
@"^k": @"deleteToEndOfParagraph:",
|
|
||||||
@"^l": @"centerSelectionInVisibleArea:",
|
|
||||||
@"^n": @"moveDown:",
|
|
||||||
@"^$n": @"moveDownAndModifySelection:",
|
|
||||||
@"^o": [@"insertNewlineIgnoringFieldEditor:", @"moveBackward:"],
|
|
||||||
@"^p": @"moveUp:",
|
|
||||||
@"^$p": @"moveUpAndModifySelection:",
|
|
||||||
@"^t": @"transpose:",
|
|
||||||
@"^v": @"pageDown:",
|
|
||||||
@"^$v": @"pageDownAndModifySelection:",
|
|
||||||
@"^y": @"yank:"
|
|
||||||
};
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPNewlineCharacter] = @"insertNewline:";
|
|
||||||
CPStandardKeyBindings[CPCarriageReturnCharacter] = @"insertNewline:";
|
|
||||||
CPStandardKeyBindings[CPEnterCharacter] = @"insertNewline:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPNewlineCharacter] = @"insertNewlineIgnoringFieldEditor:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPCarriageReturnCharacter] = @"insertNewlineIgnoringFieldEditor:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPEnterCharacter] = @"insertNewlineIgnoringFieldEditor:";
|
|
||||||
CPStandardKeyBindings[@"^" + CPNewlineCharacter] = @"insertLineBreak:";
|
|
||||||
CPStandardKeyBindings[@"^" + CPCarriageReturnCharacter] = @"insertLineBreak:";
|
|
||||||
CPStandardKeyBindings[@"^" + CPEnterCharacter] = @"insertLineBreak:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPBackspaceCharacter] = @"deleteBackward:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPBackspaceCharacter] = @"deleteWordBackward:";
|
|
||||||
CPStandardKeyBindings[CPDeleteCharacter] = @"deleteBackward:";
|
|
||||||
CPStandardKeyBindings[@"@" + CPDeleteCharacter] = @"deleteToBeginningOfLine:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPDeleteCharacter] = @"deleteWordBackward:";
|
|
||||||
CPStandardKeyBindings[@"^" + CPDeleteCharacter] = @"deleteBackwardByDecomposingPreviousCharacter:";
|
|
||||||
CPStandardKeyBindings[@"^~" + CPDeleteCharacter] = @"deleteWordBackward:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPDeleteFunctionKey] = @"deleteForward:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPDeleteFunctionKey] = @"deleteWordForward:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPTabCharacter] = @"insertTab:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPTabCharacter] = @"insertTabIgnoringFieldEditor:";
|
|
||||||
CPStandardKeyBindings[@"^" + CPTabCharacter] = @"selectNextKeyView:";
|
|
||||||
CPStandardKeyBindings[CPBackTabCharacter] = @"insertBacktab:";
|
|
||||||
CPStandardKeyBindings[@"^" + CPBackTabCharacter] = @"selectPreviousKeyView:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPEscapeFunctionKey] = @"cancelOperation:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPEscapeFunctionKey] = @"complete:";
|
|
||||||
CPStandardKeyBindings[CPF5FunctionKey] = @"complete:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPLeftArrowFunctionKey] = @"moveLeft:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPLeftArrowFunctionKey] = @"moveWordLeft:";
|
|
||||||
CPStandardKeyBindings[@"^" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLine:";
|
|
||||||
CPStandardKeyBindings[@"@" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLine:";
|
|
||||||
CPStandardKeyBindings[@"$" + CPLeftArrowFunctionKey] = @"moveLeftAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$~" + CPLeftArrowFunctionKey] = @"moveWordLeftAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$^" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLineAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$@" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLineAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"@^" + CPLeftArrowFunctionKey] = @"makeBaseWritingDirectionRightToLeft:";
|
|
||||||
CPStandardKeyBindings[@"@^~" + CPLeftArrowFunctionKey] = @"makeTextWritingDirectionRightToLeft:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPRightArrowFunctionKey] = @"moveRight:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPRightArrowFunctionKey] = @"moveWordRight:";
|
|
||||||
CPStandardKeyBindings[@"^" + CPRightArrowFunctionKey] = @"moveToRightEndOfLine:";
|
|
||||||
CPStandardKeyBindings[@"@" + CPRightArrowFunctionKey] = @"moveToRightEndOfLine:";
|
|
||||||
CPStandardKeyBindings[@"$" + CPRightArrowFunctionKey] = @"moveRightAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$~" + CPRightArrowFunctionKey] = @"moveWordRightAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$^" + CPRightArrowFunctionKey] = @"moveToRightEndOfLineAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$@" + CPRightArrowFunctionKey] = @"moveToRightEndOfLineAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"@^" + CPRightArrowFunctionKey] = @"makeBaseWritingDirectionLeftToRight:";
|
|
||||||
CPStandardKeyBindings[@"@^~" + CPRightArrowFunctionKey] = @"makeTextWritingDirectionLeftToRight:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPUpArrowFunctionKey] = @"moveUp:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPUpArrowFunctionKey] = [@"moveBackward:", @"moveToBeginningOfParagraph:"];
|
|
||||||
CPStandardKeyBindings[@"^" + CPUpArrowFunctionKey] = @"scrollPageUp:";
|
|
||||||
CPStandardKeyBindings[@"@" + CPUpArrowFunctionKey] = @"moveToBeginningOfDocument:";
|
|
||||||
CPStandardKeyBindings[@"$" + CPUpArrowFunctionKey] = @"moveUpAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$~" + CPUpArrowFunctionKey] = @"moveParagraphBackwardAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$@" + CPUpArrowFunctionKey] = @"moveToBeginningOfDocumentAndModifySelection:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPDownArrowFunctionKey] = @"moveDown:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPDownArrowFunctionKey] = [@"moveForward:", @"moveToEndOfParagraph:"];
|
|
||||||
CPStandardKeyBindings[@"^" + CPDownArrowFunctionKey] = @"scrollPageDown:";
|
|
||||||
CPStandardKeyBindings[@"@" + CPDownArrowFunctionKey] = @"moveToEndOfDocument:";
|
|
||||||
CPStandardKeyBindings[@"$" + CPDownArrowFunctionKey] = @"moveDownAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$~" + CPDownArrowFunctionKey] = @"moveParagraphForwardAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"$@" + CPDownArrowFunctionKey] = @"moveToEndOfDocumentAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[@"@^" + CPDownArrowFunctionKey] = @"makeBaseWritingDirectionNatural:";
|
|
||||||
CPStandardKeyBindings[@"@^~" + CPDownArrowFunctionKey] = @"makeTextWritingDirectionNatural:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPHomeFunctionKey] = @"scrollToBeginningOfDocument:";
|
|
||||||
CPStandardKeyBindings[@"$" + CPHomeFunctionKey] = @"moveToBeginningOfDocumentAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[CPEndFunctionKey] = @"scrollToEndOfDocument:";
|
|
||||||
CPStandardKeyBindings[@"$" + CPEndFunctionKey] = @"moveToEndOfDocumentAndModifySelection:";
|
|
||||||
|
|
||||||
CPStandardKeyBindings[CPPageUpFunctionKey] = @"scrollPageUp:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPPageUpFunctionKey] = @"pageUp:";
|
|
||||||
CPStandardKeyBindings[@"$" + CPPageUpFunctionKey] = @"pageUpAndModifySelection:";
|
|
||||||
CPStandardKeyBindings[CPPageDownFunctionKey] = @"scrollPageDown:";
|
|
||||||
CPStandardKeyBindings[@"~" + CPPageDownFunctionKey] = @"pageDown:";
|
|
||||||
CPStandardKeyBindings[@"$" + CPPageDownFunctionKey] = @"pageDownAndModifySelection:";
|
|
||||||
|
|
||||||
var CPKeyBindingCache = {};
|
|
||||||
|
|
||||||
@implementation CPKeyBinding : CPObject
|
|
||||||
{
|
|
||||||
CPString _key;
|
|
||||||
unsigned _modifierFlags;
|
|
||||||
|
|
||||||
CPArray _selectors;
|
|
||||||
|
|
||||||
CPString _cacheName;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if ([self class] !== CPKeyBinding)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self createKeyBindingsFromJSObject:CPStandardKeyBindings];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)createKeyBindingsFromJSObject:(JSObject)anObject
|
|
||||||
{
|
|
||||||
var binding;
|
|
||||||
for (binding in anObject)
|
|
||||||
[self cacheKeyBinding:[[CPKeyBinding alloc] initWithPhysicalKeyString:binding selectors:anObject[binding]]];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)cacheKeyBinding:(CPKeyBinding)aBinding
|
|
||||||
{
|
|
||||||
if (!aBinding)
|
|
||||||
return;
|
|
||||||
|
|
||||||
CPKeyBindingCache[[aBinding _cacheName]] = aBinding;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPKeyBinding)keyBindingForKey:(CPString)aKey modifierFlags:(unsigned)aFlag
|
|
||||||
{
|
|
||||||
var tempBinding = [[self alloc] initWithKey:aKey modifierFlags:aFlag selectors:nil];
|
|
||||||
return CPKeyBindingCache[[tempBinding _cacheName]];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPArray)selectorsForKey:(CPString)aKey modifierFlags:(unsigned)aFlag
|
|
||||||
{
|
|
||||||
return [[self keyBindingForKey:aKey modifierFlags:aFlag] selectors];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithPhysicalKeyString:(CPString)binding selectors:(CPArray)selectors
|
|
||||||
{
|
|
||||||
var components = binding.split(@""),
|
|
||||||
modifierFlags = ([components containsObject:@"$"] ? CPShiftKeyMask : 0) |
|
|
||||||
([components containsObject:@"^"] ? CPControlKeyMask : 0) |
|
|
||||||
([components containsObject:@"~"] ? CPAlternateKeyMask : 0) |
|
|
||||||
([components containsObject:@"@"] ? CPCommandKeyMask : 0);
|
|
||||||
|
|
||||||
if (![selectors isKindOfClass:CPArray])
|
|
||||||
selectors = [selectors];
|
|
||||||
|
|
||||||
return [self initWithKey:[components lastObject] modifierFlags:modifierFlags selectors:selectors];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithKey:(CPString)aKey modifierFlags:(unsigned)aFlag selectors:(CPArray)selectors
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_key = aKey;
|
|
||||||
_modifierFlags = aFlag;
|
|
||||||
|
|
||||||
_selectors = selectors;
|
|
||||||
|
|
||||||
// We normalize our key binding string in order to properly cache it.
|
|
||||||
// We want to ensure the modifiers are always in the same order.
|
|
||||||
var cacheName = [];
|
|
||||||
|
|
||||||
if (_modifierFlags & CPCommandKeyMask)
|
|
||||||
cacheName.push(@"@");
|
|
||||||
if (_modifierFlags & CPControlKeyMask)
|
|
||||||
cacheName.push(@"^");
|
|
||||||
if (_modifierFlags & CPAlternateKeyMask)
|
|
||||||
cacheName.push(@"~");
|
|
||||||
if (_modifierFlags & CPShiftKeyMask)
|
|
||||||
cacheName.push(@"$");
|
|
||||||
|
|
||||||
cacheName.push(_key);
|
|
||||||
|
|
||||||
_cacheName = cacheName.join(@"");
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)key
|
|
||||||
{
|
|
||||||
return _key;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)modifierFlags
|
|
||||||
{
|
|
||||||
return _modifierFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)selectors
|
|
||||||
{
|
|
||||||
return _selectors;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)_cacheName
|
|
||||||
{
|
|
||||||
return _cacheName;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEqual:(CPKeyBinding)rhs
|
|
||||||
{
|
|
||||||
return _key === [rhs key] && _modifierFlags === [rhs modifierFlags];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
return [CPString stringWithFormat:@"<KeyBinding string: '%@' modifierFlags: 0x%lx selectors: %@>", _key, _modifierFlags, _selectors];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,472 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPKeyValueBinding.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Ross Boucher 1/13/09
|
|
||||||
* Copyright 280 North, Inc.
|
|
||||||
*
|
|
||||||
* Adapted from GNUStep
|
|
||||||
* Released under the LGPL.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
@import <Foundation/CPArray.j>
|
|
||||||
@import <Foundation/CPDictionary.j>
|
|
||||||
@import <Foundation/CPValueTransformer.j>
|
|
||||||
|
|
||||||
|
|
||||||
var exposedBindingsMap = [CPDictionary new],
|
|
||||||
bindingsMap = [CPDictionary new];
|
|
||||||
|
|
||||||
var CPBindingOperationAnd = 0,
|
|
||||||
CPBindingOperationOr = 1;
|
|
||||||
|
|
||||||
@implementation CPKeyValueBinding : CPObject
|
|
||||||
{
|
|
||||||
CPDictionary _info;
|
|
||||||
id _source;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)exposeBinding:(CPString)aBinding forClass:(Class)aClass
|
|
||||||
{
|
|
||||||
var bindings = [exposedBindingsMap objectForKey:[aClass hash]];
|
|
||||||
|
|
||||||
if (!bindings)
|
|
||||||
{
|
|
||||||
bindings = [];
|
|
||||||
[exposedBindingsMap setObject:bindings forKey:[aClass hash]];
|
|
||||||
}
|
|
||||||
|
|
||||||
bindings.push(aBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPArray)exposedBindingsForClass:(Class)aClass
|
|
||||||
{
|
|
||||||
return [[exposedBindingsMap objectForKey:[aClass hash]] copy];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPKeyValueBinding)getBinding:(CPString)aBinding forObject:(id)anObject
|
|
||||||
{
|
|
||||||
return [[bindingsMap objectForKey:[anObject hash]] objectForKey:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPDictionary)infoForBinding:(CPString)aBinding forObject:(id)anObject
|
|
||||||
{
|
|
||||||
var theBinding = [self getBinding:aBinding forObject:anObject];
|
|
||||||
|
|
||||||
if (theBinding)
|
|
||||||
return theBinding._info;
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPDictionary)allBindingsForObject:(id)anObject
|
|
||||||
{
|
|
||||||
return [bindingsMap objectForKey:[anObject hash]];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)unbind:(CPString)aBinding forObject:(id)anObject
|
|
||||||
{
|
|
||||||
var bindings = [bindingsMap objectForKey:[anObject hash]];
|
|
||||||
|
|
||||||
if (!bindings)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var theBinding = [bindings objectForKey:aBinding];
|
|
||||||
|
|
||||||
if (!theBinding)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var infoDictionary = theBinding._info,
|
|
||||||
observedObject = [infoDictionary objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey];
|
|
||||||
|
|
||||||
[observedObject removeObserver:theBinding forKeyPath:keyPath];
|
|
||||||
[bindings removeObjectForKey:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)unbindAllForObject:(id)anObject
|
|
||||||
{
|
|
||||||
var bindings = [bindingsMap objectForKey:[anObject hash]];
|
|
||||||
if (!bindings)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var allKeys = [bindings allKeys],
|
|
||||||
count = allKeys.length;
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
[anObject unbind:[bindings objectForKey:allKeys[count]]];
|
|
||||||
|
|
||||||
[bindingsMap removeObjectForKey:[anObject hash]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_source = aSource;
|
|
||||||
_info = [CPDictionary dictionaryWithObjects:[aDestination, aKeyPath] forKeys:[CPObservedObjectKey, CPObservedKeyPathKey]];
|
|
||||||
|
|
||||||
if (options)
|
|
||||||
[_info setObject:options forKey:CPOptionsKey];
|
|
||||||
|
|
||||||
[aDestination addObserver:self forKeyPath:aKeyPath options:CPKeyValueObservingOptionNew context:aBinding];
|
|
||||||
|
|
||||||
var bindings = [bindingsMap objectForKey:[_source hash]];
|
|
||||||
if (!bindings)
|
|
||||||
{
|
|
||||||
bindings = [CPDictionary new];
|
|
||||||
[bindingsMap setObject:bindings forKey:[_source hash]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[bindings setObject:self forKey:aName];
|
|
||||||
[self setValueFor:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [_info objectForKey:CPOptionsKey],
|
|
||||||
newValue = [destination valueForKeyPath:keyPath],
|
|
||||||
isPlaceholder = CPIsControllerMarker(newValue);
|
|
||||||
|
|
||||||
if (isPlaceholder)
|
|
||||||
{
|
|
||||||
switch (newValue)
|
|
||||||
{
|
|
||||||
case CPMultipleValuesMarker:
|
|
||||||
newValue = [options objectForKey:CPMultipleValuesPlaceholderBindingOption] || @"Multiple Values";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPNoSelectionMarker:
|
|
||||||
newValue = [options objectForKey:CPNoSelectionPlaceholderBindingOption] || @"No Selection";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPNotApplicableMarker:
|
|
||||||
if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
|
||||||
[CPException raise:CPGenericException reason:@"can't transform non applicable key on: "+_source+" value: "+newValue];
|
|
||||||
|
|
||||||
newValue = [options objectForKey:CPNotApplicablePlaceholderBindingOption] || @"Not Applicable";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Only transform the value if the current value is not a placeholder
|
|
||||||
newValue = [self transformValue:newValue withOptions:options];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_source setValue:newValue forKey:aBinding];
|
|
||||||
|
|
||||||
if ([_source respondsToSelector:@selector(_setCurrentValueIsPlaceholder:)])
|
|
||||||
[_source _setCurrentValueIsPlaceholder:isPlaceholder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reverseSetValueFor:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [_info objectForKey:CPOptionsKey],
|
|
||||||
newValue = [_source valueForKeyPath:aBinding];
|
|
||||||
|
|
||||||
newValue = [self reverseTransformValue:newValue withOptions:options];
|
|
||||||
[destination setValue:newValue forKeyPath:keyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
|
||||||
{
|
|
||||||
if (!changes)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self setValueFor:context];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)transformValue:(id)aValue withOptions:(CPDictionary)options
|
|
||||||
{
|
|
||||||
var valueTransformerName,
|
|
||||||
valueTransformer,
|
|
||||||
placeholder;
|
|
||||||
|
|
||||||
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
|
||||||
valueTransformer;
|
|
||||||
|
|
||||||
if (valueTransformerName)
|
|
||||||
{
|
|
||||||
valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName];
|
|
||||||
|
|
||||||
if (!valueTransformer)
|
|
||||||
{
|
|
||||||
var valueTransformerClass = CPClassFromString(valueTransformerName);
|
|
||||||
if (valueTransformerClass)
|
|
||||||
{
|
|
||||||
valueTransformer = [[valueTransformerClass alloc] init];
|
|
||||||
[valueTransformerClass setValueTransformer:valueTransformer forName:valueTransformerName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
valueTransformer = [options objectForKey:CPValueTransformerBindingOption];
|
|
||||||
|
|
||||||
if (valueTransformer)
|
|
||||||
aValue = [valueTransformer transformedValue:aValue];
|
|
||||||
|
|
||||||
|
|
||||||
if (aValue === undefined || aValue === nil)
|
|
||||||
aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
|
|
||||||
|
|
||||||
return aValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)reverseTransformValue:(id)aValue withOptions: (CPDictionary)options
|
|
||||||
{
|
|
||||||
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
|
||||||
valueTransformer;
|
|
||||||
|
|
||||||
if (valueTransformerName)
|
|
||||||
valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName];
|
|
||||||
else
|
|
||||||
valueTransformer = [options objectForKey:CPValueTransformerBindingOption];
|
|
||||||
|
|
||||||
if (valueTransformer && [[valueTransformer class] allowsReverseTransformation])
|
|
||||||
aValue = [valueTransformer reverseTransformedValue:aValue];
|
|
||||||
|
|
||||||
return aValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPObject (KeyValueBindingCreation)
|
|
||||||
|
|
||||||
+ (void)exposeBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[CPKeyValueBinding exposeBinding:aBinding forClass:[self class]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)exposedBindings
|
|
||||||
{
|
|
||||||
var exposedBindings = [],
|
|
||||||
theClass = [self class];
|
|
||||||
|
|
||||||
while (theClass)
|
|
||||||
{
|
|
||||||
var temp = [CPKeyValueBinding exposedBindingsForClass:theClass];
|
|
||||||
|
|
||||||
if (temp)
|
|
||||||
[exposedBindings addObjectsFromArray:temp];
|
|
||||||
|
|
||||||
theClass = [theClass superclass];
|
|
||||||
}
|
|
||||||
|
|
||||||
return exposedBindings;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (Class)valueClassForBinding:(CPString)binding
|
|
||||||
{
|
|
||||||
return [CPString class];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
|
|
||||||
{
|
|
||||||
if (!anObject || !aKeyPath)
|
|
||||||
return CPLog.error("Invalid object or path on "+self+" for "+aBinding);
|
|
||||||
|
|
||||||
//if (![[self exposedBindings] containsObject:aBinding])
|
|
||||||
// CPLog.warn("No binding exposed on "+self+" for "+aBinding);
|
|
||||||
|
|
||||||
[self unbind:aBinding];
|
|
||||||
[[CPKeyValueBinding alloc] initWithBinding:[self _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPDictionary)infoForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
return [CPKeyValueBinding infoForBinding:aBinding forObject:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)unbind:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[CPKeyValueBinding unbind:aBinding forObject:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_replacementKeyPathForBinding:(CPString)binding
|
|
||||||
{
|
|
||||||
return binding;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPKeyValueOrBinding : CPKeyValueBinding
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var bindings = [bindingsMap valueForKey:[_source hash]];
|
|
||||||
|
|
||||||
if (!bindings)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[_source setValue:resolveMultipleValues(aBinding, bindings, CPBindingOperationOr) forKey:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
|
||||||
{
|
|
||||||
[self setValueFor:context];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPKeyValueAndBinding : CPKeyValueBinding
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var bindings = [bindingsMap objectForKey:[_source hash]];
|
|
||||||
|
|
||||||
if (!bindings)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[_source setValue:resolveMultipleValues(aBinding, bindings, CPBindingOperationAnd) forKey:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObejct change:(CPDictionary)changes context:(id)context
|
|
||||||
{
|
|
||||||
[self setValueFor:context];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var resolveMultipleValues = function resolveMultipleValues(/*CPString*/key, /*CPDictionary*/bindings, /*GSBindingOperationKind*/operation)
|
|
||||||
{
|
|
||||||
var bindingName = key,
|
|
||||||
theBinding,
|
|
||||||
count = 1;
|
|
||||||
|
|
||||||
while (theBinding = [bindings objectForKey:bindingName])
|
|
||||||
{
|
|
||||||
var infoDictionary = theBinding._info,
|
|
||||||
object = [infoDictionary objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [infoDictionary objectForKey:CPOptionsKey];
|
|
||||||
|
|
||||||
var value = [theBinding transformValue:[object valueForKeyPath:keyPath] withOptions:options];
|
|
||||||
|
|
||||||
if (value == operation)
|
|
||||||
return operation;
|
|
||||||
|
|
||||||
bindingName = [CPString stringWithFormat:@"%@%i", key, ++count];
|
|
||||||
}
|
|
||||||
|
|
||||||
return !operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
var invokeAction = function invokeAction(/*CPString*/targetKey, /*CPString*/argumentKey, /*CPDictionary*/bindings)
|
|
||||||
{
|
|
||||||
var theBinding = [bindings objectForKey:targetKey],
|
|
||||||
infoDictionary = theBinding._info,
|
|
||||||
|
|
||||||
object = [infoDictionary objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [infoDictionary objectForKey:CPOptionsKey],
|
|
||||||
|
|
||||||
target = [object valueForKeyPath:keyPath],
|
|
||||||
selector = [options objectForKey:CPSelectorNameBindingOption];
|
|
||||||
|
|
||||||
if (!target || !selector)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]];
|
|
||||||
[invocation setSelector:selector];
|
|
||||||
|
|
||||||
var bindingName = argumentKey,
|
|
||||||
count = 1;
|
|
||||||
|
|
||||||
while (theBinding = [bindings objectForKey:bindingName])
|
|
||||||
{
|
|
||||||
infoDictionary = theBinding._info;
|
|
||||||
|
|
||||||
keyPath = [infoDictionary objectForKey:CPObserverKeyPathKey];
|
|
||||||
object = [[infoDictionary objectForKey:CPObservedObjectKey] valueForKeyPath:keyPath];
|
|
||||||
|
|
||||||
if (object)
|
|
||||||
[invocation setArgument:object atIndex:++count];
|
|
||||||
|
|
||||||
bindingName = [CPString stringWithFormat:@"%@%i", argumentKey, count];
|
|
||||||
}
|
|
||||||
|
|
||||||
[invocation invoke];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys in options dictionary
|
|
||||||
|
|
||||||
// Keys in dictionary returned by infoForBinding
|
|
||||||
CPObservedObjectKey = @"CPObservedObjectKey";
|
|
||||||
CPObservedKeyPathKey = @"CPObservedKeyPathKey";
|
|
||||||
CPOptionsKey = @"CPOptionsKey";
|
|
||||||
|
|
||||||
// special markers
|
|
||||||
CPMultipleValuesMarker = @"CPMultipleValuesMarker";
|
|
||||||
CPNoSelectionMarker = @"CPNoSelectionMarker";
|
|
||||||
CPNotApplicableMarker = @"CPNotApplicableMarker";
|
|
||||||
|
|
||||||
// Binding name constants
|
|
||||||
CPAlignmentBinding = @"CPAlignmentBinding";
|
|
||||||
CPEditableBinding = @"CPEditableBinding";
|
|
||||||
CPEnabledBinding = @"CPEnabledBinding";
|
|
||||||
CPFontBinding = @"CPFontBinding";
|
|
||||||
CPHiddenBinding = @"CPHiddenBinding";
|
|
||||||
CPSelectedIndexBinding = @"CPSelectedIndexBinding";
|
|
||||||
CPTextColorBinding = @"CPTextColorBinding";
|
|
||||||
CPToolTipBinding = @"CPToolTipBinding";
|
|
||||||
CPValueBinding = @"value";
|
|
||||||
|
|
||||||
//Binding options constants
|
|
||||||
CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelectionBindingOption";
|
|
||||||
CPAllowsNullArgumentBindingOption = @"CPAllowsNullArgumentBindingOption";
|
|
||||||
CPConditionallySetsEditableBindingOption = @"CPConditionallySetsEditableBindingOption";
|
|
||||||
CPConditionallySetsEnabledBindingOption = @"CPConditionallySetsEnabledBindingOption";
|
|
||||||
CPConditionallySetsHiddenBindingOption = @"CPConditionallySetsHiddenBindingOption";
|
|
||||||
CPContinuouslyUpdatesValueBindingOption = @"CPContinuouslyUpdatesValueBindingOption";
|
|
||||||
CPCreatesSortDescriptorBindingOption = @"CPCreatesSortDescriptorBindingOption";
|
|
||||||
CPDeletesObjectsOnRemoveBindingsOption = @"CPDeletesObjectsOnRemoveBindingsOption";
|
|
||||||
CPDisplayNameBindingOption = @"CPDisplayNameBindingOption";
|
|
||||||
CPDisplayPatternBindingOption = @"CPDisplayPatternBindingOption";
|
|
||||||
CPHandlesContentAsCompoundValueBindingOption = @"CPHandlesContentAsCompoundValueBindingOption";
|
|
||||||
CPInsertsNullPlaceholderBindingOption = @"CPInsertsNullPlaceholderBindingOption";
|
|
||||||
CPInvokesSeparatelyWithArrayObjectsBindingOption = @"CPInvokesSeparatelyWithArrayObjectsBindingOption";
|
|
||||||
CPMultipleValuesPlaceholderBindingOption = @"CPMultipleValuesPlaceholderBindingOption";
|
|
||||||
CPNoSelectionPlaceholderBindingOption = @"CPNoSelectionPlaceholderBindingOption";
|
|
||||||
CPNotApplicablePlaceholderBindingOption = @"CPNotApplicablePlaceholderBindingOption";
|
|
||||||
CPNullPlaceholderBindingOption = @"CPNullPlaceholderBindingOption";
|
|
||||||
CPPredicateFormatBindingOption = @"CPPredicateFormatBindingOption";
|
|
||||||
CPRaisesForNotApplicableKeysBindingOption = @"CPRaisesForNotApplicableKeysBindingOption";
|
|
||||||
CPSelectorNameBindingOption = @"CPSelectorNameBindingOption";
|
|
||||||
CPSelectsAllWhenSettingContentBindingOption = @"CPSelectsAllWhenSettingContentBindingOption";
|
|
||||||
CPValidatesImmediatelyBindingOption = @"CPValidatesImmediatelyBindingOption";
|
|
||||||
CPValueTransformerNameBindingOption = @"CPValueTransformerNameBindingOption";
|
|
||||||
CPValueTransformerBindingOption = @"CPValueTransformerBindingOption";
|
|
||||||
|
|
||||||
CPIsControllerMarker = function(/*id*/anObject)
|
|
||||||
{
|
|
||||||
return anObject === CPMultipleValuesMarker || anObject === CPNoSelectionMarker || anObject === CPNotApplicableMarker;
|
|
||||||
}
|
|
||||||
+1937
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,553 +0,0 @@
|
|||||||
@import "CPPanel.j"
|
|
||||||
@import "_CPMenuWindow.j"
|
|
||||||
|
|
||||||
|
|
||||||
var MENUBAR_HEIGHT = 28.0,
|
|
||||||
MENUBAR_MARGIN = 10.0,
|
|
||||||
MENUBAR_LEFT_MARGIN = 10.0,
|
|
||||||
MENUBAR_RIGHT_MARGIN = 10.0;
|
|
||||||
|
|
||||||
var _CPMenuBarWindowBackgroundColor = nil,
|
|
||||||
_CPMenuBarWindowFont = nil;
|
|
||||||
|
|
||||||
@implementation _CPMenuBarWindow : CPPanel
|
|
||||||
{
|
|
||||||
CPMenu _menu;
|
|
||||||
CPView _highlightView;
|
|
||||||
CPArray _menuItemViews;
|
|
||||||
|
|
||||||
CPMenuItem _trackingMenuItem;
|
|
||||||
|
|
||||||
CPImageView _iconImageView;
|
|
||||||
CPTextField _titleField;
|
|
||||||
|
|
||||||
CPColor _textColor;
|
|
||||||
CPColor _titleColor;
|
|
||||||
|
|
||||||
CPColor _textShadowColor;
|
|
||||||
CPColor _titleShadowColor;
|
|
||||||
|
|
||||||
CPColor _highlightColor;
|
|
||||||
CPColor _highlightTextColor;
|
|
||||||
CPColor _highlightTextShadowColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self != [_CPMenuBarWindow class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var bundle = [CPBundle bundleForClass:self];
|
|
||||||
|
|
||||||
_CPMenuBarWindowFont = [CPFont boldSystemFontOfSize:12.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPFont)font
|
|
||||||
{
|
|
||||||
return _CPMenuBarWindowFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
// This only shows up in browser land, so don't bother calculating metrics in desktop.
|
|
||||||
var contentRect = [[CPPlatformWindow primaryPlatformWindow] contentBounds];
|
|
||||||
|
|
||||||
contentRect.size.height = MENUBAR_HEIGHT;
|
|
||||||
|
|
||||||
self = [super initWithContentRect:contentRect styleMask:CPBorderlessWindowMask];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
[self setLevel:CPMainMenuWindowLevel];
|
|
||||||
[self setAutoresizingMask:CPWindowWidthSizable];
|
|
||||||
|
|
||||||
var contentView = [self contentView];
|
|
||||||
|
|
||||||
[contentView setAutoresizesSubviews:NO];
|
|
||||||
|
|
||||||
[self setBecomesKeyOnlyIfNeeded:YES];
|
|
||||||
|
|
||||||
//
|
|
||||||
_iconImageView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 16.0, 16.0)];
|
|
||||||
|
|
||||||
[contentView addSubview:_iconImageView];
|
|
||||||
|
|
||||||
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
[_titleField setFont:[CPFont boldSystemFontOfSize:13.0]];
|
|
||||||
[_titleField setAlignment:CPCenterTextAlignment];
|
|
||||||
[_titleField setTextShadowOffset:CGSizeMake(0, 1)];
|
|
||||||
|
|
||||||
[contentView addSubview:_titleField];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTitle:(CPString)aTitle
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var bundleName = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPBundleName"];
|
|
||||||
|
|
||||||
if (![bundleName length])
|
|
||||||
document.title = aTitle;
|
|
||||||
else if ([aTitle length])
|
|
||||||
document.title = aTitle + @" - " + bundleName;
|
|
||||||
else
|
|
||||||
document.title = bundleName;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[_titleField setStringValue:aTitle];
|
|
||||||
[_titleField sizeToFit];
|
|
||||||
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setIconImage:(CPImage)anImage
|
|
||||||
{
|
|
||||||
[_iconImageView setImage:anImage];
|
|
||||||
[_iconImageView setHidden:anImage == nil];
|
|
||||||
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setIconImageAlphaValue:(float)anAlphaValue
|
|
||||||
{
|
|
||||||
[_iconImageView setAlphaValue:anAlphaValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (!aColor)
|
|
||||||
{
|
|
||||||
if (!_CPMenuBarWindowBackgroundColor)
|
|
||||||
_CPMenuBarWindowBackgroundColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[_CPMenuBarWindow class]] pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackground.png"] size:CGSizeMake(1.0, 28.0)]];
|
|
||||||
|
|
||||||
[[self contentView] setBackgroundColor:_CPMenuBarWindowBackgroundColor];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[[self contentView] setBackgroundColor:aColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTextColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_textColor == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_textColor = aColor;
|
|
||||||
|
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(setTextColor:) withObject:_textColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTitleColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_titleColor == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_titleColor = aColor;
|
|
||||||
|
|
||||||
[_titleField setTextColor:aColor ? aColor : [CPColor blackColor]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTextShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_textShadowColor == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_textShadowColor = aColor;
|
|
||||||
|
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(setTextShadowColor:) withObject:_textShadowColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTitleShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_titleShadowColor == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_titleShadowColor = aColor;
|
|
||||||
|
|
||||||
[_titleField setTextShadowColor:aColor ? aColor : [CPColor whiteColor]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setHighlightColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_highlightColor == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_highlightColor = aColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setHighlightTextColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_highlightTextColor == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_highlightTextColor = aColor;
|
|
||||||
|
|
||||||
// [_menuItemViews makeObjectsPerformSelector:@selector(setActivateColor:) withObject:_highlightTextColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setHighlightTextShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_highlightTextShadowColor == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_highlightTextShadowColor = aColor;
|
|
||||||
|
|
||||||
// [_menuItemViews makeObjectsPerformSelector:@selector(setActivateShadowColor:) withObject:_highlightTextShadowColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMenu:(CPMenu)aMenu
|
|
||||||
{
|
|
||||||
if (_menu == aMenu)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
|
||||||
|
|
||||||
if (_menu)
|
|
||||||
{
|
|
||||||
[defaultCenter
|
|
||||||
removeObserver:self
|
|
||||||
name:CPMenuDidAddItemNotification
|
|
||||||
object:_menu];
|
|
||||||
|
|
||||||
[defaultCenter
|
|
||||||
removeObserver:self
|
|
||||||
name:CPMenuDidChangeItemNotification
|
|
||||||
object:_menu];
|
|
||||||
|
|
||||||
[defaultCenter
|
|
||||||
removeObserver:self
|
|
||||||
name:CPMenuDidRemoveItemNotification
|
|
||||||
object:_menu];
|
|
||||||
|
|
||||||
var items = [_menu itemArray],
|
|
||||||
count = items.length;
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
[[items[count] _menuItemView] removeFromSuperview];
|
|
||||||
}
|
|
||||||
|
|
||||||
_menu = aMenu;
|
|
||||||
|
|
||||||
if (_menu)
|
|
||||||
{
|
|
||||||
[defaultCenter
|
|
||||||
addObserver:self
|
|
||||||
selector:@selector(menuDidAddItem:)
|
|
||||||
name:CPMenuDidAddItemNotification
|
|
||||||
object:_menu];
|
|
||||||
|
|
||||||
[defaultCenter
|
|
||||||
addObserver:self
|
|
||||||
selector:@selector(menuDidChangeItem:)
|
|
||||||
name:CPMenuDidChangeItemNotification
|
|
||||||
object:_menu];
|
|
||||||
|
|
||||||
[defaultCenter
|
|
||||||
addObserver:self
|
|
||||||
selector:@selector(menuDidRemoveItem:)
|
|
||||||
name:CPMenuDidRemoveItemNotification
|
|
||||||
object:_menu];
|
|
||||||
}
|
|
||||||
|
|
||||||
_menuItemViews = [];
|
|
||||||
|
|
||||||
var contentView = [self contentView],
|
|
||||||
items = [_menu itemArray],
|
|
||||||
count = items.length;
|
|
||||||
|
|
||||||
for (index = 0; index < count; ++index)
|
|
||||||
{
|
|
||||||
var item = items[index],
|
|
||||||
menuItemView = [item _menuItemView];
|
|
||||||
|
|
||||||
_menuItemViews.push(menuItemView);
|
|
||||||
|
|
||||||
[menuItemView setTextColor:_textColor];
|
|
||||||
[menuItemView setHidden:[item isHidden]];
|
|
||||||
|
|
||||||
[menuItemView synchronizeWithMenuItem];
|
|
||||||
|
|
||||||
[contentView addSubview:menuItemView];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)menuDidChangeItem:(CPNotification)aNotification
|
|
||||||
{
|
|
||||||
var menuItem = [_menu itemAtIndex:[[aNotification userInfo] objectForKey:@"CPMenuItemIndex"]],
|
|
||||||
menuItemView = [menuItem _menuItemView];
|
|
||||||
|
|
||||||
[menuItemView setHidden:[menuItem isHidden]];
|
|
||||||
[menuItemView synchronizeWithMenuItem];
|
|
||||||
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)menuDidAddItem:(CPNotification)aNotification
|
|
||||||
{
|
|
||||||
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"],
|
|
||||||
menuItem = [_menu itemAtIndex:index],
|
|
||||||
menuItemView = [menuItem _menuItemView];
|
|
||||||
|
|
||||||
[_menuItemViews insertObject:menuItemView atIndex:index];
|
|
||||||
|
|
||||||
[menuItemView setTextColor:_textColor];
|
|
||||||
[menuItemView setHidden:[menuItem isHidden]];
|
|
||||||
|
|
||||||
[menuItemView synchronizeWithMenuItem];
|
|
||||||
|
|
||||||
[[self contentView] addSubview:menuItemView];
|
|
||||||
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)menuDidRemoveItem:(CPNotification)aNotification
|
|
||||||
{
|
|
||||||
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"],
|
|
||||||
menuItemView = [_menuItemViews objectAtIndex:index];
|
|
||||||
|
|
||||||
[_menuItemViews removeObjectAtIndex:index];
|
|
||||||
|
|
||||||
[menuItemView removeFromSuperview];
|
|
||||||
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
var constraintRect = CGRectInset([[self platformWindow] visibleFrame], 5.0, 0.0);
|
|
||||||
|
|
||||||
constraintRect.size.height -= 5.0;
|
|
||||||
|
|
||||||
[[_CPMenuManager sharedMenuManager]
|
|
||||||
beginTracking:anEvent
|
|
||||||
menuContainer:self
|
|
||||||
constraintRect:constraintRect
|
|
||||||
callback:function(aMenuContainer, aMenu)
|
|
||||||
{
|
|
||||||
[aMenu _performActionOfHighlightedItemChain];
|
|
||||||
[aMenu _highlightItemAtIndex:CPNotFound];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFont)font
|
|
||||||
{
|
|
||||||
[CPFont systemFontOfSize:12.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tile
|
|
||||||
{
|
|
||||||
var items = [_menu itemArray],
|
|
||||||
index = 0,
|
|
||||||
count = items.length,
|
|
||||||
|
|
||||||
x = MENUBAR_LEFT_MARGIN,
|
|
||||||
y = 0.0,
|
|
||||||
isLeftAligned = YES;
|
|
||||||
|
|
||||||
for (; index < count; ++index)
|
|
||||||
{
|
|
||||||
var item = items[index];
|
|
||||||
|
|
||||||
if ([item isSeparatorItem])
|
|
||||||
{
|
|
||||||
x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN;
|
|
||||||
isLeftAligned = NO;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([item isHidden])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var menuItemView = [item _menuItemView],
|
|
||||||
frame = [menuItemView frame];
|
|
||||||
|
|
||||||
if (isLeftAligned)
|
|
||||||
{
|
|
||||||
[menuItemView setFrame:CGRectMake(x, 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
|
|
||||||
|
|
||||||
x += CGRectGetWidth([menuItemView frame]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[menuItemView setFrame:CGRectMake(x - CGRectGetWidth(frame), 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
|
|
||||||
|
|
||||||
x = CGRectGetMinX([menuItemView frame]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var bounds = [[self contentView] bounds],
|
|
||||||
titleFrame = [_titleField frame];
|
|
||||||
|
|
||||||
if ([_iconImageView isHidden])
|
|
||||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(titleFrame)) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var iconFrame = [_iconImageView frame],
|
|
||||||
iconWidth = CGRectGetWidth(iconFrame),
|
|
||||||
totalWidth = iconWidth + CGRectGetWidth(titleFrame);
|
|
||||||
|
|
||||||
[_iconImageView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(iconFrame)) / 2.0)];
|
|
||||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0 + iconWidth, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFrame:(CGRect)aRect display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
|
||||||
{
|
|
||||||
var size = [self frame].size;
|
|
||||||
|
|
||||||
[super setFrame:aRect display:shouldDisplay animate:shouldAnimate];
|
|
||||||
|
|
||||||
if (!_CGSizeEqualToSize(size, aRect.size))
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPMenuBarWindow (CPMenuContainer)
|
|
||||||
|
|
||||||
- (BOOL)isMenuBar
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)globalFrame
|
|
||||||
{
|
|
||||||
return [self frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
|
||||||
{
|
|
||||||
return _CPMenuManagerScrollingStateNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPInteger)itemIndexAtPoint:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var items = [_menu itemArray],
|
|
||||||
index = items.length;
|
|
||||||
|
|
||||||
while (index--)
|
|
||||||
{
|
|
||||||
var item = items[index];
|
|
||||||
|
|
||||||
if ([item isHidden] || [item isSeparatorItem])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (CGRectContainsPoint([self rectForItemAtIndex:index], aPoint))
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CPNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectForItemAtIndex:(CPInteger)anIndex
|
|
||||||
{
|
|
||||||
var menuItem = [_menu itemAtIndex:anIndex === CPNotFound ? 0 : anIndex];
|
|
||||||
|
|
||||||
return [[menuItem _menuItemView] frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPMenuBarView : _CPMenuView
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectForItemAtIndex:(int)anIndex
|
|
||||||
{
|
|
||||||
return [_menuItemViews[anIndex === CPNotFound ? 0 : anIndex] frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)itemIndexAtPoint:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var bounds = [self bounds];
|
|
||||||
|
|
||||||
if (!CGRectContainsPoint(bounds, aPoint))
|
|
||||||
return CPNotFound;
|
|
||||||
|
|
||||||
var x = aPoint.x,
|
|
||||||
low = 0,
|
|
||||||
high = _visibleMenuItemInfos.length - 1;
|
|
||||||
|
|
||||||
while (low <= high)
|
|
||||||
{
|
|
||||||
var middle = FLOOR(low + (high - low) / 2),
|
|
||||||
info = _visibleMenuItemInfos[middle],
|
|
||||||
frame = [info.view frame];
|
|
||||||
|
|
||||||
if (x < CGRectGetMinX(frame))
|
|
||||||
high = middle - 1;
|
|
||||||
|
|
||||||
else if (x > CGRectGetMaxX(frame))
|
|
||||||
low = middle + 1;
|
|
||||||
|
|
||||||
else
|
|
||||||
return info.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CPNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tile
|
|
||||||
{
|
|
||||||
var items = [_menu itemArray],
|
|
||||||
index = 0,
|
|
||||||
count = items.length,
|
|
||||||
|
|
||||||
x = MENUBAR_LEFT_MARGIN,
|
|
||||||
y = 0.0,
|
|
||||||
isLeftAligned = YES;
|
|
||||||
|
|
||||||
for (; index < count; ++index)
|
|
||||||
{
|
|
||||||
var item = items[index];
|
|
||||||
|
|
||||||
if ([item isSeparatorItem])
|
|
||||||
{
|
|
||||||
x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN;
|
|
||||||
isLeftAligned = NO;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([item isHidden])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var menuItemView = [item _menuItemView],
|
|
||||||
frame = [menuItemView frame];
|
|
||||||
|
|
||||||
if (isLeftAligned)
|
|
||||||
{
|
|
||||||
[menuItemView setFrameOrigin:CGPointMake(x, (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)];
|
|
||||||
|
|
||||||
x += CGRectGetWidth([menuItemView frame]) + MENUBAR_MARGIN;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[menuItemView setFrameOrigin:CGPointMake(x - CGRectGetWidth(frame), (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)];
|
|
||||||
|
|
||||||
x = CGRectGetMinX([menuItemView frame]) - MENUBAR_MARGIN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var bounds = [[self contentView] bounds],
|
|
||||||
titleFrame = [_titleField frame];
|
|
||||||
|
|
||||||
if ([_iconImageView isHidden])
|
|
||||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(titleFrame)) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var iconFrame = [_iconImageView frame],
|
|
||||||
iconWidth = CGRectGetWidth(iconFrame),
|
|
||||||
totalWidth = iconWidth + CGRectGetWidth(titleFrame);
|
|
||||||
|
|
||||||
[_iconImageView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(iconFrame)) / 2.0)];
|
|
||||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0 + iconWidth, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,367 +0,0 @@
|
|||||||
@import <Foundation/CPObject.j>
|
|
||||||
|
|
||||||
|
|
||||||
_CPMenuManagerScrollingStateUp = -1,
|
|
||||||
_CPMenuManagerScrollingStateDown = 1,
|
|
||||||
_CPMenuManagerScrollingStateNone = 0;
|
|
||||||
|
|
||||||
var STICKY_TIME_INTERVAL = 500;
|
|
||||||
|
|
||||||
var SharedMenuManager = nil;
|
|
||||||
|
|
||||||
@implementation _CPMenuManager: CPObject
|
|
||||||
{
|
|
||||||
CPTimeInterval _startTime;
|
|
||||||
int _scrollingState;
|
|
||||||
CGPoint _lastGlobalLocation;
|
|
||||||
|
|
||||||
CPView _lastMouseOverMenuView;
|
|
||||||
|
|
||||||
CGRect _constraintRect;
|
|
||||||
CPMutableArray _menuContainerStack;
|
|
||||||
|
|
||||||
Function _trackingCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (_CPMenuManager)sharedMenuManager
|
|
||||||
{
|
|
||||||
if (!SharedMenuManager)
|
|
||||||
SharedMenuManager = [[_CPMenuManager alloc] init];
|
|
||||||
|
|
||||||
return SharedMenuManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
if (SharedMenuManager)
|
|
||||||
return SharedMenuManager;
|
|
||||||
|
|
||||||
return [super init];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)trackingMenuContainer
|
|
||||||
{
|
|
||||||
return _menuContainerStack[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPMenu)trackingMenu
|
|
||||||
{
|
|
||||||
return [[self trackingMenuContainer] menu];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)beginTracking:(CPEvent)anEvent
|
|
||||||
menuContainer:(id)aMenuContainer
|
|
||||||
constraintRect:(CGRect)aRect
|
|
||||||
callback:(Function)aCallback
|
|
||||||
{
|
|
||||||
var menu = [aMenuContainer menu];
|
|
||||||
|
|
||||||
CPApp._activeMenu = menu;
|
|
||||||
|
|
||||||
_startTime = [anEvent timestamp];//new Date();
|
|
||||||
_scrollingState = _CPMenuManagerScrollingStateNone;
|
|
||||||
|
|
||||||
_constraintRect = aRect;
|
|
||||||
_menuContainerStack = [aMenuContainer];
|
|
||||||
_trackingCallback = aCallback;
|
|
||||||
|
|
||||||
if (menu === [CPApp mainMenu])
|
|
||||||
{
|
|
||||||
var globalLocation = [anEvent globalLocation];
|
|
||||||
|
|
||||||
// Find which menu window the mouse is currently on top of
|
|
||||||
var menuLocation = [aMenuContainer convertGlobalToBase:globalLocation];
|
|
||||||
|
|
||||||
// Find out the item the mouse is currently on top of
|
|
||||||
var activeItemIndex = [aMenuContainer itemIndexAtPoint:menuLocation],
|
|
||||||
activeItem = activeItemIndex !== CPNotFound ? [menu itemAtIndex:activeItemIndex] : nil;
|
|
||||||
|
|
||||||
_menuBarButtonItemIndex = activeItemIndex;
|
|
||||||
_menuBarButtonMenuContainer = aMenuContainer;
|
|
||||||
|
|
||||||
if ([activeItem _isMenuBarButton])
|
|
||||||
return [self trackMenuBarButtonEvent:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self trackEvent:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)trackEvent:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
var type = [anEvent type],
|
|
||||||
trackingMenu = [self trackingMenu];
|
|
||||||
|
|
||||||
// Close Menu Event.
|
|
||||||
if (type === CPAppKitDefined)
|
|
||||||
return [self completeTracking];
|
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES];
|
|
||||||
|
|
||||||
// Periodic events don't have a valid location.
|
|
||||||
var globalLocation = type === CPPeriodic ? _lastGlobalLocation : [anEvent globalLocation];
|
|
||||||
|
|
||||||
// Remember this for the next periodic event.
|
|
||||||
_lastGlobalLocation = globalLocation;
|
|
||||||
|
|
||||||
// Find which menu window the mouse is currently on top of
|
|
||||||
var activeMenuContainer = [self menuContainerForPoint:globalLocation],
|
|
||||||
activeMenu = [activeMenuContainer menu],
|
|
||||||
menuLocation = [activeMenuContainer convertGlobalToBase:globalLocation];
|
|
||||||
|
|
||||||
// Find out the item the mouse is currently on top of
|
|
||||||
var activeItemIndex = activeMenuContainer ? [activeMenuContainer itemIndexAtPoint:menuLocation] : CPNotFound,
|
|
||||||
activeItem = activeItemIndex !== CPNotFound ? [activeMenu itemAtIndex:activeItemIndex] : nil;
|
|
||||||
|
|
||||||
// If the item isn't enabled its as if we clicked on nothing.
|
|
||||||
if (![activeItem isEnabled] || [activeItem _isMenuBarButton])
|
|
||||||
{
|
|
||||||
activeItemIndex = CPNotFound;
|
|
||||||
activeItem = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mouseOverMenuView = [activeItem view];
|
|
||||||
|
|
||||||
if (type === CPPeriodic)
|
|
||||||
{
|
|
||||||
if (_scrollingState === _CPMenuManagerScrollingStateUp)
|
|
||||||
[activeMenuContainer scrollUp];
|
|
||||||
|
|
||||||
else if (_scrollingState === _CPMenuManagerScrollingStateDown)
|
|
||||||
[activeMenuContainer scrollDown];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're over a custom menu view...
|
|
||||||
if (mouseOverMenuView)
|
|
||||||
{
|
|
||||||
if (!_lastMouseOverMenuView)
|
|
||||||
[activeMenu _highlightItemAtIndex:CPNotFound];
|
|
||||||
|
|
||||||
if (_lastMouseOverMenuView != mouseOverMenuView)
|
|
||||||
{
|
|
||||||
[mouseOverMenuView mouseExited:anEvent];
|
|
||||||
// FIXME: Possibly multiple of these?
|
|
||||||
[_lastMouseOverMenuView mouseEntered:anEvent];
|
|
||||||
|
|
||||||
_lastMouseOverMenuView = mouseOverMenuView;
|
|
||||||
}
|
|
||||||
|
|
||||||
var menuContainerWindow = activeMenuContainer;
|
|
||||||
|
|
||||||
if (![menuContainerWindow isKindOfClass:[CPWindow class]])
|
|
||||||
menuContainerWindow = [menuContainerWindow window];
|
|
||||||
|
|
||||||
[menuContainerWindow
|
|
||||||
sendEvent:[CPEvent mouseEventWithType:type location:menuLocation modifierFlags:[anEvent modifierFlags]
|
|
||||||
timestamp:[anEvent timestamp]
|
|
||||||
windowNumber:menuContainerWindow
|
|
||||||
context:nil
|
|
||||||
eventNumber:0
|
|
||||||
clickCount:[anEvent clickCount]
|
|
||||||
pressure:[anEvent pressure]]];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_lastMouseOverMenuView)
|
|
||||||
{
|
|
||||||
[_lastMouseOverMenuView mouseExited:anEvent];
|
|
||||||
_lastMouseOverMenuView = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
[activeMenu _highlightItemAtIndex:activeItemIndex];
|
|
||||||
|
|
||||||
if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown)
|
|
||||||
{
|
|
||||||
var oldScrollingState = _scrollingState;
|
|
||||||
|
|
||||||
_scrollingState = [activeMenuContainer scrollingStateForPoint:globalLocation];
|
|
||||||
|
|
||||||
if (_scrollingState !== oldScrollingState)
|
|
||||||
{
|
|
||||||
if (_scrollingState === _CPMenuManagerScrollingStateNone)
|
|
||||||
[CPEvent stopPeriodicEvents];
|
|
||||||
|
|
||||||
else if (oldScrollingState === _CPMenuManagerScrollingStateNone)
|
|
||||||
[CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type === CPLeftMouseUp && ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL))
|
|
||||||
[trackingMenu cancelTracking];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the item has a submenu, show it.
|
|
||||||
if ([activeItem hasSubmenu])// && [activeItem action] === @selector(submenuAction:))
|
|
||||||
{
|
|
||||||
var activeItemRect = [activeMenuContainer rectForItemAtIndex:activeItemIndex];
|
|
||||||
|
|
||||||
if ([activeMenuContainer isMenuBar])
|
|
||||||
var newMenuOrigin = CGPointMake(CGRectGetMinX(activeItemRect), CGRectGetMaxY(activeItemRect));
|
|
||||||
else
|
|
||||||
var newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect));
|
|
||||||
|
|
||||||
newMenuOrigin = [activeMenuContainer convertBaseToGlobal:newMenuOrigin];
|
|
||||||
|
|
||||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin];
|
|
||||||
}
|
|
||||||
|
|
||||||
// This handles both the case where we've moved away from the menu, and where
|
|
||||||
// we've moved to an item without a submenu.
|
|
||||||
else
|
|
||||||
[self showMenu:nil fromMenu:activeMenu atPoint:CGPointMakeZero()];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)trackMenuBarButtonEvent:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
var type = [anEvent type];
|
|
||||||
|
|
||||||
if (type === CPAppKitDefined)
|
|
||||||
return [self completeTracking];
|
|
||||||
|
|
||||||
var globalLocation = [anEvent globalLocation];
|
|
||||||
|
|
||||||
// Find which menu window the mouse is currently on top of
|
|
||||||
var menu = [self trackingMenu],
|
|
||||||
trackingMenuContainer = [self trackingMenuContainer],
|
|
||||||
menuLocation = [trackingMenuContainer convertGlobalToBase:globalLocation];
|
|
||||||
|
|
||||||
if ([trackingMenuContainer itemIndexAtPoint:menuLocation] === _menuBarButtonItemIndex)
|
|
||||||
[menu _highlightItemAtIndex:_menuBarButtonItemIndex];
|
|
||||||
|
|
||||||
else
|
|
||||||
[menu _highlightItemAtIndex:CPNotFound];
|
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackMenuBarButtonEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES];
|
|
||||||
|
|
||||||
if (type === CPLeftMouseUp)
|
|
||||||
[menu cancelTracking];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)completeTracking
|
|
||||||
{
|
|
||||||
var trackingMenu = [self trackingMenu];
|
|
||||||
|
|
||||||
// Stop all periodic events at this point.
|
|
||||||
[CPEvent stopPeriodicEvents];
|
|
||||||
|
|
||||||
// Get the highlighted item from the original menu.
|
|
||||||
var highlightedItem = [trackingMenu highlightedItem];
|
|
||||||
|
|
||||||
// Hide all submenus.
|
|
||||||
[self showMenu:nil fromMenu:trackingMenu atPoint:nil];
|
|
||||||
|
|
||||||
var delegate = [trackingMenu delegate];
|
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(menuDidClose:)])
|
|
||||||
[delegate menuDidClose:trackingMenu];
|
|
||||||
|
|
||||||
if (_trackingCallback)
|
|
||||||
_trackingCallback([self trackingMenuContainer], trackingMenu);
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
|
||||||
postNotificationName:CPMenuDidEndTrackingNotification
|
|
||||||
object:trackingMenu];
|
|
||||||
|
|
||||||
CPApp._activeMenu = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)menuContainerForPoint:(float)aGlobalLocation
|
|
||||||
{
|
|
||||||
var count = [_menuContainerStack count],
|
|
||||||
firstMenuContainer = _menuContainerStack[0];
|
|
||||||
|
|
||||||
// Trivial case.
|
|
||||||
if (count === 1)
|
|
||||||
return firstMenuContainer;
|
|
||||||
|
|
||||||
var firstMenuWindowIndex = 0,
|
|
||||||
lastMenuWindowIndex = count - 1;
|
|
||||||
|
|
||||||
// If the first menu container is a bar, then it has to fall strictly within its bounds.
|
|
||||||
if ([firstMenuContainer isMenuBar])
|
|
||||||
{
|
|
||||||
if (CGRectContainsPoint([firstMenuContainer globalFrame], aGlobalLocation))
|
|
||||||
return firstMenuContainer;
|
|
||||||
|
|
||||||
firstMenuWindowIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = count,
|
|
||||||
x = aGlobalLocation.x,
|
|
||||||
closerDeltaX = Infinity,
|
|
||||||
closerMenuContainer = nil;
|
|
||||||
|
|
||||||
while (index-- > firstMenuWindowIndex)
|
|
||||||
{
|
|
||||||
var menuContainer = _menuContainerStack[index],
|
|
||||||
menuContainerFrame = [menuContainer globalFrame],
|
|
||||||
menuContainerMinX = _CGRectGetMinX(menuContainerFrame),
|
|
||||||
menuContainerMaxX = _CGRectGetMaxX(menuContainerFrame);
|
|
||||||
|
|
||||||
// If within the x bounds of this menu container, return it.
|
|
||||||
if (x < menuContainerMaxX && x >= menuContainerMinX)
|
|
||||||
return menuContainer;
|
|
||||||
|
|
||||||
// If this is either the first or last menu *window*, check to see how close we are to it.
|
|
||||||
if (index === firstMenuWindowIndex || index === lastMenuWindowIndex)
|
|
||||||
{
|
|
||||||
var deltaX = ABS(x < menuContainerMinX ? menuContainerMinX - x : menuContainerMaxX - x);
|
|
||||||
|
|
||||||
if (deltaX < closerDeltaX)
|
|
||||||
{
|
|
||||||
closerMenuContainer = menuContainer;
|
|
||||||
closerDeltaX = deltaX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return closerMenuContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)showMenu:(CPMenu)newMenu fromMenu:(CPMenu)baseMenu atPoint:(CGPoint)aGlobalLocation
|
|
||||||
{
|
|
||||||
var count = _menuContainerStack.length,
|
|
||||||
index = count;
|
|
||||||
|
|
||||||
// Hide all menus up to the base menu...
|
|
||||||
while (index--)
|
|
||||||
{
|
|
||||||
var menuContainer = _menuContainerStack[index],
|
|
||||||
menu = [menuContainer menu];
|
|
||||||
|
|
||||||
// If we reach the base menu, or this menu is already being shown, break.
|
|
||||||
if (menu === baseMenu)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// If this menu is already being shown, unhighlight and return.
|
|
||||||
if (menu === newMenu)//&& [menu supermenu] === baseMenu)
|
|
||||||
return [newMenu _highlightItemAtIndex:CPNotFound];
|
|
||||||
|
|
||||||
[menuContainer orderOut:self];
|
|
||||||
[menuContainer setMenu:nil];
|
|
||||||
|
|
||||||
[_CPMenuWindow poolMenuWindow:menuContainer];
|
|
||||||
[_menuContainerStack removeObjectAtIndex:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newMenu)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Unhighlight any previously highlighted item.
|
|
||||||
[newMenu _highlightItemAtIndex:CPNotFound];
|
|
||||||
|
|
||||||
var menuWindow = [_CPMenuWindow menuWindowWithMenu:newMenu font:[_menuContainerStack[0] font]];
|
|
||||||
|
|
||||||
[_menuContainerStack addObject:menuWindow];
|
|
||||||
|
|
||||||
[menuWindow setConstraintRect:_constraintRect];
|
|
||||||
|
|
||||||
// If our parent menu is a menu bar...
|
|
||||||
if (baseMenu === [self trackingMenu] && [[self trackingMenuContainer] isMenuBar])
|
|
||||||
[menuWindow setBackgroundStyle:_CPMenuWindowMenuBarBackgroundStyle];
|
|
||||||
else
|
|
||||||
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
|
||||||
|
|
||||||
[menuWindow setFrameOrigin:aGlobalLocation];
|
|
||||||
[menuWindow orderFront:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,547 +0,0 @@
|
|||||||
@import "CPWindow.j"
|
|
||||||
|
|
||||||
|
|
||||||
var _CPMenuWindowPool = [],
|
|
||||||
_CPMenuWindowPoolCapacity = 5,
|
|
||||||
|
|
||||||
_CPMenuWindowBackgroundColors = [];
|
|
||||||
|
|
||||||
_CPMenuWindowMenuBarBackgroundStyle = 0;
|
|
||||||
_CPMenuWindowPopUpBackgroundStyle = 1;
|
|
||||||
_CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|
||||||
|
|
||||||
var STICKY_TIME_INTERVAL = 500,
|
|
||||||
|
|
||||||
TOP_MARGIN = 5.0,
|
|
||||||
LEFT_MARGIN = 1.0,
|
|
||||||
RIGHT_MARGIN = 1.0,
|
|
||||||
BOTTOM_MARGIN = 5.0,
|
|
||||||
|
|
||||||
SCROLL_INDICATOR_HEIGHT = 16.0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
@implementation _CPMenuWindow : CPWindow
|
|
||||||
{
|
|
||||||
_CPMenuView _menuView;
|
|
||||||
CPClipView _menuClipView;
|
|
||||||
|
|
||||||
CPImageView _moreAboveView;
|
|
||||||
CPImageView _moreBelowView;
|
|
||||||
|
|
||||||
CGRect _unconstrainedFrame;
|
|
||||||
CGRect _constraintRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)menuWindowWithMenu:(CPMenu)aMenu font:(CPFont)aFont
|
|
||||||
{
|
|
||||||
var menuWindow = nil;
|
|
||||||
|
|
||||||
if (_CPMenuWindowPool.length)
|
|
||||||
{
|
|
||||||
menuWindow = _CPMenuWindowPool.pop();
|
|
||||||
|
|
||||||
// Do this so that coordinates will be accurate.
|
|
||||||
[menuWindow setFrameOrigin:CGPointMakeZero()];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
menuWindow = [[_CPMenuWindow alloc] init];
|
|
||||||
|
|
||||||
[menuWindow setFont:aFont];
|
|
||||||
[menuWindow setMenu:aMenu];
|
|
||||||
[menuWindow setMinWidth:[aMenu minimumWidth]];
|
|
||||||
|
|
||||||
return menuWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)poolMenuWindow:(_CPMenuWindow)aMenuWindow
|
|
||||||
{
|
|
||||||
if (!aMenuWindow || _CPMenuWindowPool.length >= _CPMenuWindowPoolCapacity)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_CPMenuWindowPool.push(aMenuWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self != [_CPMenuWindow class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var bundle = [CPBundle bundleForClass:self];
|
|
||||||
|
|
||||||
_CPMenuWindowMoreAboveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreAbove.png"] size:CGSizeMake(38.0, 18.0)];
|
|
||||||
_CPMenuWindowMoreBelowImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreBelow.png"] size:CGSizeMake(38.0, 18.0)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithContentRect:(CGRect)aRect styleMask:(unsigned)aStyleMask
|
|
||||||
{
|
|
||||||
_constraintRect = _CGRectMakeZero();
|
|
||||||
_unconstrainedFrame = _CGRectMakeZero();
|
|
||||||
|
|
||||||
self = [super initWithContentRect:aRect styleMask:CPBorderlessWindowMask];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
[self setLevel:CPPopUpMenuWindowLevel];
|
|
||||||
[self setHasShadow:YES];
|
|
||||||
[self setShadowStyle:CPMenuWindowShadowStyle];
|
|
||||||
[self setAcceptsMouseMovedEvents:YES];
|
|
||||||
|
|
||||||
var contentView = [self contentView];
|
|
||||||
|
|
||||||
_menuView = [[_CPMenuView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
_menuClipView = [[CPClipView alloc] initWithFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, 0.0, 0.0)];
|
|
||||||
[_menuClipView setDocumentView:_menuView];
|
|
||||||
|
|
||||||
[contentView addSubview:_menuClipView];
|
|
||||||
|
|
||||||
_moreAboveView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
[_moreAboveView setImage:_CPMenuWindowMoreAboveImage];
|
|
||||||
[_moreAboveView setFrameSize:[_CPMenuWindowMoreAboveImage size]];
|
|
||||||
|
|
||||||
[contentView addSubview:_moreAboveView];
|
|
||||||
|
|
||||||
_moreBelowView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
[_moreBelowView setImage:_CPMenuWindowMoreBelowImage];
|
|
||||||
[_moreBelowView setFrameSize:[_CPMenuWindowMoreBelowImage size]];
|
|
||||||
|
|
||||||
[contentView addSubview:_moreBelowView];
|
|
||||||
|
|
||||||
[self setShadowStyle:CPWindowShadowStyleMenu];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (float)_standardLeftMargin
|
|
||||||
{
|
|
||||||
return LEFT_MARGIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
[_menuView setFont:aFont];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFont)font
|
|
||||||
{
|
|
||||||
return [_menuView font];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPColor)backgroundColorForBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle
|
|
||||||
{
|
|
||||||
var color = _CPMenuWindowBackgroundColors[aBackgroundStyle];
|
|
||||||
|
|
||||||
if (!color)
|
|
||||||
{
|
|
||||||
var bundle = [CPBundle bundleForClass:[self class]];
|
|
||||||
|
|
||||||
if (aBackgroundStyle == _CPMenuWindowPopUpBackgroundStyle)
|
|
||||||
color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
|
||||||
[
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded0.png"] size:CGSizeMake(4.0, 4.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow1.png"] size:CGSizeMake(1.0, 4.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded2.png"] size:CGSizeMake(4.0, 4.0)],
|
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)],
|
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)]
|
|
||||||
]]];
|
|
||||||
|
|
||||||
else if (aBackgroundStyle == _CPMenuWindowMenuBarBackgroundStyle)
|
|
||||||
color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
|
||||||
[
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 0.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 0.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 0.0)],
|
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)],
|
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)]
|
|
||||||
]]];
|
|
||||||
|
|
||||||
_CPMenuWindowBackgroundColors[aBackgroundStyle] = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle
|
|
||||||
{
|
|
||||||
[self setBackgroundColor:[[self class] backgroundColorForBackgroundStyle:aBackgroundStyle]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMenu:(CPMenu)aMenu
|
|
||||||
{
|
|
||||||
[aMenu _setMenuWindow:self];
|
|
||||||
[_menuView setMenu:aMenu];
|
|
||||||
|
|
||||||
var menuViewSize = [_menuView frame].size;
|
|
||||||
|
|
||||||
[self setFrameSize:CGSizeMake(LEFT_MARGIN + menuViewSize.width + RIGHT_MARGIN, TOP_MARGIN + menuViewSize.height + BOTTOM_MARGIN)];
|
|
||||||
|
|
||||||
[_menuView scrollPoint:CGPointMake(0.0, 0.0)];
|
|
||||||
[_menuClipView setFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, menuViewSize.width, menuViewSize.height)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMinWidth:(float)aWidth
|
|
||||||
{
|
|
||||||
var size = [self unconstrainedFrame].size;
|
|
||||||
|
|
||||||
[self setFrameSize:CGSizeMake(MAX(size.width, aWidth), size.height)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPMenu)menu
|
|
||||||
{
|
|
||||||
return [_menuView menu];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)orderFront:(id)aSender
|
|
||||||
{
|
|
||||||
[self setFrame:_unconstrainedFrame];
|
|
||||||
|
|
||||||
[super orderFront:aSender];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setConstraintRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
_constraintRect = aRect;
|
|
||||||
|
|
||||||
[self setFrame:_unconstrainedFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)unconstrainedFrame
|
|
||||||
{
|
|
||||||
return _CGRectMakeCopy(_unconstrainedFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need this because if not this will call setFrame: with -frame instead of -unconstrainedFrame, turning
|
|
||||||
// the constrained frame into the unconstrained frame.
|
|
||||||
- (void)setFrameOrigin:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
[super setFrame:_CGRectMake(aPoint.x, aPoint.y, _CGRectGetWidth(_unconstrainedFrame), _CGRectGetHeight(_unconstrainedFrame))];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFrameSize:(CGSize)aSize
|
|
||||||
{
|
|
||||||
[super setFrame:_CGRectMake(_CGRectGetMinX(_unconstrainedFrame), _CGRectGetMinY(_unconstrainedFrame), aSize.width, aSize.height)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
|
||||||
{
|
|
||||||
// FIXME: There are integral window issues with platform windows.
|
|
||||||
// FIXME: This gets called far too often.
|
|
||||||
_unconstrainedFrame = _CGRectMakeCopy(aFrame);
|
|
||||||
|
|
||||||
var constrainedFrame = CGRectIntersection(_unconstrainedFrame, _constraintRect);
|
|
||||||
|
|
||||||
// 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(_constraintRect))
|
|
||||||
constrainedFrame.size.width = CGRectGetWidth(_constraintRect);
|
|
||||||
|
|
||||||
if (CGRectGetMaxX(constrainedFrame) > CGRectGetMaxX(_constraintRect))
|
|
||||||
constrainedFrame.origin.x -= CGRectGetMaxX(constrainedFrame) - CGRectGetMaxX(_constraintRect);
|
|
||||||
|
|
||||||
if (CGRectGetMinX(constrainedFrame) < CGRectGetMinX(_constraintRect))
|
|
||||||
constrainedFrame.origin.x = CGRectGetMinX(_constraintRect);
|
|
||||||
|
|
||||||
[super setFrame:constrainedFrame display:shouldDisplay animate:shouldAnimate];
|
|
||||||
|
|
||||||
// This needs to happen before changing the frame.
|
|
||||||
var menuViewOrigin = CGPointMake(CGRectGetMinX(aFrame) + LEFT_MARGIN, CGRectGetMinY(aFrame) + TOP_MARGIN),
|
|
||||||
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];
|
|
||||||
|
|
||||||
if (moreAbove)
|
|
||||||
{
|
|
||||||
topMargin += SCROLL_INDICATOR_HEIGHT;
|
|
||||||
|
|
||||||
var frame = [_moreAboveView frame];
|
|
||||||
|
|
||||||
[_moreAboveView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(frame)) / 2.0, (TOP_MARGIN + SCROLL_INDICATOR_HEIGHT - CGRectGetHeight(frame)) / 2.0)];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_moreAboveView setHidden:!moreAbove];
|
|
||||||
|
|
||||||
if (moreBelow)
|
|
||||||
{
|
|
||||||
bottomMargin += SCROLL_INDICATOR_HEIGHT;
|
|
||||||
|
|
||||||
[_moreBelowView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth([_moreBelowView frame])) / 2.0, CGRectGetHeight(bounds) - SCROLL_INDICATOR_HEIGHT - BOTTOM_MARGIN)];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_moreBelowView setHidden:!moreBelow];
|
|
||||||
|
|
||||||
var clipFrame = CGRectMake(LEFT_MARGIN, topMargin, CGRectGetWidth(constrainedFrame) - LEFT_MARGIN - RIGHT_MARGIN, CGRectGetHeight(constrainedFrame) - topMargin - bottomMargin)
|
|
||||||
|
|
||||||
[_menuClipView setFrame:clipFrame];
|
|
||||||
[_menuView setFrameSize:CGSizeMake(CGRectGetWidth(clipFrame), CGRectGetHeight([_menuView frame]))];
|
|
||||||
|
|
||||||
[_menuView scrollPoint:CGPointMake(0.0, [self convertBaseToGlobal:clipFrame.origin].y - menuViewOrigin.y)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)hasMinimumNumberOfVisibleItems
|
|
||||||
{
|
|
||||||
var visibleRect = [_menuView visibleRect];
|
|
||||||
|
|
||||||
// Clearly if the entire view isn't visible the minimum won't be visible.
|
|
||||||
if (CGRectIsEmpty(visibleRect))
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
var numberOfUnhiddenItems = [_menuView numberOfUnhiddenItems],
|
|
||||||
minimumNumberOfVisibleItems = MIN(numberOfUnhiddenItems, 3),
|
|
||||||
count = 0,
|
|
||||||
index = [_menuView itemIndexAtPoint:[_menuView convertPoint:[_menuClipView frame].origin fromView:nil]];
|
|
||||||
|
|
||||||
for (; index < numberOfUnhiddenItems && count < minimumNumberOfVisibleItems; ++index)
|
|
||||||
{
|
|
||||||
var itemRect = [_menuView rectForUnhiddenItemAtIndex:index],
|
|
||||||
visibleItemRect = CGRectIntersection(visibleRect, itemRect);
|
|
||||||
|
|
||||||
// As soon as we get to the first unhidden item that is no longer visible, stop.
|
|
||||||
if (CGRectIsEmpty(visibleItemRect))
|
|
||||||
break;
|
|
||||||
|
|
||||||
// If the item is *completely* visible, count it.
|
|
||||||
if (CGRectEqualToRect(visibleItemRect, itemRect))
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count >= minimumNumberOfVisibleItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canScrollUp
|
|
||||||
{
|
|
||||||
return ![_moreAboveView isHidden];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canScrollDown
|
|
||||||
{
|
|
||||||
return ![_moreBelowView isHidden];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canScroll
|
|
||||||
{
|
|
||||||
return [self canScrollUp] || [self canScrollDown];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollUp
|
|
||||||
{
|
|
||||||
if (CGRectGetMinY(_unconstrainedFrame) >= CGRectGetMinY(_constraintRect))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_unconstrainedFrame.origin.y += 10;
|
|
||||||
|
|
||||||
[self setFrame:_unconstrainedFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollDown
|
|
||||||
{
|
|
||||||
if (CGRectGetMaxY(_unconstrainedFrame) <= CGRectGetHeight(_constraintRect))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_unconstrainedFrame.origin.y -= 10;
|
|
||||||
|
|
||||||
[self setFrame:_unconstrainedFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPMenuWindow (CPMenuContainer)
|
|
||||||
|
|
||||||
- (CGRect)globalFrame
|
|
||||||
{
|
|
||||||
return [self frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isMenuBar
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
|
||||||
{
|
|
||||||
var frame = [self frame];
|
|
||||||
|
|
||||||
if (![self canScroll])
|
|
||||||
return _CPMenuManagerScrollingStateNone;
|
|
||||||
|
|
||||||
// If we're at or above of the top scroll indicator...
|
|
||||||
if (aGlobalLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT)
|
|
||||||
return _CPMenuManagerScrollingStateUp;
|
|
||||||
|
|
||||||
// If we're at or below the bottom scroll indicator...
|
|
||||||
if (aGlobalLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT)
|
|
||||||
return _CPMenuManagerScrollingStateDown;
|
|
||||||
|
|
||||||
return _CPMenuManagerScrollingStateNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)deltaYForItemAtIndex:(int)anIndex
|
|
||||||
{
|
|
||||||
return TOP_MARGIN + CGRectGetMinY([_menuView rectForItemAtIndex:anIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGPoint)rectForItemAtIndex:(int)anIndex
|
|
||||||
{
|
|
||||||
return [_menuView convertRect:[_menuView rectForItemAtIndex:anIndex] toView:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)itemIndexAtPoint:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
// Don't return indexes of items that aren't visible.
|
|
||||||
if (!CGRectContainsPoint([_menuClipView bounds], [_menuClipView convertPoint:aPoint fromView:nil]))
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return [_menuView itemIndexAtPoint:[_menuView convertPoint:aPoint fromView:nil]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
/*
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
|
|
||||||
@implementation _CPMenuView : CPView
|
|
||||||
{
|
|
||||||
CPArray _menuItemViews;
|
|
||||||
CPArray _visibleMenuItemInfos;
|
|
||||||
|
|
||||||
CPFont _font @accessors(property=font);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)numberOfUnhiddenItems
|
|
||||||
{
|
|
||||||
return _visibleMenuItemInfos.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectForUnhiddenItemAtIndex:(int)anIndex
|
|
||||||
{
|
|
||||||
return [self rectForItemAtIndex:_visibleMenuItemInfos[anIndex].index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectForItemAtIndex:(int)anIndex
|
|
||||||
{
|
|
||||||
return [_menuItemViews[anIndex === CPNotFound ? 0 : anIndex] frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)itemIndexAtPoint:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var x = aPoint.x,
|
|
||||||
bounds = [self bounds];
|
|
||||||
|
|
||||||
if (x < CGRectGetMinX(bounds) || x > CGRectGetMaxX(bounds))
|
|
||||||
return CPNotFound;
|
|
||||||
|
|
||||||
var y = aPoint.y,
|
|
||||||
low = 0,
|
|
||||||
high = _visibleMenuItemInfos.length - 1;
|
|
||||||
|
|
||||||
while (low <= high)
|
|
||||||
{
|
|
||||||
var middle = FLOOR(low + (high - low) / 2),
|
|
||||||
info = _visibleMenuItemInfos[middle]
|
|
||||||
frame = [info.view frame];
|
|
||||||
|
|
||||||
if (y < CGRectGetMinY(frame))
|
|
||||||
high = middle - 1;
|
|
||||||
|
|
||||||
else if (y > CGRectGetMaxY(frame))
|
|
||||||
low = middle + 1;
|
|
||||||
|
|
||||||
else
|
|
||||||
return info.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CPNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tile
|
|
||||||
{
|
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
|
||||||
|
|
||||||
_menuItemViews = [];
|
|
||||||
_visibleMenuItemInfos = [];
|
|
||||||
|
|
||||||
var menu = [self menu];
|
|
||||||
|
|
||||||
if (!menu)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var items = [menu itemArray],
|
|
||||||
index = 0,
|
|
||||||
count = [items count],
|
|
||||||
maxWidth = 0,
|
|
||||||
y = 0,
|
|
||||||
showsStateColumn = [menu showsStateColumn];
|
|
||||||
|
|
||||||
for (; index < count; ++index)
|
|
||||||
{
|
|
||||||
var item = items[index],
|
|
||||||
view = [item _menuItemView];
|
|
||||||
|
|
||||||
_menuItemViews.push(view);
|
|
||||||
|
|
||||||
if ([item isHidden])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_visibleMenuItemInfos.push({ view:view, index:index });
|
|
||||||
|
|
||||||
[view setFont:_font];
|
|
||||||
[view setShowsStateColumn:showsStateColumn];
|
|
||||||
[view synchronizeWithMenuItem];
|
|
||||||
|
|
||||||
[view setFrameOrigin:CGPointMake(0.0, y)];
|
|
||||||
|
|
||||||
[self addSubview:view];
|
|
||||||
|
|
||||||
var size = [view minSize],
|
|
||||||
width = size.width;
|
|
||||||
|
|
||||||
if (maxWidth < width)
|
|
||||||
maxWidth = width;
|
|
||||||
|
|
||||||
y += size.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (index = 0; index < count; ++index)
|
|
||||||
{
|
|
||||||
var view = _menuItemViews[index];
|
|
||||||
|
|
||||||
[view setFrameSize:CGSizeMake(maxWidth, CGRectGetHeight([view frame]))];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setAutoresizesSubviews:NO];
|
|
||||||
[self setFrameSize:CGSizeMake(maxWidth, y)];
|
|
||||||
[self setAutoresizesSubviews:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMenu:(CPMenu)aMenu
|
|
||||||
{
|
|
||||||
[super setMenu:aMenu];
|
|
||||||
[self tile];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,239 +0,0 @@
|
|||||||
@import "CPView.j"
|
|
||||||
|
|
||||||
|
|
||||||
var HORIZONTAL_MARGIN = 8.0,
|
|
||||||
SUBMENU_INDICATOR_MARGIN = 3.0,
|
|
||||||
VERTICAL_MARGIN = 4.0;
|
|
||||||
|
|
||||||
var SelectionColor = nil,
|
|
||||||
SUBMENU_INDICATOR_COLOR = nil,
|
|
||||||
_CPMenuItemSelectionColor = nil,
|
|
||||||
_CPMenuItemTextShadowColor = nil,
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages = [],
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
|
||||||
|
|
||||||
@implementation _CPMenuItemMenuBarView : CPView
|
|
||||||
{
|
|
||||||
CPMenuItem _menuItem @accessors(property=menuItem);
|
|
||||||
|
|
||||||
CPFont _font;
|
|
||||||
CPColor _textColor;
|
|
||||||
CPColor _textShadowColor;
|
|
||||||
|
|
||||||
BOOL _isDirty;
|
|
||||||
|
|
||||||
_CPImageAndTextView _imageAndTextView;
|
|
||||||
CPView _submenuIndicatorView;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self !== [_CPMenuItemMenuBarView class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var bundle = [CPBundle bundleForClass:self];
|
|
||||||
|
|
||||||
SelectionColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackgroundSelected.png"] size:CGSizeMake(1.0, 28.0)]];
|
|
||||||
|
|
||||||
SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
|
|
||||||
|
|
||||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
|
||||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)view
|
|
||||||
{
|
|
||||||
return [[self alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(HORIZONTAL_MARGIN, 0.0, 0.0, 0.0)];
|
|
||||||
|
|
||||||
[_imageAndTextView setImagePosition:CPImageLeft];
|
|
||||||
[_imageAndTextView setImageOffset:3.0];
|
|
||||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
|
||||||
[_imageAndTextView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
|
|
||||||
|
|
||||||
[self addSubview:_imageAndTextView];
|
|
||||||
|
|
||||||
_submenuIndicatorView = [[_CPMenuItemMenuBarSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 9.0, 6.0)];
|
|
||||||
|
|
||||||
[_submenuIndicatorView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
|
|
||||||
|
|
||||||
[self addSubview:_submenuIndicatorView];
|
|
||||||
|
|
||||||
[self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textColor
|
|
||||||
{
|
|
||||||
if (![_menuItem isEnabled])
|
|
||||||
return [CPColor lightGrayColor];
|
|
||||||
|
|
||||||
return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textShadowColor
|
|
||||||
{
|
|
||||||
if (![_menuItem isEnabled])
|
|
||||||
return [CPColor colorWithWhite:0.8 alpha:0.8];
|
|
||||||
|
|
||||||
return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)update
|
|
||||||
{
|
|
||||||
var x = HORIZONTAL_MARGIN,
|
|
||||||
height = 0.0;
|
|
||||||
|
|
||||||
[_imageAndTextView setFont:[_menuItem font] || [_CPMenuBarWindow font]];
|
|
||||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
|
||||||
[_imageAndTextView setImage:[_menuItem image]];
|
|
||||||
[_imageAndTextView setText:[_menuItem title]];
|
|
||||||
[_imageAndTextView setTextColor:[self textColor]];
|
|
||||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
|
||||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
|
||||||
[_imageAndTextView sizeToFit];
|
|
||||||
|
|
||||||
var imageAndTextViewFrame = [_imageAndTextView frame];
|
|
||||||
|
|
||||||
imageAndTextViewFrame.origin.x = x;
|
|
||||||
x += CGRectGetWidth(imageAndTextViewFrame);
|
|
||||||
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame));
|
|
||||||
|
|
||||||
var hasSubmenuIndicator = [_menuItem hasSubmenu] && [_menuItem action];
|
|
||||||
|
|
||||||
if (hasSubmenuIndicator)
|
|
||||||
{
|
|
||||||
[_submenuIndicatorView setHidden:NO];
|
|
||||||
[_submenuIndicatorView setColor:[self textColor]];
|
|
||||||
[_submenuIndicatorView setShadowColor:[self textShadowColor]];
|
|
||||||
|
|
||||||
var submenuViewFrame = [_submenuIndicatorView frame];
|
|
||||||
|
|
||||||
submenuViewFrame.origin.x = x + SUBMENU_INDICATOR_MARGIN;
|
|
||||||
|
|
||||||
x = CGRectGetMaxX(submenuViewFrame);
|
|
||||||
height = MAX(height, CGRectGetHeight(submenuViewFrame));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[_submenuIndicatorView setHidden:YES];
|
|
||||||
|
|
||||||
height += 2.0 * VERTICAL_MARGIN;
|
|
||||||
|
|
||||||
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
|
||||||
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
|
||||||
|
|
||||||
if (hasSubmenuIndicator)
|
|
||||||
{
|
|
||||||
submenuViewFrame.origin.y = FLOOR((height - CGRectGetHeight(submenuViewFrame)) / 2.0) + 1.0;
|
|
||||||
[_submenuIndicatorView setFrame:submenuViewFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
_minSize = CGSizeMake(x + HORIZONTAL_MARGIN, height);
|
|
||||||
|
|
||||||
[self setAutoresizesSubviews:NO];
|
|
||||||
[self setFrameSize:_minSize];
|
|
||||||
[self setAutoresizesSubviews:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)highlight:(BOOL)shouldHighlight
|
|
||||||
{
|
|
||||||
// FIXME: This should probably be even throw.
|
|
||||||
if (![_menuItem isEnabled])
|
|
||||||
shouldHighlight = NO;
|
|
||||||
|
|
||||||
if (shouldHighlight)
|
|
||||||
{
|
|
||||||
if (![_menuItem _isMenuBarButton])
|
|
||||||
[self setBackgroundColor:SelectionColor];
|
|
||||||
|
|
||||||
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
|
||||||
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
|
||||||
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
|
||||||
|
|
||||||
[_submenuIndicatorView setColor:[CPColor whiteColor]];
|
|
||||||
[_submenuIndicatorView setShadowColor:[CPColor colorWithWhite:0.1 alpha:0.7]];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[self setBackgroundColor:nil];
|
|
||||||
|
|
||||||
[_imageAndTextView setImage:[_menuItem image]];
|
|
||||||
[_imageAndTextView setTextColor:[self textColor]];
|
|
||||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
|
||||||
|
|
||||||
[_submenuIndicatorView setColor:[self textColor]];
|
|
||||||
[_submenuIndicatorView setShadowColor:[self textShadowColor]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPMenuItemMenuBarSubmenuIndicatorView : CPView
|
|
||||||
{
|
|
||||||
CPColor _color;
|
|
||||||
CPColor _shadowColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_color === aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_color = aColor;
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_shadowColor === aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_shadowColor = aColor;
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
bounds = [self bounds];
|
|
||||||
|
|
||||||
bounds.size.height -= 1.0;
|
|
||||||
bounds.size.width -= 2.0;
|
|
||||||
bounds.origin.x += 1.0;
|
|
||||||
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
|
|
||||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
|
|
||||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMinY(bounds));
|
|
||||||
CGContextAddLineToPoint(context, CGRectGetMidX(bounds), CGRectGetMaxY(bounds));
|
|
||||||
|
|
||||||
CGContextClosePath(context);
|
|
||||||
|
|
||||||
CGContextSetShadowWithColor(context, CGSizeMake(0.0, 1.0), 1.1, _shadowColor || [CPColor whiteColor]);
|
|
||||||
CGContextSetFillColor(context, _color || [CPColor blackColor]);
|
|
||||||
CGContextFillPath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
|
|
||||||
@import "CPView.j"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPMenuItemSeparatorView : CPView
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)view
|
|
||||||
{
|
|
||||||
return [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 10.0)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
[self setAutoresizingMask:CPViewWidthSizable];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
bounds = [self bounds];
|
|
||||||
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
|
|
||||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
|
||||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, [CPColor lightGrayColor]);
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,306 +0,0 @@
|
|||||||
@import "CPControl.j"
|
|
||||||
|
|
||||||
|
|
||||||
var LEFT_MARGIN = 3.0,
|
|
||||||
RIGHT_MARGIN = 14.0 + 3.0,
|
|
||||||
STATE_COLUMN_WIDTH = 14.0,
|
|
||||||
INDENTATION_WIDTH = 17.0,
|
|
||||||
VERTICAL_MARGIN = 4.0,
|
|
||||||
|
|
||||||
RIGHT_COLUMNS_MARGIN = 30.0,
|
|
||||||
KEY_EQUIVALENT_MARGIN = 10.0;
|
|
||||||
|
|
||||||
var SUBMENU_INDICATOR_COLOR = nil,
|
|
||||||
_CPMenuItemSelectionColor = nil,
|
|
||||||
_CPMenuItemTextShadowColor = nil,
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages = [],
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
|
||||||
|
|
||||||
@implementation _CPMenuItemStandardView : CPView
|
|
||||||
{
|
|
||||||
CPMenuItem _menuItem @accessors(property=menuItem);
|
|
||||||
|
|
||||||
CPFont _font;
|
|
||||||
CPColor _textColor;
|
|
||||||
CPColor _textShadowColor;
|
|
||||||
|
|
||||||
CGSize _minSize @accessors(readonly, property=minSize);
|
|
||||||
BOOL _isDirty;
|
|
||||||
|
|
||||||
CPImageView _stateView;
|
|
||||||
_CPImageAndTextView _imageAndTextView;
|
|
||||||
_CPImageAndTextView _keyEquivalentView;
|
|
||||||
CPView _submenuIndicatorView;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self !== [_CPMenuItemStandardView class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
|
|
||||||
|
|
||||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
|
||||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
|
|
||||||
|
|
||||||
var bundle = [CPBundle bundleForClass:self];
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)view
|
|
||||||
{
|
|
||||||
return [[self alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (float)_standardLeftMargin
|
|
||||||
{
|
|
||||||
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
|
||||||
|
|
||||||
[_stateView setImageScaling:CPScaleNone];
|
|
||||||
|
|
||||||
[self addSubview:_stateView];
|
|
||||||
|
|
||||||
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
|
||||||
|
|
||||||
[_imageAndTextView setImagePosition:CPImageLeft];
|
|
||||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
|
||||||
|
|
||||||
[self addSubview:_imageAndTextView];
|
|
||||||
|
|
||||||
_keyEquivalentView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
|
||||||
|
|
||||||
[_keyEquivalentView setImagePosition:CPNoImage];
|
|
||||||
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
|
||||||
[_keyEquivalentView setAutoresizingMask:CPViewMinXMargin];
|
|
||||||
|
|
||||||
[self addSubview:_keyEquivalentView];
|
|
||||||
|
|
||||||
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
|
|
||||||
|
|
||||||
[_submenuIndicatorView setColor:SUBMENU_INDICATOR_COLOR];
|
|
||||||
[_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin];
|
|
||||||
|
|
||||||
[self addSubview:_submenuIndicatorView];
|
|
||||||
|
|
||||||
[self setAutoresizingMask:CPViewWidthSizable];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textColor
|
|
||||||
{
|
|
||||||
if (![_menuItem isEnabled])
|
|
||||||
return [CPColor lightGrayColor];
|
|
||||||
|
|
||||||
return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textShadowColor
|
|
||||||
{
|
|
||||||
if (![_menuItem isEnabled])
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
_font = aFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)update
|
|
||||||
{
|
|
||||||
var x = LEFT_MARGIN + [_menuItem indentationLevel] * INDENTATION_WIDTH,
|
|
||||||
height = 0.0,
|
|
||||||
hasStateColumn = [[_menuItem menu] showsStateColumn];
|
|
||||||
|
|
||||||
if (hasStateColumn)
|
|
||||||
{
|
|
||||||
[_stateView setHidden:NO];
|
|
||||||
[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
|
|
||||||
|
|
||||||
x += STATE_COLUMN_WIDTH;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[_stateView setHidden:YES];
|
|
||||||
|
|
||||||
[_imageAndTextView setFont:[_menuItem font] || _font];
|
|
||||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
|
||||||
[_imageAndTextView setImage:[_menuItem image]];
|
|
||||||
[_imageAndTextView setText:[_menuItem title]];
|
|
||||||
[_imageAndTextView setTextColor:[self textColor]];
|
|
||||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
|
||||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
|
||||||
[_imageAndTextView sizeToFit];
|
|
||||||
|
|
||||||
var imageAndTextViewFrame = [_imageAndTextView frame];
|
|
||||||
|
|
||||||
imageAndTextViewFrame.origin.x = x;
|
|
||||||
x += CGRectGetWidth(imageAndTextViewFrame);
|
|
||||||
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame));
|
|
||||||
|
|
||||||
var hasKeyEquivalent = !![_menuItem keyEquivalent],
|
|
||||||
hasSubmenu = [_menuItem hasSubmenu];
|
|
||||||
|
|
||||||
if (hasKeyEquivalent || hasSubmenu)
|
|
||||||
x += RIGHT_COLUMNS_MARGIN;
|
|
||||||
|
|
||||||
if (hasKeyEquivalent)
|
|
||||||
{
|
|
||||||
[_keyEquivalentView setFont:[_menuItem font] || _font];
|
|
||||||
[_keyEquivalentView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
|
||||||
[_keyEquivalentView setImage:[_menuItem image]];
|
|
||||||
[_keyEquivalentView setText:[_menuItem keyEquivalentStringRepresentation]];
|
|
||||||
[_keyEquivalentView setTextColor:[self textColor]];
|
|
||||||
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
|
||||||
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0, 1)];
|
|
||||||
[_keyEquivalentView setFrameOrigin:CGPointMake(x, VERTICAL_MARGIN)];
|
|
||||||
[_keyEquivalentView sizeToFit];
|
|
||||||
|
|
||||||
var keyEquivalentViewFrame = [_keyEquivalentView frame];
|
|
||||||
|
|
||||||
keyEquivalentViewFrame.origin.x = x;
|
|
||||||
x += CGRectGetWidth(keyEquivalentViewFrame);
|
|
||||||
height = MAX(height, CGRectGetHeight(keyEquivalentViewFrame));
|
|
||||||
|
|
||||||
if (hasSubmenu)
|
|
||||||
x += RIGHT_COLUMNS_MARGIN;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[_keyEquivalentView setHidden:YES];
|
|
||||||
|
|
||||||
if (hasSubmenu)
|
|
||||||
{
|
|
||||||
[_submenuIndicatorView setHidden:NO];
|
|
||||||
|
|
||||||
var submenuViewFrame = [_submenuIndicatorView frame];
|
|
||||||
|
|
||||||
submenuViewFrame.origin.x = x;
|
|
||||||
|
|
||||||
x += CGRectGetWidth(submenuViewFrame);
|
|
||||||
height = MAX(height, CGRectGetHeight(submenuViewFrame));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[_submenuIndicatorView setHidden:YES];
|
|
||||||
|
|
||||||
height += 2.0 * VERTICAL_MARGIN;
|
|
||||||
|
|
||||||
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
|
||||||
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
|
||||||
|
|
||||||
if (hasStateColumn)
|
|
||||||
[_stateView setFrameSize:CGSizeMake(STATE_COLUMN_WIDTH, height)];
|
|
||||||
|
|
||||||
if (hasKeyEquivalent)
|
|
||||||
{
|
|
||||||
keyEquivalentViewFrame.origin.y = FLOOR((height - CGRectGetHeight(keyEquivalentViewFrame)) / 2.0);
|
|
||||||
[_keyEquivalentView setFrame:keyEquivalentViewFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasSubmenu)
|
|
||||||
{
|
|
||||||
submenuViewFrame.origin.y = FLOOR((height - CGRectGetHeight(submenuViewFrame)) / 2.0);
|
|
||||||
[_submenuIndicatorView setFrame:submenuViewFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
_minSize = CGSizeMake(x + RIGHT_MARGIN, height);
|
|
||||||
|
|
||||||
[self setAutoresizesSubviews:NO];
|
|
||||||
[self setFrameSize:_minSize];
|
|
||||||
[self setAutoresizesSubviews:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)highlight:(BOOL)shouldHighlight
|
|
||||||
{
|
|
||||||
// FIXME: This should probably be even throw.
|
|
||||||
if (![_menuItem isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (shouldHighlight)
|
|
||||||
{
|
|
||||||
[self setBackgroundColor:_CPMenuItemSelectionColor];
|
|
||||||
|
|
||||||
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
|
||||||
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
|
||||||
[_keyEquivalentView setTextColor:[CPColor whiteColor]];
|
|
||||||
[_submenuIndicatorView setColor:[CPColor whiteColor]];
|
|
||||||
|
|
||||||
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
|
||||||
[_keyEquivalentView setTextShadowColor:_CPMenuItemTextShadowColor];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[self setBackgroundColor:nil];
|
|
||||||
|
|
||||||
[_imageAndTextView setImage:[_menuItem image]];
|
|
||||||
[_imageAndTextView setTextColor:[self textColor]];
|
|
||||||
[_keyEquivalentView setTextColor:[self textColor]];
|
|
||||||
[_submenuIndicatorView setColor:SUBMENU_INDICATOR_COLOR];
|
|
||||||
|
|
||||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
|
||||||
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([[_menuItem menu] showsStateColumn])
|
|
||||||
{
|
|
||||||
if (shouldHighlight)
|
|
||||||
[_stateView setImage:_CPMenuItemDefaultStateHighlightedImages[[_menuItem state]] || nil];
|
|
||||||
else
|
|
||||||
[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPMenuItemSubmenuIndicatorView : CPView
|
|
||||||
{
|
|
||||||
CPColor _color;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_color === aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_color = aColor;
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
bounds = [self bounds];
|
|
||||||
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
|
|
||||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
|
|
||||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMidY(bounds));
|
|
||||||
CGContextAddLineToPoint(context, CGRectGetMinX(bounds), CGRectGetMaxY(bounds));
|
|
||||||
|
|
||||||
CGContextClosePath(context);
|
|
||||||
|
|
||||||
CGContextSetFillColor(context, _color);
|
|
||||||
CGContextFillPath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
|
|
||||||
@import <AppKit/CPControl.j>
|
|
||||||
|
|
||||||
@import "_CPMenuItemSeparatorView.j"
|
|
||||||
@import "_CPMenuItemStandardView.j"
|
|
||||||
@import "_CPMenuItemMenuBarView.j"
|
|
||||||
|
|
||||||
|
|
||||||
var LEFT_MARGIN = 3.0,
|
|
||||||
RIGHT_MARGIN = 16.0,
|
|
||||||
STATE_COLUMN_WIDTH = 14.0,
|
|
||||||
INDENTATION_WIDTH = 17.0,
|
|
||||||
VERTICAL_MARGIN = 4.0;
|
|
||||||
|
|
||||||
var _CPMenuItemSelectionColor = nil,
|
|
||||||
_CPMenuItemTextShadowColor = nil,
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages = [],
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
|
||||||
|
|
||||||
/*
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
@implementation _CPMenuItemView : CPView
|
|
||||||
{
|
|
||||||
CPMenuItem _menuItem;
|
|
||||||
CPView _view;
|
|
||||||
|
|
||||||
CPFont _font;
|
|
||||||
CPColor _textColor;
|
|
||||||
CPColor _textShadowColor;
|
|
||||||
|
|
||||||
CGSize _minSize;
|
|
||||||
BOOL _isDirty;
|
|
||||||
BOOL _showsStateColumn;
|
|
||||||
|
|
||||||
CPImageView _stateView;
|
|
||||||
_CPImageAndTextView _imageAndTextView;
|
|
||||||
CPView _submenuView;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self != [_CPMenuItemView class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
|
||||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
|
|
||||||
|
|
||||||
var bundle = [CPBundle bundleForClass:self];
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
|
||||||
|
|
||||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
|
||||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (float)leftMargin
|
|
||||||
{
|
|
||||||
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_menuItem = aMenuItem;
|
|
||||||
_showsStateColumn = YES;
|
|
||||||
_isDirty = YES;
|
|
||||||
|
|
||||||
[self setAutoresizingMask:CPViewWidthSizable];
|
|
||||||
|
|
||||||
[self synchronizeWithMenuItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGSize)minSize
|
|
||||||
{
|
|
||||||
return _minSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDirty
|
|
||||||
{
|
|
||||||
_isDirty = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)synchronizeWithMenuItem
|
|
||||||
{
|
|
||||||
var menuItemView = [_menuItem view];
|
|
||||||
|
|
||||||
if ([_menuItem isSeparatorItem])
|
|
||||||
{
|
|
||||||
if (![_view isKindOfClass:[_CPMenuItemSeparatorView class]])
|
|
||||||
{
|
|
||||||
[_view removeFromSuperview];
|
|
||||||
_view = [_CPMenuItemSeparatorView view];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (menuItemView)
|
|
||||||
{
|
|
||||||
if (_view !== menuItemView)
|
|
||||||
{
|
|
||||||
[_view removeFromSuperview];
|
|
||||||
_view = menuItemView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ([_menuItem menu] == [CPApp mainMenu])
|
|
||||||
{
|
|
||||||
if (![_view isKindOfClass:[_CPMenuItemMenuBarView class]])
|
|
||||||
{
|
|
||||||
[_view removeFromSuperview];
|
|
||||||
_view = [_CPMenuItemMenuBarView view];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_view setMenuItem:_menuItem];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (![_view isKindOfClass:[_CPMenuItemStandardView class]])
|
|
||||||
{
|
|
||||||
[_view removeFromSuperview];
|
|
||||||
_view = [_CPMenuItemStandardView view];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_view setMenuItem:_menuItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([_view superview] !== self)
|
|
||||||
[self addSubview:_view];
|
|
||||||
|
|
||||||
if ([_view respondsToSelector:@selector(update)])
|
|
||||||
[_view update];
|
|
||||||
|
|
||||||
_minSize = [_view frame].size;
|
|
||||||
[self setAutoresizesSubviews:NO];
|
|
||||||
[self setFrameSize:_minSize];
|
|
||||||
[self setAutoresizesSubviews:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setShowsStateColumn:(BOOL)shouldShowStateColumn
|
|
||||||
{
|
|
||||||
_showsStateColumn = shouldShowStateColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)highlight:(BOOL)shouldHighlight
|
|
||||||
{
|
|
||||||
if ([_view respondsToSelector:@selector(highlight:)])
|
|
||||||
[_view highlight:shouldHighlight];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)eventOnSubmenu:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
if (![_menuItem hasSubmenu])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return CGRectContainsPoint([_submenuView frame], [self convertPoint:[anEvent locationInWindow] fromView:nil]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isHidden
|
|
||||||
{
|
|
||||||
return [_menuItem isHidden];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPMenuItem)menuItem
|
|
||||||
{
|
|
||||||
return _menuItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
if (_font === aFont)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_font = aFont;
|
|
||||||
|
|
||||||
if ([_view respondsToSelector:@selector(setFont:)])
|
|
||||||
[_view setFont:aFont];
|
|
||||||
|
|
||||||
[self setDirty];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTextColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_textColor == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_textColor = aColor;
|
|
||||||
|
|
||||||
[_imageAndTextView setTextColor:[self textColor]];
|
|
||||||
[_submenuView setColor:[self textColor]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textColor
|
|
||||||
{
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTextShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_textShadowColor == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_textShadowColor = aColor;
|
|
||||||
|
|
||||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
|
||||||
//[_submenuView setColor:[self textColor]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textShadowColor
|
|
||||||
{
|
|
||||||
return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPMenuItemArrowView : CPView
|
|
||||||
{
|
|
||||||
CPColor _color;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if (_color == aColor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_color = aColor;
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
|
|
||||||
CGContextMoveToPoint(context, 1.0, 4.0);
|
|
||||||
CGContextAddLineToPoint(context, 9.0, 4.0);
|
|
||||||
CGContextAddLineToPoint(context, 5.0, 8.0);
|
|
||||||
CGContextAddLineToPoint(context, 1.0, 4.0);
|
|
||||||
|
|
||||||
CGContextClosePath(context);
|
|
||||||
|
|
||||||
CGContextSetFillColor(context, _color);
|
|
||||||
CGContextFillPath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,625 +0,0 @@
|
|||||||
|
|
||||||
@import <Foundation/CPDictionary.j>
|
|
||||||
@import <Foundation/CPCountedSet.j>
|
|
||||||
|
|
||||||
@import "CPController.j"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPObjectController : CPController
|
|
||||||
{
|
|
||||||
id _contentObject;
|
|
||||||
id _selection;
|
|
||||||
|
|
||||||
Class _objectClass;
|
|
||||||
|
|
||||||
BOOL _isEditable;
|
|
||||||
BOOL _automaticallyPreparesContent;
|
|
||||||
|
|
||||||
CPCountedSet _observedKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)initialize
|
|
||||||
{
|
|
||||||
[self exposeBinding:@"editable"];
|
|
||||||
[self exposeBinding:@"contentObject"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingContentObject
|
|
||||||
{
|
|
||||||
return [CPSet setWithObjects:"content"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
|
|
||||||
{
|
|
||||||
if (aKey === @"contentObject")
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingCanAdd
|
|
||||||
{
|
|
||||||
return [CPSet setWithObject:"editable"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingCanInsert
|
|
||||||
{
|
|
||||||
return [CPSet setWithObject:"editable"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingCanRemove
|
|
||||||
{
|
|
||||||
return [CPSet setWithObjects:"editable", "selection"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
return [self initWithContent:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithContent:(id)aContent
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
[self setContent:aContent];
|
|
||||||
[self setEditable:YES];
|
|
||||||
[self setObjectClass:[CPMutableDictionary class]];
|
|
||||||
|
|
||||||
_observedKeys = [[CPCountedSet alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)content
|
|
||||||
{
|
|
||||||
return _contentObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setContent:(id)aContent
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"contentObject"];
|
|
||||||
[self _selectionWillChange];
|
|
||||||
|
|
||||||
_contentObject = aContent;
|
|
||||||
|
|
||||||
[self didChangeValueForKey:@"contentObject"];
|
|
||||||
[self _selectionDidChange];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setContentObject:(id)aContent
|
|
||||||
{
|
|
||||||
[self setContent:aContent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_contentObject
|
|
||||||
{
|
|
||||||
return [self content];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAutomaticallyPreparesContent:(BOOL)shouldAutomaticallyPrepareContent
|
|
||||||
{
|
|
||||||
_automaticallyPreparesContent = shouldAutomaticallyPrepareContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)automaticallyPreparesContent
|
|
||||||
{
|
|
||||||
return _automaticallyPreparesContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)prepareContent
|
|
||||||
{
|
|
||||||
[self setContent:[self newObject]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setObjectClass:(Class)aClass
|
|
||||||
{
|
|
||||||
_objectClass = aClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (Class)objectClass
|
|
||||||
{
|
|
||||||
return _objectClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)newObject
|
|
||||||
{
|
|
||||||
return [[[self objectClass] alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addObject:(id)anObject
|
|
||||||
{
|
|
||||||
[self setContent:anObject];
|
|
||||||
|
|
||||||
[[CPKeyValueBinding getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObject:(id)anObject
|
|
||||||
{
|
|
||||||
if ([self content] === anObject)
|
|
||||||
[self setContent:nil];
|
|
||||||
|
|
||||||
[[CPKeyValueBinding getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)add:(id)aSender
|
|
||||||
{
|
|
||||||
// FIXME: This should happen on the next run loop?
|
|
||||||
[self addObject:[self newObject]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canAdd
|
|
||||||
{
|
|
||||||
return [self isEditable];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)remove:(id)aSender
|
|
||||||
{
|
|
||||||
// FIXME: This should happen on the next run loop?
|
|
||||||
[self removeObject:[self content]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canRemove
|
|
||||||
{
|
|
||||||
return [self isEditable] && [[self selectedObjects] count];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setEditable:(BOOL)shouldBeEditable
|
|
||||||
{
|
|
||||||
_isEditable = shouldBeEditable;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEditable
|
|
||||||
{
|
|
||||||
return _isEditable;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)selectedObjects
|
|
||||||
{
|
|
||||||
return [[_CPObservableArray alloc] initWithObjects:[_contentObject] count:1];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)selection
|
|
||||||
{
|
|
||||||
return _selection;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_selectionWillChange
|
|
||||||
{
|
|
||||||
[_selection controllerWillChange];
|
|
||||||
[self willChangeValueForKey:@"selection"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_selectionDidChange
|
|
||||||
{
|
|
||||||
if (_selection === undefined || _selection === nil)
|
|
||||||
_selection = [[CPControllerSelectionProxy alloc] initWithController:self];
|
|
||||||
|
|
||||||
[_selection controllerDidChange];
|
|
||||||
[self didChangeValueForKey:@"selection"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)observedKeys
|
|
||||||
{
|
|
||||||
return _observedKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
|
|
||||||
{
|
|
||||||
[_observedKeys addObject:aKeyPath];
|
|
||||||
[super addObserver:anObserver forKeyPath:aKeyPath options:options context:context];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
|
|
||||||
{
|
|
||||||
[_observedKeys removeObject:aKeyPath];
|
|
||||||
[super removeObserver:anObserver forKeyPath:aKeyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
|
|
||||||
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
|
|
||||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
|
|
||||||
|
|
||||||
@implementation CPObjectController (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
|
|
||||||
objectClass = CPClassFromString(objectClassName);
|
|
||||||
|
|
||||||
// FIXME: Error if objectClass === nil
|
|
||||||
|
|
||||||
[self setObjectClass:objectClass];
|
|
||||||
[self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
|
|
||||||
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey] || NO];
|
|
||||||
|
|
||||||
_observedKeys = [[CPCountedSet alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[aCoder encodeObject:CPStringFromClass(objectClass) forKey:CPObjectControllerObjectClassNameKey];
|
|
||||||
[aCoder encodeObject:[self isEditable] forKey:CPObjectControllerIsEditableKey];
|
|
||||||
|
|
||||||
if (![self automaticallyPreparesContent])
|
|
||||||
[aCoder encodeBOOL:YES forKey:CPObjectControllerAutomaticallyPreparesContentKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPObservationProxy : CPObject
|
|
||||||
{
|
|
||||||
id _keyPath;
|
|
||||||
id _observer;
|
|
||||||
id _object;
|
|
||||||
|
|
||||||
BOOL _notifyObject;
|
|
||||||
|
|
||||||
id _context;
|
|
||||||
int _options;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithKeyPath:(id)aKeyPath observer:(id)anObserver object:(id)anObject
|
|
||||||
{
|
|
||||||
if (self=[super init])
|
|
||||||
{
|
|
||||||
_keyPath = aKeyPath;
|
|
||||||
_observer = anObserver;
|
|
||||||
_object = anObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)observer
|
|
||||||
{
|
|
||||||
return _observer;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)keyPath
|
|
||||||
{
|
|
||||||
return _keyPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)context
|
|
||||||
{
|
|
||||||
return _context;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)options
|
|
||||||
{
|
|
||||||
return _options;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setNotifyObject:(BOOL)notify
|
|
||||||
{
|
|
||||||
_notifyObject = notify;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)anObject
|
|
||||||
{
|
|
||||||
if ([anObject class] === [self class])
|
|
||||||
{
|
|
||||||
if (anObject._observer === _observer && [anObject._keyPath isEqual:_keyPath] && [anObject._object isEqual:_object])
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)change context:(id)context
|
|
||||||
{
|
|
||||||
if (_notifyObject)
|
|
||||||
[_object observeValueForKeyPath:_keyPath ofObject:_object change:change context:context];
|
|
||||||
|
|
||||||
[_observer observeValueForKeyPath:_keyPath ofObject:_object change:change context:context];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
return [super description] + [CPString stringWithFormat:@"observation proxy for %@ on key path %@", _observer, _keyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPObservableArray : CPMutableArray
|
|
||||||
{
|
|
||||||
CPArray _observationProxies;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)alloc
|
|
||||||
{
|
|
||||||
var a = [];
|
|
||||||
a.isa = self;
|
|
||||||
|
|
||||||
var ivars = class_copyIvarList(self),
|
|
||||||
count = ivars.length;
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
a[ivar_getName(ivars[count])] = nil;
|
|
||||||
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
return "<_CPObservableArray: "+[super description]+" >";
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithArray:(CPArray)anArray
|
|
||||||
{
|
|
||||||
if (self = [super initWithArray:anArray])
|
|
||||||
{
|
|
||||||
_observationProxies = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithObjects:(CPArray)objects count:(unsigned)count
|
|
||||||
{
|
|
||||||
if (self = [super initWithObjects:objects count:count])
|
|
||||||
{
|
|
||||||
_observationProxies = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
|
|
||||||
{
|
|
||||||
if (aKeyPath.indexOf("@") === 0)
|
|
||||||
{
|
|
||||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self];
|
|
||||||
|
|
||||||
proxy._options = options;
|
|
||||||
proxy._context = context;
|
|
||||||
|
|
||||||
[_observationProxies addObject:proxy];
|
|
||||||
|
|
||||||
var dotIndex = aKeyPath.indexOf("."),
|
|
||||||
remaining = aKeyPath.substring(dotIndex+1),
|
|
||||||
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
|
||||||
|
|
||||||
[self addObserver:proxy toObjectsAtIndexes:indexes forKeyPath:remaining options:options context:context];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
|
||||||
[self addObserver:anObserver toObjectsAtIndexes:indexes forKeyPath:aKeyPath options:options context:context];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
|
|
||||||
{
|
|
||||||
if (aKeyPath.indexOf("@") === 0)
|
|
||||||
{
|
|
||||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self],
|
|
||||||
index = [_observationProxies indexOfObject:proxy];
|
|
||||||
|
|
||||||
proxy = [_observationProxies objectAtIndex:index];
|
|
||||||
|
|
||||||
var dotIndex = aKeyPath.indexOf("."),
|
|
||||||
remaining = aKeyPath.substring(dotIndex+1),
|
|
||||||
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
|
||||||
|
|
||||||
[self removeObserver:proxy fromObjectsAtIndexes:indexes forKeyPath:remaining];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
|
||||||
[self removeObserver:anObserver fromObjectsAtIndexes:indexes forKeyPath:aKeyPath];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertObject:(id)anObject atIndex:(unsigned)anIndex
|
|
||||||
{
|
|
||||||
for (var i=0, count=[_observationProxies count]; i<count; i++)
|
|
||||||
{
|
|
||||||
var proxy = [_observationProxies objectAtIndex:i],
|
|
||||||
keyPath = [proxy keyPath],
|
|
||||||
operator = keyPath.indexOf(".") === 0;
|
|
||||||
|
|
||||||
if (operator)
|
|
||||||
[self willChangeValueForKey:keyPath];
|
|
||||||
|
|
||||||
[anObject addObserver:proxy forKeyPath:keyPath options:[proxy options] context:[proxy context]];
|
|
||||||
|
|
||||||
if (operator)
|
|
||||||
[self didChangeValueForKey:keyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
[super insertObject:anObject atIndex:anIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObjectAtIndex:(unsigned)anIndex
|
|
||||||
{
|
|
||||||
for (var i=0, count=[_observationProxies count]; i<count; i++)
|
|
||||||
{
|
|
||||||
var proxy = [_observationProxies objectAtIndex:i],
|
|
||||||
keyPath = [proxy keyPath],
|
|
||||||
operator = keyPath.indexOf(".") === 0;
|
|
||||||
|
|
||||||
if (operator)
|
|
||||||
[self willChangeValueForKey:keyPath];
|
|
||||||
|
|
||||||
[anObject removeObserver:proxy forKeyPath:keyPath];
|
|
||||||
|
|
||||||
if (operator)
|
|
||||||
[self didChangeValueForKey:keyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
[super removeObjectAtIndex:anIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (_CPObservableArray)objectsAtIndexes:(CPIndexSet)theIndexes
|
|
||||||
{
|
|
||||||
return [_CPObservableArray arrayWithArray:[super objectsAtIndexes:theIndexes]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addObject:(id)anObject
|
|
||||||
{
|
|
||||||
[self insertObject:anObject atIndex:[self count]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeLastObject
|
|
||||||
{
|
|
||||||
[self removeObjectAtIndex:[self count]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)replaceObjectAtIndex:(unsigned)anIndex withObject:(id)anObject
|
|
||||||
{
|
|
||||||
var currentObject = [self objectAtIndex:anIndex];
|
|
||||||
|
|
||||||
for (var i=0, count=[_observationProxies count]; i<count; i++)
|
|
||||||
{
|
|
||||||
var proxy = [_observationProxies objectAtIndex:i],
|
|
||||||
keyPath = [proxy keyPath],
|
|
||||||
operator = keyPath.indexOf(".") === 0;
|
|
||||||
|
|
||||||
if (operator)
|
|
||||||
[self willChangeValueForKey:keyPath];
|
|
||||||
|
|
||||||
[currentObject removeObserver:proxy forKeyPath:keyPath];
|
|
||||||
[anObject addObserver:proxy forKeyPath:keyPath options:[proxy options] context:[proxy context]];
|
|
||||||
|
|
||||||
if (operator)
|
|
||||||
[self didChangeValueForKey:keyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
[super replaceObjectAtIndex:anIndex withObject:anObject];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPControllerSelectionProxy : CPObject
|
|
||||||
{
|
|
||||||
id _controller;
|
|
||||||
id _keys;
|
|
||||||
|
|
||||||
CPDictionary _cachedValues;
|
|
||||||
CPArray _observationProxies;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithController:(id)aController
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_cachedValues = [CPDictionary dictionary];
|
|
||||||
_observationProxies = [CPArray array];
|
|
||||||
_controller = aController;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForKey:(CPString)aKey
|
|
||||||
{
|
|
||||||
var value = [_cachedValues objectForKey:aKey];
|
|
||||||
|
|
||||||
if (value !== undefined && value !== nil)
|
|
||||||
return value;
|
|
||||||
|
|
||||||
var allValues = [[_controller selectedObjects] valueForKeyPath:aKey],
|
|
||||||
count = [allValues count];
|
|
||||||
|
|
||||||
if (!count)
|
|
||||||
value = CPNoSelectionMarker;
|
|
||||||
else if (count === 1)
|
|
||||||
value = [allValues objectAtIndex:0];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ([_controller alwaysUsesMultipleValuesMarker])
|
|
||||||
value = CPMultipleValuesMarker;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
value = [allValues objectAtIndex:0];
|
|
||||||
|
|
||||||
for (var i=0, count=[allValues count]; i<count && value!=CPMultipleValuesMarker; i++)
|
|
||||||
{
|
|
||||||
if (![value isEqual:[allValues objectAtIndex:i]])
|
|
||||||
value = CPMultipleValuesMarker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[_cachedValues setValue:value forKey:aKey];
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)count
|
|
||||||
{
|
|
||||||
return [_cachedValues count];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)keyEnumerator
|
|
||||||
{
|
|
||||||
return [_cachedValues keyEnumerator];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forKey:(CPString)aKey
|
|
||||||
{
|
|
||||||
[[_controller selectedObjects] setValue:aValue forKey:aKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)controllerWillChange
|
|
||||||
{
|
|
||||||
_keys = [_cachedValues allKeys];
|
|
||||||
|
|
||||||
if (!_keys)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (var i=0, count=_keys.length; i<count; i++)
|
|
||||||
[self willChangeValueForKey:_keys[i]];
|
|
||||||
|
|
||||||
[_cachedValues removeAllObjects];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)controllerDidChange
|
|
||||||
{
|
|
||||||
[_cachedValues removeAllObjects];
|
|
||||||
|
|
||||||
if (!_keys)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (var i=0, count=_keys.length; i<count; i++)
|
|
||||||
[self didChangeValueForKey:_keys[i]];
|
|
||||||
|
|
||||||
_keys = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)change context:(id)context
|
|
||||||
{
|
|
||||||
[_cachedValues removeObjectForKey:aKeyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addObserver:(id)anObject forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
|
|
||||||
{
|
|
||||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObject object:self];
|
|
||||||
|
|
||||||
[proxy setNotifyObject:YES];
|
|
||||||
[_observationProxies addObject:proxy];
|
|
||||||
|
|
||||||
[[_controller selectedObjects] addObserver:proxy forKeyPath:aKeyPath options:options context:context];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObserver:(id)anObject forKeyPath:(CPString)aKeyPath
|
|
||||||
{
|
|
||||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObject object:self],
|
|
||||||
index = [_observationProxies indexOfObject:proxy];
|
|
||||||
|
|
||||||
[[_controller selectedObjects] removeObserver:[_observationProxies objectAtIndex:index] forKeyPath:aKeyPath];
|
|
||||||
[_observationProxies removeObjectAtIndex:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
@import "CPPanel.j"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPOpenPanel : CPPanel
|
|
||||||
{
|
|
||||||
BOOL _canChooseFiles @accessors(property=canChooseFiles);
|
|
||||||
BOOL _canChooseDirectories @accessors(property=canChooseDirectories);
|
|
||||||
BOOL _allowsMultipleSelection @accessors(property=allowsMultipleSelection);
|
|
||||||
CPURL _directoryURL @accessors(property=directoryURL);
|
|
||||||
CPArray _URLs;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)openPanel
|
|
||||||
{
|
|
||||||
return [[CPOpenPanel alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPInteger)runModal
|
|
||||||
{
|
|
||||||
if (typeof window["cpOpenPanel"] === "function")
|
|
||||||
{
|
|
||||||
// FIXME: Is this correct???
|
|
||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
|
||||||
|
|
||||||
var options = { directoryURL: [self directoryURL],
|
|
||||||
canChooseFiles: [self canChooseFiles],
|
|
||||||
canChooseDirectories: [self canChooseDirectories],
|
|
||||||
allowsMultipleSelection: [self allowsMultipleSelection] };
|
|
||||||
|
|
||||||
var result = window.cpOpenPanel(options);
|
|
||||||
|
|
||||||
_URLs = result.URLs;
|
|
||||||
|
|
||||||
return result.button;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw "-runModal is unimplemented.";
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)URLs
|
|
||||||
{
|
|
||||||
return _URLs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+57
-1279
File diff suppressed because it is too large
Load Diff
+6
-13
@@ -46,13 +46,6 @@ CPCancelButton = 0;
|
|||||||
<li>A panel can be made to work even when there's an attention panel on-screen. This permits actions within the panel to affect the attention panel.</li>
|
<li>A panel can be made to work even when there's an attention panel on-screen. This permits actions within the panel to affect the attention panel.</li>
|
||||||
</ul>
|
</ul>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
@global
|
|
||||||
@class CPWindow
|
|
||||||
*/
|
|
||||||
CPDocModalWindowMask = 1 << 6;
|
|
||||||
|
|
||||||
@implementation CPPanel : CPWindow
|
@implementation CPPanel : CPWindow
|
||||||
{
|
{
|
||||||
BOOL _becomesKeyOnlyIfNeeded;
|
BOOL _becomesKeyOnlyIfNeeded;
|
||||||
@@ -60,7 +53,7 @@ CPDocModalWindowMask = 1 << 6;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the receiver is a floating panel (like a palette).
|
Returns <code>YES</code> if the receiver is a floating panel (like a palette).
|
||||||
*/
|
*/
|
||||||
- (BOOL)isFloatingPanel
|
- (BOOL)isFloatingPanel
|
||||||
{
|
{
|
||||||
@@ -68,8 +61,8 @@ CPDocModalWindowMask = 1 << 6;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver to be a floating panel. \c YES
|
Sets the receiver to be a floating panel. <code>YES</code>
|
||||||
makes the window a floating panel. \c NO makes it a normal window.
|
makes the window a floating panel. <code>NO</code> makes it a normal window.
|
||||||
@param isFloatingPanel specifies whether to make it floating
|
@param isFloatingPanel specifies whether to make it floating
|
||||||
*/
|
*/
|
||||||
- (void)setFloatingPanel:(BOOL)isFloatingPanel
|
- (void)setFloatingPanel:(BOOL)isFloatingPanel
|
||||||
@@ -78,8 +71,8 @@ CPDocModalWindowMask = 1 << 6;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the window only becomes key
|
Returns <code>YES</code> if the window only becomes key
|
||||||
if needed. \c NO means it behaves just like other windows.
|
if needed. <code>NO</code> means it behaves just like other windows.
|
||||||
*/
|
*/
|
||||||
- (BOOL)becomesKeyOnlyIfNeeded
|
- (BOOL)becomesKeyOnlyIfNeeded
|
||||||
{
|
{
|
||||||
@@ -88,7 +81,7 @@ CPDocModalWindowMask = 1 << 6;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets whether the the window becomes key only if needed.
|
Sets whether the the window becomes key only if needed.
|
||||||
@param shouldBecomeKeyOnlyIfNeeded \c YES makes the window become key only if needed
|
@param shouldBecomeKeyOnlyIfNeeded <code>YES</code> makes the window become key only if needed
|
||||||
*/
|
*/
|
||||||
- (void)setBecomesKeyOnlyIfNeeded:(BOOL)shouldBecomeKeyOnlyIfNeeded
|
- (void)setBecomesKeyOnlyIfNeeded:(BOOL)shouldBecomeKeyOnlyIfNeeded
|
||||||
{
|
{
|
||||||
|
|||||||
+20
-160
@@ -41,14 +41,10 @@ CPURLPboardType = @"CPURLPboardType";
|
|||||||
CPImagesPboardType = @"CPImagesPboardType";
|
CPImagesPboardType = @"CPImagesPboardType";
|
||||||
CPVideosPboardType = @"CPVideosPboardType";
|
CPVideosPboardType = @"CPVideosPboardType";
|
||||||
|
|
||||||
UTF8PboardType = @"public.utf8-plain-text";
|
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
CPImagePboardType = @"CPImagePboardType";
|
CPImagePboardType = @"CPImagePboardType";
|
||||||
|
|
||||||
|
var CPPasteboards = nil;
|
||||||
var CPPasteboards = nil,
|
|
||||||
supportsNativePasteboard = NO;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -64,8 +60,6 @@ var CPPasteboards = nil,
|
|||||||
|
|
||||||
unsigned _changeCount;
|
unsigned _changeCount;
|
||||||
CPString _stateUID;
|
CPString _stateUID;
|
||||||
|
|
||||||
WebScriptObject _nativePasteboard;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -79,9 +73,6 @@ var CPPasteboards = nil,
|
|||||||
[self setVersion:1.0];
|
[self setVersion:1.0];
|
||||||
|
|
||||||
CPPasteboards = [CPDictionary dictionary];
|
CPPasteboards = [CPDictionary dictionary];
|
||||||
|
|
||||||
if (typeof window.cpPasteboardWithName !== "undefined")
|
|
||||||
supportsNativePasteboard = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -100,7 +91,7 @@ var CPPasteboards = nil,
|
|||||||
+ (id)pasteboardWithName:(CPString)aName
|
+ (id)pasteboardWithName:(CPString)aName
|
||||||
{
|
{
|
||||||
var pasteboard = [CPPasteboards objectForKey:aName];
|
var pasteboard = [CPPasteboards objectForKey:aName];
|
||||||
|
|
||||||
if (pasteboard)
|
if (pasteboard)
|
||||||
return pasteboard;
|
return pasteboard;
|
||||||
|
|
||||||
@@ -124,12 +115,6 @@ var CPPasteboards = nil,
|
|||||||
_provided = [CPDictionary dictionary];
|
_provided = [CPDictionary dictionary];
|
||||||
|
|
||||||
_changeCount = 0;
|
_changeCount = 0;
|
||||||
|
|
||||||
if (supportsNativePasteboard)
|
|
||||||
{
|
|
||||||
_nativePasteboard = window.cpPasteboardWithName(aName);
|
|
||||||
[self _synchronizePasteboard];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -158,16 +143,7 @@ var CPPasteboards = nil,
|
|||||||
|
|
||||||
[_owners setObject:anOwner forKey:type];
|
[_owners setObject:anOwner forKey:type];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_nativePasteboard)
|
|
||||||
{
|
|
||||||
var nativeTypes = [types copy];
|
|
||||||
if ([types containsObject:CPStringPboardType])
|
|
||||||
nativeTypes.push(UTF8PboardType);
|
|
||||||
|
|
||||||
_nativePasteboard.addTypes_(nativeTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ++_changeCount;
|
return ++_changeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,12 +154,6 @@ var CPPasteboards = nil,
|
|||||||
@return the pasteboard's change count
|
@return the pasteboard's change count
|
||||||
*/
|
*/
|
||||||
- (unsigned)declareTypes:(CPArray)types owner:(id)anOwner
|
- (unsigned)declareTypes:(CPArray)types owner:(id)anOwner
|
||||||
{
|
|
||||||
[self _declareTypes:types owner:anOwner updateNativePasteboard:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
- (unsigned)_declareTypes:(CPArray)types owner:(id)anOwner updateNativePasteboard:(BOOL)shouldUpdate
|
|
||||||
{
|
{
|
||||||
[_types setArray:types];
|
[_types setArray:types];
|
||||||
|
|
||||||
@@ -194,16 +164,7 @@ var CPPasteboards = nil,
|
|||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
[_owners setObject:anOwner forKey:_types[count]];
|
[_owners setObject:anOwner forKey:_types[count]];
|
||||||
|
|
||||||
if (_nativePasteboard && shouldUpdate)
|
|
||||||
{
|
|
||||||
var nativeTypes = [types copy];
|
|
||||||
if ([types containsObject:CPStringPboardType])
|
|
||||||
nativeTypes.push(UTF8PboardType);
|
|
||||||
|
|
||||||
_nativePasteboard.declareTypes_(nativeTypes);
|
|
||||||
_changeCount = _nativePasteboard.changeCount();
|
|
||||||
}
|
|
||||||
return ++_changeCount;
|
return ++_changeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,15 +172,12 @@ var CPPasteboards = nil,
|
|||||||
Sets the pasteboard data for the specified type
|
Sets the pasteboard data for the specified type
|
||||||
@param aData the data
|
@param aData the data
|
||||||
@param aType the data type being set
|
@param aType the data type being set
|
||||||
@return \c YES if the data was successfully written to the pasteboard
|
@return <code>YES</code> if the data was successfully written to the pasteboard
|
||||||
*/
|
*/
|
||||||
- (BOOL)setData:(CPData)aData forType:(CPString)aType
|
- (BOOL)setData:(CPData)aData forType:(CPString)aType
|
||||||
{
|
{
|
||||||
[_provided setObject:aData forKey:aType];
|
[_provided setObject:aData forKey:aType];
|
||||||
|
|
||||||
if (aType === CPStringPboardType)
|
|
||||||
[self setData:aData forType:UTF8PboardType];
|
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,22 +185,22 @@ var CPPasteboards = nil,
|
|||||||
Writes the specified property list as data for the specified type
|
Writes the specified property list as data for the specified type
|
||||||
@param aPropertyList the property list to write
|
@param aPropertyList the property list to write
|
||||||
@param aType the data type
|
@param aType the data type
|
||||||
@return \c YES if the property list was successfully written to the pasteboard
|
@return <code>YES</code> if the property list was successfully written to the pasteboard
|
||||||
*/
|
*/
|
||||||
- (BOOL)setPropertyList:(id)aPropertyList forType:(CPString)aType
|
- (BOOL)setPropertyList:(id)aPropertyList forType:(CPString)aType
|
||||||
{
|
{
|
||||||
return [self setData:[CPPropertyListSerialization dataFromPropertyList:aPropertyList format:CPPropertyList280NorthFormat_v1_0] forType:aType];
|
return [self setData:[CPPropertyListSerialization dataFromPropertyList:aPropertyList format:CPPropertyListXMLFormat_v1_0 errorDescription:nil] forType:aType];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the specified string as data for the specified type
|
Sets the specified string as data for the specified type
|
||||||
@param aString the string to write
|
@param aString the string to write
|
||||||
@param aType the data type
|
@param aType the data type
|
||||||
@return \c YES if the string was successfully written to the pasteboard
|
@return <code>YES</code> if the string was successfully written to the pasteboard
|
||||||
*/
|
*/
|
||||||
- (void)setString:(CPString)aString forType:(CPString)aType
|
- (void)setString:(CPString)aString forType:(CPString)aType
|
||||||
{
|
{
|
||||||
[self setPropertyList:aString forType:aType];
|
return [self setPropertyList:aString forType:aType];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determining Types
|
// Determining Types
|
||||||
@@ -250,11 +208,11 @@ var CPPasteboards = nil,
|
|||||||
Checks the pasteboard's types for a match with the types listen in the specified array. The array should
|
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.
|
be ordered by the requestor's most preferred data type first.
|
||||||
@param anArray an array of requested types ordered by preference
|
@param anArray an array of requested types ordered by preference
|
||||||
@return the highest match with the pasteboard's supported types or \c nil if no match was found
|
@return the highest match with the pasteboard's supported types or <code>nil</code> if no match was found
|
||||||
*/
|
*/
|
||||||
- (CPString)availableTypeFromArray:(CPArray)anArray
|
- (CPString)availableTypeFromArray:(CPArray)anArray
|
||||||
{
|
{
|
||||||
return [[self types] firstObjectCommonWithArray:anArray];
|
return [_types firstObjectCommonWithArray:anArray];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -262,7 +220,6 @@ var CPPasteboards = nil,
|
|||||||
*/
|
*/
|
||||||
- (CPArray)types
|
- (CPArray)types
|
||||||
{
|
{
|
||||||
[self _synchronizePasteboard];
|
|
||||||
return _types;
|
return _types;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +235,7 @@ var CPPasteboards = nil,
|
|||||||
/*!
|
/*!
|
||||||
Returns the pasteboard data for the specified data type
|
Returns the pasteboard data for the specified data type
|
||||||
@param aType the requested data type
|
@param aType the requested data type
|
||||||
@return the requested data or \c nil if the data doesn't exist
|
@return the requested data or <code>nil</code> if the data doesn't exist
|
||||||
*/
|
*/
|
||||||
- (CPData)dataForType:(CPString)aType
|
- (CPData)dataForType:(CPString)aType
|
||||||
{
|
{
|
||||||
@@ -291,57 +248,27 @@ var CPPasteboards = nil,
|
|||||||
|
|
||||||
if (owner)
|
if (owner)
|
||||||
{
|
{
|
||||||
[owner pasteboard:self provideDataForType:aType];
|
[owner pasteboard:self provideDataForType:aType];
|
||||||
|
|
||||||
|
++_changeCount;
|
||||||
|
|
||||||
return [_provided objectForKey:aType];
|
return [_provided objectForKey:aType];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aType === CPStringPboardType)
|
|
||||||
return [self dataForType:UTF8PboardType];
|
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_synchronizePasteboard
|
|
||||||
{
|
|
||||||
if (_nativePasteboard && _nativePasteboard.changeCount() > _changeCount)
|
|
||||||
{
|
|
||||||
var nativeTypes = [_nativePasteboard.types() copy];
|
|
||||||
if ([nativeTypes containsObject:UTF8PboardType])
|
|
||||||
nativeTypes.push(CPStringPboardType);
|
|
||||||
|
|
||||||
[self _declareTypes:nativeTypes owner:self updateNativePasteboard:NO];
|
|
||||||
|
|
||||||
_changeCount = _nativePasteboard.changeCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @ignore
|
|
||||||
method provided for integration with native pasteboard
|
|
||||||
*/
|
|
||||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
|
||||||
{
|
|
||||||
if (aType === CPStringPboardType)
|
|
||||||
{
|
|
||||||
var string = _nativePasteboard.stringForType_(UTF8PboardType);
|
|
||||||
|
|
||||||
[self setString:string forType:CPStringPboardType];
|
|
||||||
[self setString:string forType:UTF8PboardType];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[self setString:_nativePasteboard.stringForType_(aType) forType:aType];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the property list for the specified data type
|
Returns the property list for the specified data type
|
||||||
@param aType the requested data type
|
@param aType the requested data type
|
||||||
@return the property list or \c nil if the list was not found
|
@return the property list or <code>nil</code> if the list was not found
|
||||||
*/
|
*/
|
||||||
- (id)propertyListForType:(CPString)aType
|
- (id)propertyListForType:(CPString)aType
|
||||||
{
|
{
|
||||||
var data = [self dataForType:aType];
|
var data = [self dataForType:aType];
|
||||||
|
|
||||||
if (data)
|
if (data)
|
||||||
return [CPPropertyListSerialization propertyListFromData:data format:CPPropertyList280NorthFormat_v1_0];
|
return [CPPropertyListSerialization propertyListFromData:data format:CPPropertyListXMLFormat_v1_0 errorDescription:nil];
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@@ -349,7 +276,7 @@ var CPPasteboards = nil,
|
|||||||
/*!
|
/*!
|
||||||
Returns the string for the specified data type
|
Returns the string for the specified data type
|
||||||
@param aType the requested data type
|
@param aType the requested data type
|
||||||
@return the string or \c nil if the string was not found
|
@return the string or <code>nil</code> if the string was not found
|
||||||
*/
|
*/
|
||||||
- (CPString)stringForType:(CPString)aType
|
- (CPString)stringForType:(CPString)aType
|
||||||
{
|
{
|
||||||
@@ -376,70 +303,3 @@ var CPPasteboards = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@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] rawString]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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];
|
|
||||||
|
|
||||||
return [CPData dataWithRawString:dataString];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)propertyListForType:(CPString)aType
|
|
||||||
{
|
|
||||||
if (aType === CPStringPboardType)
|
|
||||||
return _dataTransfer.getData(aType);
|
|
||||||
|
|
||||||
return [CPPropertyListSerialization propertyListFromData:[self dataForType:aType] format:CPPropertyListUnknownFormat];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
+93
-118
@@ -25,12 +25,14 @@
|
|||||||
@import "CPMenu.j"
|
@import "CPMenu.j"
|
||||||
@import "CPMenuItem.j"
|
@import "CPMenuItem.j"
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
|
||||||
var VISIBLE_MARGIN = 7.0;
|
var VISIBLE_MARGIN = 7.0;
|
||||||
|
|
||||||
CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPPopUpButton
|
@class CPPopUpButton
|
||||||
|
|
||||||
@@ -40,11 +42,11 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
{
|
{
|
||||||
int _selectedIndex;
|
int _selectedIndex;
|
||||||
CPRectEdge _preferredEdge;
|
CPRectEdge _preferredEdge;
|
||||||
|
|
||||||
CPMenu _menu;
|
CPMenu _menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)themeClass
|
||||||
{
|
{
|
||||||
return "popup-button";
|
return "popup-button";
|
||||||
}
|
}
|
||||||
@@ -52,27 +54,27 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
/*!
|
/*!
|
||||||
Initializes the pop-up button to the specified size.
|
Initializes the pop-up button to the specified size.
|
||||||
@param aFrame the size for the button
|
@param aFrame the size for the button
|
||||||
@param shouldPullDown \c YES makes this a pull-down menu, \c NO makes it a pop-up menu.
|
@param shouldPullDown <code>YES</code> makes this a pull-down menu, <code>NO</code> makes it a pop-up menu.
|
||||||
@return the initialized pop-up button
|
@return the initialized pop-up button
|
||||||
*/
|
*/
|
||||||
- (id)initWithFrame:(CGRect)aFrame pullsDown:(BOOL)shouldPullDown
|
- (id)initWithFrame:(CGRect)aFrame pullsDown:(BOOL)shouldPullDown
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_selectedIndex = CPNotFound;
|
_selectedIndex = CPNotFound;
|
||||||
_preferredEdge = CPMaxYEdge;
|
_preferredEdge = CPMaxYEdge;
|
||||||
|
|
||||||
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
||||||
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
|
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
|
||||||
[self setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
|
[self setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
|
||||||
|
|
||||||
[self setMenu:[[CPMenu alloc] initWithTitle:@""]];
|
[self setMenu:[[CPMenu alloc] initWithTitle:@""]];
|
||||||
|
|
||||||
[self setPullsDown:shouldPullDown];
|
[self setPullsDown:shouldPullDown];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +87,8 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Specifies whether the object is a pull-down or a pop-up menu.
|
Specifies whether the object is a pull-down or a pop-up menu.
|
||||||
@param shouldPullDown \c YES makes the pop-up button
|
@param shouldPullDown <code>YES</code> makes the pop-up button
|
||||||
a pull-down menu. \c NO makes it a pop-up menu.
|
a pull-down menu. <code>NO</code> makes it a pop-up menu.
|
||||||
*/
|
*/
|
||||||
- (void)setPullsDown:(BOOL)shouldPullDown
|
- (void)setPullsDown:(BOOL)shouldPullDown
|
||||||
{
|
{
|
||||||
@@ -109,7 +111,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the button is a pull-down menu. \c NO if the button is a pop-up menu.
|
Returns <code>YES</code> if the button is a pull-down menu. <code>NO</code> if the button is a pop-up menu.
|
||||||
*/
|
*/
|
||||||
- (BOOL)pullsDown
|
- (BOOL)pullsDown
|
||||||
{
|
{
|
||||||
@@ -143,7 +145,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
{
|
{
|
||||||
var index = 0,
|
var index = 0,
|
||||||
count = [titles count];
|
count = [titles count];
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
[self addItemWithTitle:titles[index]];
|
[self addItemWithTitle:titles[index]];
|
||||||
}
|
}
|
||||||
@@ -157,11 +159,11 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
{
|
{
|
||||||
var items = [self itemArray],
|
var items = [self itemArray],
|
||||||
count = [items count];
|
count = [items count];
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
if ([items[count] title] == aTitle)
|
if ([items[count] title] == aTitle)
|
||||||
[self removeItemAtIndex:count];
|
[self removeItemAtIndex:count];
|
||||||
|
|
||||||
[_menu insertItemWithTitle:aTitle action:NULL keyEquivalent:nil atIndex:anIndex];
|
[_menu insertItemWithTitle:aTitle action:NULL keyEquivalent:nil atIndex:anIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +173,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
- (void)removeAllItems
|
- (void)removeAllItems
|
||||||
{
|
{
|
||||||
var count = [_menu numberOfItems];
|
var count = [_menu numberOfItems];
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
[_menu removeItemAtIndex:0];
|
[_menu removeItemAtIndex:0];
|
||||||
}
|
}
|
||||||
@@ -198,18 +200,18 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
|
|
||||||
// Getting the User's Selection
|
// Getting the User's Selection
|
||||||
/*!
|
/*!
|
||||||
Returns the selected item or \c nil if no item is selected.
|
Returns the selected item or <code>nil</code> if no item is selected.
|
||||||
*/
|
*/
|
||||||
- (CPMenuItem)selectedItem
|
- (CPMenuItem)selectedItem
|
||||||
{
|
{
|
||||||
if (_selectedIndex < 0 || _selectedIndex > [self numberOfItems] - 1)
|
if (_selectedIndex < 0)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
return [_menu itemAtIndex:_selectedIndex];
|
return [_menu itemAtIndex:_selectedIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the title of the selected item or \c nil if no item is selected.
|
Returns the title of the selected item or <code>nil</code> if no item is selected.
|
||||||
*/
|
*/
|
||||||
- (CPString)titleOfSelectedItem
|
- (CPString)titleOfSelectedItem
|
||||||
{
|
{
|
||||||
@@ -251,15 +253,15 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
{
|
{
|
||||||
if (_selectedIndex == anIndex)
|
if (_selectedIndex == anIndex)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_selectedIndex >= 0 && ![self pullsDown])
|
if (_selectedIndex >= 0 && ![self pullsDown])
|
||||||
[[self selectedItem] setState:CPOffState];
|
[[self selectedItem] setState:CPOffState];
|
||||||
|
|
||||||
_selectedIndex = anIndex;
|
_selectedIndex = anIndex;
|
||||||
|
|
||||||
if (_selectedIndex >= 0 && ![self pullsDown])
|
if (_selectedIndex >= 0 && ![self pullsDown])
|
||||||
[[self selectedItem] setState:CPOnState];
|
[[self selectedItem] setState:CPOnState];
|
||||||
|
|
||||||
[self synchronizeTitleAndSelectedItem];
|
[self synchronizeTitleAndSelectedItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +310,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||||
|
|
||||||
if (_menu)
|
if (_menu)
|
||||||
{
|
{
|
||||||
[defaultCenter
|
[defaultCenter
|
||||||
@@ -328,7 +330,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
}
|
}
|
||||||
|
|
||||||
_menu = aMenu;
|
_menu = aMenu;
|
||||||
|
|
||||||
if (_menu)
|
if (_menu)
|
||||||
{
|
{
|
||||||
[defaultCenter
|
[defaultCenter
|
||||||
@@ -336,20 +338,20 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
selector:@selector(menuDidAddItem:)
|
selector:@selector(menuDidAddItem:)
|
||||||
name:CPMenuDidAddItemNotification
|
name:CPMenuDidAddItemNotification
|
||||||
object:_menu];
|
object:_menu];
|
||||||
|
|
||||||
[defaultCenter
|
[defaultCenter
|
||||||
addObserver:self
|
addObserver:self
|
||||||
selector:@selector(menuDidChangeItem:)
|
selector:@selector(menuDidChangeItem:)
|
||||||
name:CPMenuDidChangeItemNotification
|
name:CPMenuDidChangeItemNotification
|
||||||
object:_menu];
|
object:_menu];
|
||||||
|
|
||||||
[defaultCenter
|
[defaultCenter
|
||||||
addObserver:self
|
addObserver:self
|
||||||
selector:@selector(menuDidRemoveItem:)
|
selector:@selector(menuDidRemoveItem:)
|
||||||
name:CPMenuDidRemoveItemNotification
|
name:CPMenuDidRemoveItemNotification
|
||||||
object:_menu];
|
object:_menu];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self synchronizeTitleAndSelectedItem];
|
[self synchronizeTitleAndSelectedItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,7 +372,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the item at the specified index or \c nil if the item does not exist.
|
Returns the item at the specified index or <code>nil</code> if the item does not exist.
|
||||||
@param anIndex the index of the item to obtain
|
@param anIndex the index of the item to obtain
|
||||||
*/
|
*/
|
||||||
- (CPMenuItem)itemAtIndex:(unsigned)anIndex
|
- (CPMenuItem)itemAtIndex:(unsigned)anIndex
|
||||||
@@ -379,7 +381,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the title of the item at the specified index or \c nil if no item exists.
|
Returns the title of the item at the specified index or <code>nil</code> if no item exists.
|
||||||
@param anIndex the index of the item
|
@param anIndex the index of the item
|
||||||
*/
|
*/
|
||||||
- (CPString)itemTitleAtIndex:(unsigned)anIndex
|
- (CPString)itemTitleAtIndex:(unsigned)anIndex
|
||||||
@@ -394,13 +396,12 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
{
|
{
|
||||||
var titles = [],
|
var titles = [],
|
||||||
items = [self itemArray],
|
items = [self itemArray],
|
||||||
|
|
||||||
index = 0,
|
index = 0,
|
||||||
count = [items count];
|
count = [items count];
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
titles.push([items[index] title]);
|
items.push([items[index] title]);
|
||||||
|
|
||||||
return titles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -518,14 +519,14 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var index = [self indexOfItemWithTitle:aTitle];
|
var index = [self indexOfItemWithTitle:aTitle];
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
[self addItemWithTitle:aTitle];
|
[self addItemWithTitle:aTitle];
|
||||||
|
|
||||||
index = [self numberOfItems] - 1;
|
index = [self numberOfItems] - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self selectItemAtIndex:index];
|
[self selectItemAtIndex:index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -553,7 +554,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
if ([self pullsDown])
|
if ([self pullsDown])
|
||||||
{
|
{
|
||||||
var items = [_menu itemArray];
|
var items = [_menu itemArray];
|
||||||
|
|
||||||
if ([items count] > 0)
|
if ([items count] > 0)
|
||||||
item = items[0];
|
item = items[0];
|
||||||
}
|
}
|
||||||
@@ -572,22 +573,22 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
- (void)menuDidAddItem:(CPNotification)aNotification
|
- (void)menuDidAddItem:(CPNotification)aNotification
|
||||||
{
|
{
|
||||||
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"];
|
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"];
|
||||||
|
|
||||||
if (_selectedIndex < 0)
|
if (_selectedIndex < 0)
|
||||||
[self selectItemAtIndex:0];
|
[self selectItemAtIndex:0];
|
||||||
|
|
||||||
else if (index == _selectedIndex)
|
else if (index == _selectedIndex)
|
||||||
[self synchronizeTitleAndSelectedItem];
|
[self synchronizeTitleAndSelectedItem];
|
||||||
|
|
||||||
else if (index < _selectedIndex)
|
else if (index < _selectedIndex)
|
||||||
++_selectedIndex;
|
++_selectedIndex;
|
||||||
|
|
||||||
if (index == 0 && [self pullsDown])
|
if (index == 0 && [self pullsDown])
|
||||||
{
|
{
|
||||||
var items = [_menu itemArray];
|
var items = [_menu itemArray];
|
||||||
|
|
||||||
[items[0] setHidden:YES];
|
[items[0] setHidden:YES];
|
||||||
|
|
||||||
if (items.length > 0)
|
if (items.length > 0)
|
||||||
[items[1] setHidden:NO];
|
[items[1] setHidden:NO];
|
||||||
}
|
}
|
||||||
@@ -612,10 +613,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
|
|
||||||
if ([self pullsDown] && index != 0)
|
if ([self pullsDown] && index != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (![self pullsDown] && index != _selectedIndex)
|
if (![self pullsDown] && index != _selectedIndex)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self synchronizeTitleAndSelectedItem];
|
[self synchronizeTitleAndSelectedItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,63 +627,30 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
- (void)menuDidRemoveItem:(CPNotification)aNotification
|
- (void)menuDidRemoveItem:(CPNotification)aNotification
|
||||||
{
|
{
|
||||||
var numberOfItems = [self numberOfItems];
|
var numberOfItems = [self numberOfItems];
|
||||||
|
|
||||||
if (numberOfItems <= _selectedIndex && numberOfItems > 0)
|
if (numberOfItems <= _selectedIndex)
|
||||||
[self selectItemAtIndex:numberOfItems - 1];
|
[self selectItemAtIndex:numberOfItems - 1];
|
||||||
else
|
|
||||||
[self synchronizeTitleAndSelectedItem];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
if (![self isEnabled] || ![self numberOfItems])
|
if (![self isEnabled])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self highlight:YES];
|
[self highlight:YES];
|
||||||
|
|
||||||
var menu = [self menu],
|
var menu = [self menu],
|
||||||
bounds = [self bounds],
|
theWindow = [self window],
|
||||||
minimumWidth = CGRectGetWidth(bounds);
|
menuWindow = [_CPMenuWindow menuWindowWithMenu:menu font:[self font]];
|
||||||
|
|
||||||
// FIXME: setFont: should set the font on the menu.
|
[menuWindow setDelegate:self];
|
||||||
[menu setFont:[self font]];
|
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
||||||
|
|
||||||
|
// Pull Down Menus show up directly below their buttons.
|
||||||
if ([self pullsDown])
|
if ([self pullsDown])
|
||||||
{
|
var menuOrigin = [theWindow convertBaseToBridge:[self convertPoint:CGPointMake(0.0, CGRectGetMaxY([self bounds])) toView:nil]];
|
||||||
var positionedItem = nil,
|
|
||||||
location = CGPointMake(0.0, CGRectGetMaxY(bounds));
|
// Pop Up Menus attempt to show up "on top" of the selected item.
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var contentRect = [self contentRectForBounds:bounds],
|
|
||||||
positionedItem = [self selectedItem],
|
|
||||||
standardLeftMargin = [_CPMenuWindow _standardLeftMargin] + [_CPMenuItemStandardView _standardLeftMargin],
|
|
||||||
location = CGPointMake(CGRectGetMinX(contentRect) - standardLeftMargin, 0.0);
|
|
||||||
|
|
||||||
minimumWidth += standardLeftMargin;
|
|
||||||
|
|
||||||
// To ensure the selected item is highlighted correctly, unset the highlighted item
|
|
||||||
[menu _highlightItemAtIndex:CPNotFound];
|
|
||||||
}
|
|
||||||
|
|
||||||
[menu setMinimumWidth:minimumWidth];
|
|
||||||
|
|
||||||
[menu
|
|
||||||
_popUpMenuPositioningItem:positionedItem
|
|
||||||
atLocation:location
|
|
||||||
topY:CGRectGetMinY(bounds)
|
|
||||||
bottomY:CGRectGetMaxY(bounds)
|
|
||||||
inView:self
|
|
||||||
callback:function(aMenu)
|
|
||||||
{
|
|
||||||
[self highlight:NO];
|
|
||||||
|
|
||||||
var highlightedItem = [aMenu highlightedItem];
|
|
||||||
|
|
||||||
if ([highlightedItem _isSelectable])
|
|
||||||
[self selectItem:highlightedItem];
|
|
||||||
}];
|
|
||||||
/*
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This is confusing, I KNOW, so let me explain it to you.
|
// This is confusing, I KNOW, so let me explain it to you.
|
||||||
@@ -690,19 +658,43 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
// 1. So calculate where our content is, then calculate where the menu item is.
|
// 1. So calculate where our content is, then calculate where the menu item is.
|
||||||
// 2. Move LEFT by whatever indentation we have (offsetWidths, aka, window margin, item margin, etc).
|
// 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.
|
// 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:bounds] toView:nil],
|
var contentRect = [self convertRect:[self contentRectForBounds:[self bounds]] toView:nil],
|
||||||
menuOrigin = [theWindow convertBaseToGlobal:contentRect.origin],
|
menuOrigin = [theWindow convertBaseToBridge:contentRect.origin],
|
||||||
menuItemRect = [menuWindow rectForItemAtIndex:_selectedIndex];
|
menuItemRect = [menuWindow rectForItemAtIndex:_selectedIndex];
|
||||||
|
|
||||||
menuOrigin.x -= CGRectGetMinX(menuItemRect) + [menuWindow overlapOffsetWidth] + [[[menu itemAtIndex:_selectedIndex] _menuItemView] overlapOffsetWidth];
|
menuOrigin.x -= CGRectGetMinX(menuItemRect) + [menuWindow overlapOffsetWidth] + [[[menu itemAtIndex:_selectedIndex] _menuItemView] overlapOffsetWidth];
|
||||||
menuOrigin.y -= CGRectGetMinY(menuItemRect) + (CGRectGetHeight(menuItemRect) - CGRectGetHeight(contentRect)) / 2.0;
|
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;
|
||||||
|
|
||||||
|
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:)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)rightMouseDown:(CPEvent)anEvent
|
/*
|
||||||
|
@ignore
|
||||||
|
*/
|
||||||
|
- (void)menuWindowDidFinishTracking:(_CPMenuWindow)aMenuWindow highlightedItem:(CPMenuItem)aMenuItem
|
||||||
{
|
{
|
||||||
// Disable standard CPView behaviour which incorrectly displays the menu as a 'context menu'.
|
[_CPMenuWindow poolMenuWindow:aMenuWindow];
|
||||||
|
|
||||||
|
[self highlight:NO];
|
||||||
|
|
||||||
|
var index = [_menu indexOfItem:aMenuItem];
|
||||||
|
|
||||||
|
if (index == CPNotFound)
|
||||||
|
return;
|
||||||
|
|
||||||
|
[self selectItemAtIndex:index];
|
||||||
|
|
||||||
|
[CPApp sendAction:[aMenuItem action] to:[aMenuItem target] from:aMenuItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_popUpItemAction:(id)aSender
|
- (void)_popUpItemAction:(id)aSender
|
||||||
@@ -710,23 +702,6 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
[self sendAction:[self action] to:[self target]];
|
[self sendAction:[self action] to:[self target]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
var count = objects.length,
|
|
||||||
value = [objects[0] valueForKeyPath:aKeyPath];
|
|
||||||
|
|
||||||
[self selectItemWithTag:value];
|
|
||||||
[self setEnabled:YES];
|
|
||||||
|
|
||||||
while (count-- > 1)
|
|
||||||
{
|
|
||||||
if (value !== [objects[count] valueForKeyPath:aKeyPath])
|
|
||||||
{
|
|
||||||
[[self selectedItem] setState:CPOffState];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
||||||
@@ -744,7 +719,7 @@ var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
|||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super initWithCoder:aCoder];
|
self = [super initWithCoder:aCoder];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
// Nothing is currently selected
|
// Nothing is currently selected
|
||||||
@@ -753,7 +728,7 @@ var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
|||||||
[self setMenu:[aCoder decodeObjectForKey:CPPopUpButtonMenuKey]];
|
[self setMenu:[aCoder decodeObjectForKey:CPPopUpButtonMenuKey]];
|
||||||
[self selectItemAtIndex:[aCoder decodeObjectForKey:CPPopUpButtonSelectedIndexKey]];
|
[self selectItemAtIndex:[aCoder decodeObjectForKey:CPPopUpButtonSelectedIndexKey]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -765,7 +740,7 @@ var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
|||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
[aCoder encodeObject:_menu forKey:CPPopUpButtonMenuKey];
|
[aCoder encodeObject:_menu forKey:CPPopUpButtonMenuKey];
|
||||||
[aCoder encodeInt:_selectedIndex forKey:CPPopUpButtonSelectedIndexKey];
|
[aCoder encodeInt:_selectedIndex forKey:CPPopUpButtonSelectedIndexKey];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,11 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CGGeometry.j"
|
@import <AppKit/CGGeometry.j>
|
||||||
@import "CPImageView.j"
|
@import <AppKit/CPImageView.j>
|
||||||
@import "CPView.j"
|
@import <AppKit/CPView.j>
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -102,14 +104,17 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
for (; start <= end; ++start)
|
for (; start <= end; ++start)
|
||||||
{
|
{
|
||||||
CPProgressIndicatorBezelBorderViewPool[start] = [];
|
CPProgressIndicatorBezelBorderViewPool[start] = [];
|
||||||
|
|
||||||
CPProgressIndicatorBezelBorderViewPool[start][CPMiniControlSize] = [];
|
CPProgressIndicatorBezelBorderViewPool[start][CPMiniControlSize] = [];
|
||||||
CPProgressIndicatorBezelBorderViewPool[start][CPSmallControlSize] = [];
|
CPProgressIndicatorBezelBorderViewPool[start][CPSmallControlSize] = [];
|
||||||
CPProgressIndicatorBezelBorderViewPool[start][CPRegularControlSize] = [];
|
CPProgressIndicatorBezelBorderViewPool[start][CPRegularControlSize] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
CPProgressIndicatorClassName = [self className];
|
CPProgressIndicatorClassName = [self className];
|
||||||
|
|
||||||
|
|
||||||
CPProgressIndicatorStyleIdentifiers = [];
|
CPProgressIndicatorStyleIdentifiers = [];
|
||||||
|
|
||||||
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] = @"Bar";
|
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] = @"Bar";
|
||||||
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorSpinningStyle] = @"Spinny";
|
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorSpinningStyle] = @"Spinny";
|
||||||
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle] = @"HUDBar";
|
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle] = @"HUDBar";
|
||||||
@@ -121,20 +126,30 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
CPProgressIndicatorStyleSizes = [];
|
CPProgressIndicatorStyleSizes = [];
|
||||||
|
|
||||||
// Bar Sttyle
|
// Bar Sttyle
|
||||||
var prefixes = [
|
var prefix = CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle];
|
||||||
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
|
|
||||||
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
|
|
||||||
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle],
|
|
||||||
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle]
|
|
||||||
];
|
|
||||||
|
|
||||||
for (var i = 0, count = prefixes.length; i<count; i++)
|
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||||
{
|
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||||
var prefix = prefixes[i];
|
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||||
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
|
|
||||||
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
|
prefix = CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle];
|
||||||
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
|
|
||||||
}
|
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||||
|
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||||
|
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||||
|
|
||||||
|
// HUD Bar Style
|
||||||
|
prefix = CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle];
|
||||||
|
|
||||||
|
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||||
|
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||||
|
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||||
|
|
||||||
|
prefix = CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle];
|
||||||
|
|
||||||
|
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||||
|
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||||
|
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
@@ -190,7 +205,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Always returns \c NO. Cappuccino does not have multiple threads.
|
Always returns <code>NO</code>. Cappuccino does not have multiple threads.
|
||||||
*/
|
*/
|
||||||
- (BOOL)usesThreadedAnimation
|
- (BOOL)usesThreadedAnimation
|
||||||
{
|
{
|
||||||
@@ -314,7 +329,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Specifies whether this progress indicator should be indeterminate or display progress based on it's max and min.
|
Specifies whether this progress indicator should be indeterminate or display progress based on it's max and min.
|
||||||
@param isDeterminate \c YES makes the indicator indeterminate
|
@param isDeterminate <code>YES</code> makes the indicator indeterminate
|
||||||
*/
|
*/
|
||||||
- (void)setIndeterminate:(BOOL)isIndeterminate
|
- (void)setIndeterminate:(BOOL)isIndeterminate
|
||||||
{
|
{
|
||||||
@@ -327,7 +342,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the progress bar is indeterminate.
|
Returns <code>YES</code> if the progress bar is indeterminate.
|
||||||
*/
|
*/
|
||||||
- (BOOL)isIndeterminate
|
- (BOOL)isIndeterminate
|
||||||
{
|
{
|
||||||
@@ -362,9 +377,9 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets whether the indicator should be displayed when it isn't animating. By default this is \c YES if the style
|
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 \c NO if it's CPProgressIndicatorSpinningStyle.
|
is CPProgressIndicatorBarStyle, and <code>NO</code> if it's CPProgressIndicatorSpinningStyle.
|
||||||
@param isDisplayedWhenStopped \c YES means the indicator will be displayed when it's not animating.
|
@param isDisplayedWhenStopped <code>YES</code> means the indicator will be displayed when it's not animating.
|
||||||
*/
|
*/
|
||||||
- (void)setDisplayedWhenStopped:(BOOL)isDisplayedWhenStopped
|
- (void)setDisplayedWhenStopped:(BOOL)isDisplayedWhenStopped
|
||||||
{
|
{
|
||||||
@@ -379,7 +394,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the progress bar is displayed when not animating.
|
Returns <code>YES</code> if the progress bar is displayed when not animating.
|
||||||
*/
|
*/
|
||||||
- (BOOL)isDisplayedWhenStopped
|
- (BOOL)isDisplayedWhenStopped
|
||||||
{
|
{
|
||||||
@@ -413,25 +428,21 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
|
|
||||||
if (!_barView)
|
if (!_barView)
|
||||||
{
|
{
|
||||||
_barView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 16.0)];
|
_barView = [[CPView alloc] initWithFrame:CGRectMake(2.0, 2.0, 0.0, 9.0)];
|
||||||
|
|
||||||
|
[_barView setBackgroundColor:[CPColor redColor]];
|
||||||
|
|
||||||
[self addSubview:_barView];
|
[self addSubview:_barView];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_barView setBackgroundColor:_CPControlThreePartImagePattern(
|
[_barView setBackgroundColor:_CPControlColorWithPatternImage(
|
||||||
NO,
|
|
||||||
CPProgressIndicatorStyleSizes,
|
CPProgressIndicatorStyleSizes,
|
||||||
CPProgressIndicatorClassName,
|
CPProgressIndicatorClassName,
|
||||||
@"Bar",
|
@"Bar",
|
||||||
CPProgressIndicatorStyleIdentifiers[_style],
|
CPProgressIndicatorStyleIdentifiers[_style],
|
||||||
_CPControlIdentifierForControlSize(_controlSize))];
|
_CPControlIdentifierForControlSize(_controlSize))];
|
||||||
|
|
||||||
var width = CGRectGetWidth([self bounds]),
|
[_barView setFrameSize:CGSizeMake(CGRectGetWidth([self bounds]) * (_doubleValue - _minValue) / (_maxValue - _minValue) - 4.0, 9.0)];
|
||||||
barWidth = width * ((_doubleValue - _minValue) / (_maxValue - _minValue));
|
|
||||||
|
|
||||||
if (barWidth > 0.0 && barWidth < 4.0)
|
|
||||||
barWidth = 4.0;
|
|
||||||
|
|
||||||
[_barView setFrameSize:CGSizeMake(barWidth, 16.0)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
|
|||||||
+17
-26
@@ -25,29 +25,28 @@
|
|||||||
|
|
||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
|
|
||||||
from this mailing list thread:
|
from this mailing list thread:
|
||||||
http://groups.google.com/group/objectivej/browse_thread/thread/7c41cbd9cbee9ea3
|
http://groups.google.com/group/objectivej/browse_thread/thread/7c41cbd9cbee9ea3
|
||||||
|
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
Creating a checkbox is easy enough:
|
Creating a checkbox is easy enough:
|
||||||
|
|
||||||
checkbox = [[CPCheckBox alloc] initWithFrame:aFrame];
|
checkbox = [[CPCheckBox alloc] initWithFrame:aFrame];
|
||||||
|
|
||||||
That's basically all there is to it. Radio buttons are very similar,
|
That's basically all there is to it. Radio buttons are very similar,
|
||||||
the key difference is the introduction of a new class CPRadioGroup,
|
the key difference is the introduction of a new class CPRadioGroup,
|
||||||
which defines which radio buttons are part of the same group:
|
which defines which radio buttons are part of the same group:
|
||||||
|
|
||||||
[myRadioButton setRadioGroup:aRadioGroup];
|
[myRadioButton setRadioGroup:aRadioGroup];
|
||||||
|
|
||||||
Every radio button receives a unique radio group by default (so if you
|
Every radio button receives a unique radio group by default (so if you
|
||||||
do nothing further, they will all behave independently), but you can
|
do nothing further, they will all behave independently), but you can
|
||||||
use an existing radio button's group with other buttons as so:
|
use an existing radio button's group with other buttons as so:
|
||||||
|
|
||||||
button1 = [[CPRadio alloc] initWithFrame:aFrame];
|
button1 = [[CPRadio alloc] initWithFrame:aFrame];
|
||||||
...
|
...
|
||||||
button2 = [[CPRadio alloc] initWithFrame:aFrame radioGroup:[button1
|
button2 = [[CPRadio alloc] initWithFrame:aFrame radioGroup:[button1
|
||||||
@@ -56,16 +55,13 @@
|
|||||||
button3 = [[CPRadio alloc] initWithFrame:aFrame radioGroup:[button1
|
button3 = [[CPRadio alloc] initWithFrame:aFrame radioGroup:[button1
|
||||||
radioGroup]];
|
radioGroup]];
|
||||||
...etc...
|
...etc...
|
||||||
|
|
||||||
Here, all the radio buttons will act "together". [[button1 radioGroup]
|
Here, all the radio buttons will act "together". [[button1 radioGroup]
|
||||||
allRadios] returns every button that's part of this group, and
|
allRadios] returns every button that's part of this group, and
|
||||||
[[button1 radioGroup] selectedRadio] returns the currently selected
|
[[button1 radioGroup] selectedRadio] returns the currently selected
|
||||||
option.
|
option.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CPRadioImageOffset = 4.0;
|
|
||||||
|
|
||||||
@implementation CPRadio : CPButton
|
@implementation CPRadio : CPButton
|
||||||
{
|
{
|
||||||
CPRadioGroup _radioGroup;
|
CPRadioGroup _radioGroup;
|
||||||
@@ -90,7 +86,7 @@ CPRadioImageOffset = 4.0;
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)themeClass
|
||||||
{
|
{
|
||||||
return @"radio";
|
return @"radio";
|
||||||
}
|
}
|
||||||
@@ -99,10 +95,10 @@ CPRadioImageOffset = 4.0;
|
|||||||
- (id)initWithFrame:(CGRect)aFrame radioGroup:(CPRadioGroup)aRadioGroup
|
- (id)initWithFrame:(CGRect)aFrame radioGroup:(CPRadioGroup)aRadioGroup
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
[self setRadioGroup:aRadioGroup];
|
[self setRadioGroup:aRadioGroup || [CPRadioGroup new]];
|
||||||
|
|
||||||
[self setHighlightsBy:CPContentsCellMask];
|
[self setHighlightsBy:CPContentsCellMask];
|
||||||
[self setShowsStateBy:CPContentsCellMask];
|
[self setShowsStateBy:CPContentsCellMask];
|
||||||
@@ -113,13 +109,13 @@ CPRadioImageOffset = 4.0;
|
|||||||
|
|
||||||
[self setBordered:YES];
|
[self setBordered:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
return [self initWithFrame:aFrame radioGroup:[CPRadioGroup new]];
|
return [self initWithFrame:aFrame radioGroup:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPInteger)nextState
|
- (CPInteger)nextState
|
||||||
@@ -145,7 +141,7 @@ CPRadioImageOffset = 4.0;
|
|||||||
- (void)setObjectValue:(id)aValue
|
- (void)setObjectValue:(id)aValue
|
||||||
{
|
{
|
||||||
[super setObjectValue:aValue];
|
[super setObjectValue:aValue];
|
||||||
|
|
||||||
if ([self state] === CPOnState)
|
if ([self state] === CPOnState)
|
||||||
[_radioGroup _setSelectedRadio:self];
|
[_radioGroup _setSelectedRadio:self];
|
||||||
}
|
}
|
||||||
@@ -179,9 +175,6 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
|||||||
{
|
{
|
||||||
CPSet _radios;
|
CPSet _radios;
|
||||||
CPRadio _selectedRadio;
|
CPRadio _selectedRadio;
|
||||||
|
|
||||||
id _target @accessors(property=target);
|
|
||||||
SEL _action @accessors(property=action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
@@ -220,8 +213,6 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
|||||||
|
|
||||||
[_selectedRadio setState:CPOffState];
|
[_selectedRadio setState:CPOffState];
|
||||||
_selectedRadio = aRadio;
|
_selectedRadio = aRadio;
|
||||||
|
|
||||||
[CPApp sendAction:_action to:_target from:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPRadio)selectedRadio
|
- (CPRadio)selectedRadio
|
||||||
|
|||||||
+67
-80
@@ -23,23 +23,19 @@
|
|||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
|
||||||
|
|
||||||
CPDeleteKeyCode = 8;
|
CPDeleteKeyCode = 8;
|
||||||
CPTabKeyCode = 9;
|
CPTabKeyCode = 9;
|
||||||
CPReturnKeyCode = 13;
|
CPReturnKeyCode = 13;
|
||||||
CPEscapeKeyCode = 27;
|
CPEscapeKeyCode = 27;
|
||||||
CPSpaceKeyCode = 32;
|
CPLeftArrowKeyCode = 37;
|
||||||
CPPageUpKeyCode = 33;
|
CPUpArrowKeyCode = 38;
|
||||||
CPPageDownKeyCode = 34;
|
CPRightArrowKeyCode = 39;
|
||||||
CPLeftArrowKeyCode = 37;
|
CPDownArrowKeyCode = 40;
|
||||||
CPUpArrowKeyCode = 38;
|
|
||||||
CPRightArrowKeyCode = 39;
|
|
||||||
CPDownArrowKeyCode = 40;
|
|
||||||
CPDeleteForwardKeyCode = 46;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPResponder
|
@class CPResponder
|
||||||
|
|
||||||
Subclasses of CPResonder can be part of the responder chain.
|
Subclasses of CPResonder can be part of the responder chain.
|
||||||
*/
|
*/
|
||||||
@implementation CPResponder : CPObject
|
@implementation CPResponder : CPObject
|
||||||
@@ -50,7 +46,7 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
|
|
||||||
// Changing the first responder
|
// Changing the first responder
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the receiver is able to become the first responder. \c NO otherwise.
|
Returns <code>YES</code> if the receiver is able to become the first responder. <code>NO</code> otherwise.
|
||||||
*/
|
*/
|
||||||
- (BOOL)acceptsFirstResponder
|
- (BOOL)acceptsFirstResponder
|
||||||
{
|
{
|
||||||
@@ -59,8 +55,8 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Notifies the receiver that it will become the first responder. The receiver can reject first
|
Notifies the receiver that it will become the first responder. The receiver can reject first
|
||||||
responder if it returns \c NO. The default implementation always returns \c YES.
|
responder if it returns <code>NO</code>. The default implementation always returns <code>YES</code>.
|
||||||
@return \c YES if the receiver accepts first responder status.
|
@return <code>YES</code> if the receiver accepts first responder status.
|
||||||
*/
|
*/
|
||||||
- (BOOL)becomeFirstResponder
|
- (BOOL)becomeFirstResponder
|
||||||
{
|
{
|
||||||
@@ -69,7 +65,7 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Notifies the receiver that it has been asked to give up first responder status.
|
Notifies the receiver that it has been asked to give up first responder status.
|
||||||
@return \c YES if the receiver is willing to give up first responder status.
|
@return <code>YES</code> if the receiver is willing to give up first responder status.
|
||||||
*/
|
*/
|
||||||
- (BOOL)resignFirstResponder
|
- (BOOL)resignFirstResponder
|
||||||
{
|
{
|
||||||
@@ -100,29 +96,41 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
*/
|
*/
|
||||||
- (void)interpretKeyEvents:(CPArray)events
|
- (void)interpretKeyEvents:(CPArray)events
|
||||||
{
|
{
|
||||||
var index = 0,
|
var event,
|
||||||
count = [events count];
|
index = 0;
|
||||||
|
|
||||||
for (; index < count; ++index)
|
while(event = events[index++])
|
||||||
{
|
{
|
||||||
var event = events[index],
|
switch([event keyCode])
|
||||||
modifierFlags = [event modifierFlags],
|
|
||||||
character = [event charactersIgnoringModifiers],
|
|
||||||
selectorNames = [CPKeyBinding selectorsForKey:character modifierFlags:modifierFlags];
|
|
||||||
|
|
||||||
if (selectorNames)
|
|
||||||
{
|
{
|
||||||
for (var s = 0, scount = selectorNames.length; s < scount; s++)
|
case CPLeftArrowKeyCode: [self moveBackward:self];
|
||||||
{
|
break;
|
||||||
var selector = selectorNames[s];
|
case CPRightArrowKeyCode: [self moveForward:self];
|
||||||
if (!selector)
|
break;
|
||||||
continue;
|
case CPUpArrowKeyCode: [self moveUp:self];
|
||||||
|
break;
|
||||||
|
case CPDownArrowKeyCode: [self moveDown:self];
|
||||||
|
break;
|
||||||
|
case CPDeleteKeyCode: [self deleteBackward:self];
|
||||||
|
break;
|
||||||
|
case CPReturnKeyCode:
|
||||||
|
case 3: [self insertLineBreak:self];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CPEscapeKeyCode: [self cancel:self];
|
||||||
|
break;
|
||||||
|
|
||||||
[self doCommandBySelector:CPSelectorFromString(selector)];
|
case CPTabKeyCode: var shift = [event modifierFlags] & CPShiftKeyMask;
|
||||||
}
|
|
||||||
|
if (!shift)
|
||||||
|
[self insertTab:self];
|
||||||
|
else
|
||||||
|
[self insertBackTab:self];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: [self insertText:[event characters]];
|
||||||
}
|
}
|
||||||
else if (!(modifierFlags & (CPCommandKeyMask | CPControlKeyMask)) && [self respondsToSelector:@selector(insertText:)])
|
|
||||||
[self insertText:[event characters]];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,15 +143,6 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Notifies the receiver that the user has clicked the right mouse down in its area.
|
|
||||||
@param anEvent contains information about the right click
|
|
||||||
*/
|
|
||||||
- (void)rightMouseDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Notifies the receiver that the user has initiated a drag
|
Notifies the receiver that the user has initiated a drag
|
||||||
over it. A drag is a mouse movement while the left button is down.
|
over it. A drag is a mouse movement while the left button is down.
|
||||||
@@ -163,15 +162,6 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Notifies the receiver that the user has released the right mouse button.
|
|
||||||
@param anEvent contains information about the release
|
|
||||||
*/
|
|
||||||
- (void)rightMouseUp:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Notifies the receiver that the user has moved the mouse (with no buttons down).
|
Notifies the receiver that the user has moved the mouse (with no buttons down).
|
||||||
@param anEvent contains information about the movement
|
@param anEvent contains information about the movement
|
||||||
@@ -222,20 +212,11 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Notifies the receiver that the user has pressed or released a modifier key (Shift, Control, and so on).
|
|
||||||
@param anEvent information about the key press
|
|
||||||
*/
|
|
||||||
- (void)flagsChanged:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
FIXME This description is bad.
|
FIXME This description is bad.
|
||||||
Based on \c anEvent, the receiver should simulate the event.
|
Based on <code>anEvent</code>, the receiver should simulate the event.
|
||||||
@param anEvent the event to simulate
|
@param anEvent the event to simulate
|
||||||
@return \c YES if the event receiver simulated the event
|
@return <code>YES</code> if the event receiver simulated the event
|
||||||
*/
|
*/
|
||||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
@@ -243,6 +224,14 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Action Methods
|
// 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.
|
Insert a line break at the caret position or selection.
|
||||||
@param aSender the object requesting this
|
@param aSender the object requesting this
|
||||||
@@ -258,7 +247,11 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
*/
|
*/
|
||||||
- (void)insertNewline:(id)aSender
|
- (void)insertNewline:(id)aSender
|
||||||
{
|
{
|
||||||
[[self nextResponder] insertNewline:aSender];
|
[self insertNewline:aSender];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cancel:(id)sender
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)insertTab:(id)sender
|
- (void)insertTab:(id)sender
|
||||||
@@ -280,12 +273,12 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
// Dispatch methods
|
// Dispatch methods
|
||||||
/*!
|
/*!
|
||||||
The receiver will attempt to perform the command,
|
The receiver will attempt to perform the command,
|
||||||
if it responds to it. If not, the \c -nextResponder will be called to do it.
|
if it responds to it. If not, the <code>nextResponder</code> will be called to do it.
|
||||||
@param aSelector the command to attempt
|
@param aSelector the command to attempt
|
||||||
*/
|
*/
|
||||||
- (void)doCommandBySelector:(SEL)aSelector
|
- (void)doCommandBySelector:(SEL)aSelector
|
||||||
{
|
{
|
||||||
if ([self respondsToSelector:aSelector])
|
if([self respondsToSelector:aSelector])
|
||||||
[self performSelector:aSelector];
|
[self performSelector:aSelector];
|
||||||
else
|
else
|
||||||
[_nextResponder doCommandBySelector:aSelector];
|
[_nextResponder doCommandBySelector:aSelector];
|
||||||
@@ -295,15 +288,15 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
The receiver will attempt to perform the command, or pass it on to the next responder if it doesn't respond to it.
|
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 aSelector the command to perform
|
||||||
@param anObject the argument to the method
|
@param anObject the argument to the method
|
||||||
@return \c YES if the receiver was able to perform the command, or a responder down the chain was
|
@return <code>YES</code> if the receiver was able to perform the command, or a responder down the chain was
|
||||||
able to perform the command.
|
able to perform the command.
|
||||||
*/
|
*/
|
||||||
- (BOOL)tryToPerform:(SEL)aSelector with:(id)anObject
|
- (BOOL)tryToPerform:(SEL)aSelector with:(id)anObject
|
||||||
{
|
{
|
||||||
if ([self respondsToSelector:aSelector])
|
if([self respondsToSelector:aSelector])
|
||||||
{
|
{
|
||||||
[self performSelector:aSelector withObject:anObject];
|
[self performSelector:aSelector withObject:anObject];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,8 +335,7 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPResponderNextResponderKey = @"CPResponderNextResponderKey",
|
var CPResponderNextResponderKey = @"CPResponderNextResponderKey";
|
||||||
CPResponderMenuKey = @"CPResponderMenuKey";
|
|
||||||
|
|
||||||
@implementation CPResponder (CPCoding)
|
@implementation CPResponder (CPCoding)
|
||||||
|
|
||||||
@@ -355,13 +347,10 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey",
|
|||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
|
||||||
_nextResponder = [aCoder decodeObjectForKey:CPResponderNextResponderKey];
|
_nextResponder = [aCoder decodeObjectForKey:CPResponderNextResponderKey];
|
||||||
_menu = [aCoder decodeObjectForKey:CPResponderMenuKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,8 +363,6 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey",
|
|||||||
// This will come out nil on the other side with decodeObjectForKey:
|
// This will come out nil on the other side with decodeObjectForKey:
|
||||||
if (_nextResponder !== nil)
|
if (_nextResponder !== nil)
|
||||||
[aCoder encodeConditionalObject:_nextResponder forKey:CPResponderNextResponderKey];
|
[aCoder encodeConditionalObject:_nextResponder forKey:CPResponderNextResponderKey];
|
||||||
|
|
||||||
[aCoder encodeObject:_menu forKey:CPResponderMenuKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
|
|
||||||
@import "CPPanel.j"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPSavePanel : CPPanel
|
|
||||||
{
|
|
||||||
CPURL _URL;
|
|
||||||
|
|
||||||
BOOL _isExtensionHidden @accessors(getter=isExtensionHidden, setter=setExtensionHidden:);
|
|
||||||
BOOL _canSelectHiddenExtension @accessors(property=canSelectHiddenExtension);
|
|
||||||
BOOL _allowsOtherFileTypes @accessors(property=allowsOtherFileTypes);
|
|
||||||
BOOL _canCreateDirectories @accessors(property=canCreateDirectories);
|
|
||||||
|
|
||||||
CPArray _allowedFileTypes @accessors(property=allowedFileTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)savePanel
|
|
||||||
{
|
|
||||||
return [[CPSavePanel alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_canCreateDirectories = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPInteger)runModal
|
|
||||||
{
|
|
||||||
// FIXME: Is this correct???
|
|
||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
|
||||||
|
|
||||||
if (typeof window["cpSavePanel"] === "function")
|
|
||||||
{
|
|
||||||
var resultObject = window.cpSavePanel({
|
|
||||||
isExtensionHidden: _isExtensionHidden,
|
|
||||||
canSelectHiddenExtension: _canSelectHiddenExtension,
|
|
||||||
allowsOtherFileTypes: _allowsOtherFileTypes,
|
|
||||||
canCreateDirectories: _canCreateDirectories,
|
|
||||||
allowedFileTypes: _allowedFileTypes
|
|
||||||
}),
|
|
||||||
result = resultObject.button;
|
|
||||||
|
|
||||||
_URL = result ? [CPURL URLWithString:resultObject.URL] : nil;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// FIXME: This is not the best way to do this.
|
|
||||||
var documentName = window.prompt("Document Name:"),
|
|
||||||
result = documentName !== null;
|
|
||||||
|
|
||||||
_URL = result ? [[self class] proposedFileURLWithDocumentName:documentName] : nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPURL)URL
|
|
||||||
{
|
|
||||||
return _URL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPScreen : CPObject
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)visibleFrame
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
return _CGRectMake(window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight);
|
|
||||||
#else
|
|
||||||
return _CGRectMakeZero();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+199
-598
File diff suppressed because it is too large
Load Diff
+112
-144
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
|
||||||
// CPScroller Constants
|
// CPScroller Constants
|
||||||
CPScrollerNoPart = 0;
|
CPScrollerNoPart = 0;
|
||||||
@@ -39,9 +41,9 @@ CPNoScrollerParts = 0;
|
|||||||
CPOnlyScrollerArrows = 1;
|
CPOnlyScrollerArrows = 1;
|
||||||
CPAllScrollerParts = 2;
|
CPAllScrollerParts = 2;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPScroller
|
@class CPScroller
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
|
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
|
||||||
@@ -62,33 +64,32 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
|
|
||||||
BOOL _isVertical @accessors(readonly, getter=isVertical);
|
BOOL _isVertical @accessors(readonly, getter=isVertical);
|
||||||
float _knobProportion;
|
float _knobProportion;
|
||||||
|
|
||||||
CPScrollerPart _hitPart;
|
CPScrollerPart _hitPart;
|
||||||
|
|
||||||
CPScrollerPart _trackingPart;
|
CPScrollerPart _trackingPart;
|
||||||
float _trackingFloatValue;
|
float _trackingFloatValue;
|
||||||
CGPoint _trackingStartPoint;
|
CGPoint _trackingStartPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)themeClass
|
||||||
{
|
{
|
||||||
return "scroller";
|
return "scroller";
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return [CPDictionary dictionaryWithJSObject:{
|
return [CPDictionary dictionaryWithObjects:[ nil, nil, nil, nil,
|
||||||
@"scroller-width": 15.0,
|
_CGSizeMakeZero(), _CGSizeMakeZero(), _CGInsetMakeZero(), _CGInsetMakeZero(), _CGSizeMakeZero()]
|
||||||
@"knob-slot-color": [CPColor lightGrayColor],
|
forKeys:[ @"knob-slot-color",
|
||||||
@"decrement-line-color": [CPNull null],
|
@"decrement-line-color",
|
||||||
@"increment-line-color": [CPNull null],
|
@"increment-line-color",
|
||||||
@"knob-color": [CPColor grayColor],
|
@"knob-color",
|
||||||
@"decrement-line-size":_CGSizeMakeZero(),
|
@"decrement-line-size",
|
||||||
@"increment-line-size":_CGSizeMakeZero(),
|
@"increment-line-size",
|
||||||
@"track-inset":_CGInsetMakeZero(),
|
@"track-inset",
|
||||||
@"knob-inset": _CGInsetMakeZero(),
|
@"knob-inset",
|
||||||
@"minimum-knob-length":21.0
|
@"minimum-knob-length"]];
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -97,18 +98,17 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_controlSize = CPRegularControlSize;
|
_controlSize = CPRegularControlSize;
|
||||||
_partRects = [];
|
_partRects = [];
|
||||||
|
|
||||||
[self setFloatValue:0.0];
|
[self setFloatValue:0.0 knobProportion:1.0];
|
||||||
[self setKnobProportion:1.0];
|
|
||||||
|
|
||||||
_hitPart = CPScrollerNoPart;
|
_hitPart = CPScrollerNoPart;
|
||||||
|
|
||||||
[self _calculateIsVertical];
|
[self _recalculateIsVertical];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -120,7 +120,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
*/
|
*/
|
||||||
+ (float)scrollerWidth
|
+ (float)scrollerWidth
|
||||||
{
|
{
|
||||||
return [[[CPScroller alloc] init] currentValueForThemeAttribute:@"scroller-width"];
|
return 17.0;//[self scrollerWidthForControlSize:CPRegularControlSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -129,7 +129,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
*/
|
*/
|
||||||
+ (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
|
+ (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
|
||||||
{
|
{
|
||||||
return [self scrollerWidth];
|
return 17.0;//_CPScrollerWidths[aControlSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -155,17 +155,28 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
return _controlSize;
|
return _controlSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setObjectValue:(id)aValue
|
// 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
|
||||||
{
|
{
|
||||||
[super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
|
[super setFloatValue:MIN(1.0, MAX(0.0, aValue))];
|
||||||
|
|
||||||
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setKnobProportion:(float)aProportion
|
/*!
|
||||||
|
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
|
||||||
{
|
{
|
||||||
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setFloatValue:aValue];
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -179,10 +190,10 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
- (id)currentValueForThemeAttribute:(CPString)anAttributeName
|
- (id)currentValueForThemeAttribute:(CPString)anAttributeName
|
||||||
{
|
{
|
||||||
var themeState = _themeState;
|
var themeState = _themeState;
|
||||||
|
|
||||||
if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
|
if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
|
||||||
themeState &= ~CPThemeStateHighlighted;
|
themeState &= ~CPThemeStateHighlighted;
|
||||||
|
|
||||||
return [self valueForThemeAttribute:anAttributeName inState:themeState];
|
return [self valueForThemeAttribute:anAttributeName inState:themeState];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,32 +208,32 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the part of the scroller that would be hit by \c aPoint.
|
Returns the part of the scroller that would be hit by <code>aPoint</code>.
|
||||||
@param aPoint the simulated point hit
|
@param aPoint the simulated point hit
|
||||||
@return the part of the scroller that intersects the point
|
@return the part of the scroller that intersects the point
|
||||||
*/
|
*/
|
||||||
- (CPScrollerPart)testPart:(CGPoint)aPoint
|
- (CPScrollerPart)testPart:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
aPoint = [self convertPoint:aPoint fromView:nil];
|
aPoint = [self convertPoint:aPoint fromView:nil];
|
||||||
|
|
||||||
// The ordering of these tests is important. We check the knob and
|
// The ordering of these tests is important. We check the knob and
|
||||||
// page rects first since they may overlap with the arrows.
|
// page rects first since they may overlap with the arrows.
|
||||||
|
|
||||||
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
|
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
|
||||||
return CPScrollerKnob;
|
return CPScrollerKnob;
|
||||||
|
|
||||||
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint))
|
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint))
|
||||||
return CPScrollerDecrementPage;
|
return CPScrollerDecrementPage;
|
||||||
|
|
||||||
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementPage], aPoint))
|
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementPage], aPoint))
|
||||||
return CPScrollerIncrementPage;
|
return CPScrollerIncrementPage;
|
||||||
|
|
||||||
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementLine], aPoint))
|
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementLine], aPoint))
|
||||||
return CPScrollerDecrementLine;
|
return CPScrollerDecrementLine;
|
||||||
|
|
||||||
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementLine], aPoint))
|
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementLine], aPoint))
|
||||||
return CPScrollerIncrementLine;
|
return CPScrollerIncrementLine;
|
||||||
|
|
||||||
if (CGRectContainsPoint([self rectForPart:CPScrollerKnobSlot], aPoint))
|
if (CGRectContainsPoint([self rectForPart:CPScrollerKnobSlot], aPoint))
|
||||||
return CPScrollerKnobSlot;
|
return CPScrollerKnobSlot;
|
||||||
|
|
||||||
@@ -240,16 +251,16 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
if (_knobProportion === 1.0)
|
if (_knobProportion === 1.0)
|
||||||
{
|
{
|
||||||
_usableParts = CPNoScrollerParts;
|
_usableParts = CPNoScrollerParts;
|
||||||
|
|
||||||
_partRects[CPScrollerDecrementPage] = CGRectMakeZero();
|
_partRects[CPScrollerDecrementPage] = _CGRectMakeZero();
|
||||||
_partRects[CPScrollerKnob] = CGRectMakeZero();
|
_partRects[CPScrollerKnob] = _CGRectMakeZero();
|
||||||
_partRects[CPScrollerIncrementPage] = CGRectMakeZero();
|
_partRects[CPScrollerIncrementPage] = _CGRectMakeZero();
|
||||||
_partRects[CPScrollerDecrementLine] = CGRectMakeZero();
|
_partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
|
||||||
_partRects[CPScrollerIncrementLine] = CGRectMakeZero();
|
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
|
||||||
|
|
||||||
// In this case, the slot is the entirety of the scroller.
|
// In this case, the slot is the entirety of the scroller.
|
||||||
_partRects[CPScrollerKnobSlot] = CGRectMakeCopy(bounds);
|
_partRects[CPScrollerKnobSlot] = _CGRectMakeCopy(bounds);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,8 +270,8 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
var knobInset = [self currentValueForThemeAttribute:@"knob-inset"],
|
var knobInset = [self currentValueForThemeAttribute:@"knob-inset"],
|
||||||
trackInset = [self currentValueForThemeAttribute:@"track-inset"],
|
trackInset = [self currentValueForThemeAttribute:@"track-inset"],
|
||||||
width = _CGRectGetWidth(bounds),
|
width = _CGRectGetWidth(bounds),
|
||||||
height = _CGRectGetHeight(bounds);
|
height = _CGRectGetHeight(bounds);
|
||||||
|
|
||||||
if ([self isVertical])
|
if ([self isVertical])
|
||||||
{
|
{
|
||||||
var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
|
var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
|
||||||
@@ -278,24 +289,15 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
_partRects[CPScrollerIncrementPage] = _CGRectMake(0.0, knobLocation + knobHeight, width, height - (knobLocation + knobHeight) - effectiveIncrementLineHeight);
|
_partRects[CPScrollerIncrementPage] = _CGRectMake(0.0, knobLocation + knobHeight, width, height - (knobLocation + knobHeight) - effectiveIncrementLineHeight);
|
||||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, effectiveDecrementLineHeight, width - trackInset.left - trackInset.right, slotHeight);
|
_partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, effectiveDecrementLineHeight, width - trackInset.left - trackInset.right, slotHeight);
|
||||||
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
|
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
|
||||||
_partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
|
_partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
|
||||||
|
|
||||||
if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
|
|
||||||
_partRects[CPScrollerKnob] = _CGRectMakeZero();
|
|
||||||
|
|
||||||
if (height < decrementLineSize.height + incrementLineSize.height - 2)
|
|
||||||
{
|
|
||||||
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
|
|
||||||
_partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
|
|
||||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, 0, width - trackInset.left - trackInset.right, height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
|
var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
|
||||||
incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
|
incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
|
||||||
effectiveDecrementLineWidth = decrementLineSize.width + trackInset.left,
|
effectiveDecrementLineWidth = decrementLineSize.width + trackInset.left,
|
||||||
effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
|
effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right;
|
||||||
slotWidth = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
|
slotWidth = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
|
||||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||||
knobWidth = MAX(minimumKnobLength, (slotWidth * _knobProportion)),
|
knobWidth = MAX(minimumKnobLength, (slotWidth * _knobProportion)),
|
||||||
@@ -308,16 +310,6 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotWidth, height - trackInset.top - trackInset.bottom);
|
_partRects[CPScrollerKnobSlot] = _CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotWidth, height - trackInset.top - trackInset.bottom);
|
||||||
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
|
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
|
||||||
_partRects[CPScrollerIncrementLine] = _CGRectMake(width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
|
_partRects[CPScrollerIncrementLine] = _CGRectMake(width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
|
||||||
|
|
||||||
if (width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
|
|
||||||
_partRects[CPScrollerKnob] = _CGRectMakeZero();
|
|
||||||
|
|
||||||
if (width < decrementLineSize.width + incrementLineSize.width - 2)
|
|
||||||
{
|
|
||||||
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
|
|
||||||
_partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
|
|
||||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(0.0, 0.0, width, slotHeight);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,9 +349,9 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
- (CPView)createViewForPart:(CPScrollerPart)aPart
|
- (CPView)createViewForPart:(CPScrollerPart)aPart
|
||||||
{
|
{
|
||||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
[view setHitTests:NO];
|
[view setHitTests:NO];
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +365,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
[view setHitTests:NO];
|
[view setHitTests:NO];
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,12 +379,12 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
{
|
{
|
||||||
var part = PARTS_ARRANGEMENT[index];
|
var part = PARTS_ARRANGEMENT[index];
|
||||||
|
|
||||||
if (index === 0)
|
if (index === 0)
|
||||||
view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowBelow relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index + 1]];
|
view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowBelow relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index + 1]];
|
||||||
else
|
else
|
||||||
view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowAbove relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index - 1]];
|
view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowAbove relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index - 1]];
|
||||||
|
|
||||||
if (view)
|
if (view)
|
||||||
[view setBackgroundColor:[self currentValueForThemeAttribute:NAMES_FOR_PARTS[part] + "-color"]];
|
[view setBackgroundColor:[self currentValueForThemeAttribute:NAMES_FOR_PARTS[part] + "-color"]];
|
||||||
}
|
}
|
||||||
@@ -402,7 +394,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
Caches images for the scroll arrow and knob.
|
Caches images for the scroll arrow and knob.
|
||||||
*/
|
*/
|
||||||
- (void)drawParts
|
- (void)drawParts
|
||||||
{
|
{
|
||||||
[self drawKnobSlot];
|
[self drawKnobSlot];
|
||||||
[self drawKnob];
|
[self drawKnob];
|
||||||
[self drawArrow:CPScrollerDecrementArrow highlight:NO];
|
[self drawArrow:CPScrollerDecrementArrow highlight:NO];
|
||||||
@@ -425,37 +417,37 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
- (void)trackKnob:(CPEvent)anEvent
|
- (void)trackKnob:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var type = [anEvent type];
|
var type = [anEvent type];
|
||||||
|
|
||||||
if (type === CPLeftMouseUp)
|
if (type === CPLeftMouseUp)
|
||||||
{
|
{
|
||||||
_hitPart = CPScrollerNoPart;
|
_hitPart = CPScrollerNoPart;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === CPLeftMouseDown)
|
if (type === CPLeftMouseDown)
|
||||||
{
|
{
|
||||||
_trackingFloatValue = [self floatValue];
|
_trackingFloatValue = [self floatValue];
|
||||||
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type === CPLeftMouseDragged)
|
else if (type === CPLeftMouseDragged)
|
||||||
{
|
{
|
||||||
var knobRect = [self rectForPart:CPScrollerKnob],
|
var knobRect = [self rectForPart:CPScrollerKnob],
|
||||||
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
|
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
|
||||||
remainder = ![self isVertical] ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
|
remainder = ![self isVertical] ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
|
||||||
|
|
||||||
if (remainder <= 0)
|
if (remainder <= 0)
|
||||||
[self setFloatValue:0.0];
|
[self setFloatValue:0.0];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
delta = ![self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
|
delta = ![self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
|
||||||
|
|
||||||
[self setFloatValue:_trackingFloatValue + delta / remainder];
|
[self setFloatValue:_trackingFloatValue + delta / remainder];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
[CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||||
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
[self sendAction:[self action] to:[self target]];
|
||||||
@@ -473,26 +465,26 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
{
|
{
|
||||||
[self highlight:NO];
|
[self highlight:NO];
|
||||||
[CPEvent stopPeriodicEvents];
|
[CPEvent stopPeriodicEvents];
|
||||||
|
|
||||||
_hitPart = CPScrollerNoPart;
|
_hitPart = CPScrollerNoPart;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === CPLeftMouseDown)
|
if (type === CPLeftMouseDown)
|
||||||
{
|
{
|
||||||
_trackingPart = [self hitPart];
|
_trackingPart = [self hitPart];
|
||||||
|
|
||||||
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
|
|
||||||
if ([anEvent modifierFlags] & CPAlternateKeyMask)
|
if ([anEvent modifierFlags] & CPAlternateKeyMask)
|
||||||
{
|
{
|
||||||
if (_trackingPart == CPScrollerDecrementLine)
|
if (_trackingPart == CPScrollerDecrementLine)
|
||||||
_hitPart = CPScrollerDecrementPage;
|
_hitPart = CPScrollerDecrementPage;
|
||||||
|
|
||||||
else if (_trackingPart == CPScrollerIncrementLine)
|
else if (_trackingPart == CPScrollerIncrementLine)
|
||||||
_hitPart = CPScrollerIncrementPage;
|
_hitPart = CPScrollerIncrementPage;
|
||||||
|
|
||||||
else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
|
else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
|
||||||
{
|
{
|
||||||
var knobRect = [self rectForPart:CPScrollerKnob],
|
var knobRect = [self rectForPart:CPScrollerKnob],
|
||||||
@@ -501,55 +493,55 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
remainder = (![self isVertical] ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
|
remainder = (![self isVertical] ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
|
||||||
|
|
||||||
[self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
|
[self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
|
||||||
|
|
||||||
_hitPart = CPScrollerKnob;
|
_hitPart = CPScrollerKnob;
|
||||||
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
[self sendAction:[self action] to:[self target]];
|
||||||
|
|
||||||
// Now just track the knob.
|
// Now just track the knob.
|
||||||
return [self trackKnob:anEvent];
|
return [self trackKnob:anEvent];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self highlight:YES];
|
[self highlight:YES];
|
||||||
[self sendAction:[self action] to:[self target]];
|
[self sendAction:[self action] to:[self target]];
|
||||||
|
|
||||||
[CPEvent startPeriodicEventsAfterDelay:0.5 withPeriod:0.04];
|
[CPEvent startPeriodicEventsAfterDelay:0.5 withPeriod:0.04];
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type === CPLeftMouseDragged)
|
else if (type === CPLeftMouseDragged)
|
||||||
{
|
{
|
||||||
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
|
|
||||||
if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
|
if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
|
||||||
{
|
{
|
||||||
var hitPart = [self testPart:[anEvent locationInWindow]];
|
var hitPart = [self testPart:[anEvent locationInWindow]];
|
||||||
|
|
||||||
if (hitPart == CPScrollerDecrementPage || hitPart == CPScrollerIncrementPage)
|
if (hitPart == CPScrollerDecrementPage || hitPart == CPScrollerIncrementPage)
|
||||||
{
|
{
|
||||||
_trackingPart = hitPart;
|
_trackingPart = hitPart;
|
||||||
_hitPart = hitPart;
|
_hitPart = hitPart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self highlight:CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)];
|
[self highlight:CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)];
|
||||||
}
|
}
|
||||||
else if (type == CPPeriodic && CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint))
|
else if (type == CPPeriodic && CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint))
|
||||||
[self sendAction:[self action] to:[self target]];
|
[self sendAction:[self action] to:[self target]];
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
[CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_calculateIsVertical
|
- (void)_recalculateIsVertical
|
||||||
{
|
{
|
||||||
// Recalculate isVertical.
|
// Recalculate isVertical.
|
||||||
var bounds = [self bounds],
|
var bounds = [self bounds],
|
||||||
width = _CGRectGetWidth(bounds),
|
width = _CGRectGetWidth(bounds),
|
||||||
height = _CGRectGetHeight(bounds);
|
height = _CGRectGetHeight(bounds);
|
||||||
|
|
||||||
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
||||||
|
|
||||||
if (_isVertical === 1)
|
if (_isVertical === 1)
|
||||||
[self setThemeState:CPThemeStateVertical];
|
[self setThemeState:CPThemeStateVertical];
|
||||||
else if (_isVertical === 0)
|
else if (_isVertical === 0)
|
||||||
@@ -560,6 +552,8 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
{
|
{
|
||||||
[super setFrameSize:aSize];
|
[super setFrameSize:aSize];
|
||||||
|
|
||||||
|
[self _recalculateIsVertical];
|
||||||
|
|
||||||
[self checkSpaceForParts];
|
[self checkSpaceForParts];
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
@@ -568,15 +562,15 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
|||||||
{
|
{
|
||||||
if (![self isEnabled])
|
if (![self isEnabled])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_hitPart = [self testPart:[anEvent locationInWindow]];
|
_hitPart = [self testPart:[anEvent locationInWindow]];
|
||||||
|
|
||||||
switch (_hitPart)
|
switch (_hitPart)
|
||||||
{
|
{
|
||||||
case CPScrollerKnob: return [self trackKnob:anEvent];
|
case CPScrollerKnob: return [self trackKnob:anEvent];
|
||||||
|
|
||||||
case CPScrollerDecrementLine:
|
case CPScrollerDecrementLine:
|
||||||
case CPScrollerIncrementLine:
|
case CPScrollerIncrementLine:
|
||||||
case CPScrollerDecrementPage:
|
case CPScrollerDecrementPage:
|
||||||
case CPScrollerIncrementPage: return [self trackScrollButtons:anEvent];
|
case CPScrollerIncrementPage: return [self trackScrollButtons:anEvent];
|
||||||
}
|
}
|
||||||
@@ -596,55 +590,29 @@ var CPScrollerControlSizeKey = "CPScrollerControlSize",
|
|||||||
_controlSize = CPRegularControlSize;
|
_controlSize = CPRegularControlSize;
|
||||||
if ([aCoder containsValueForKey:CPScrollerControlSizeKey])
|
if ([aCoder containsValueForKey:CPScrollerControlSizeKey])
|
||||||
_controlSize = [aCoder decodeIntForKey:CPScrollerControlSizeKey];
|
_controlSize = [aCoder decodeIntForKey:CPScrollerControlSizeKey];
|
||||||
|
|
||||||
_knobProportion = 1.0;
|
_knobProportion = 1.0;
|
||||||
if ([aCoder containsValueForKey:CPScrollerKnobProportionKey])
|
if ([aCoder containsValueForKey:CPScrollerKnobProportionKey])
|
||||||
_knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
|
_knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
|
||||||
|
|
||||||
_partRects = [];
|
_partRects = [];
|
||||||
|
|
||||||
_hitPart = CPScrollerNoPart;
|
_hitPart = CPScrollerNoPart;
|
||||||
|
|
||||||
[self _calculateIsVertical];
|
[self _recalculateIsVertical];
|
||||||
|
// [self checkSpaceForParts];
|
||||||
// Adjust the size of the scroller if the size from cib
|
// [self setNeedsLayout];
|
||||||
// isn't equal to the scrollerWidth
|
|
||||||
var frame = [self frame],
|
|
||||||
scrollerWidth = [CPScroller scrollerWidth];
|
|
||||||
|
|
||||||
if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
|
|
||||||
frame.size.width = scrollerWidth;
|
|
||||||
|
|
||||||
if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
|
|
||||||
frame.size.height = scrollerWidth;
|
|
||||||
|
|
||||||
[self setFrame:frame];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
[aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
|
[aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
|
||||||
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
|
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@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
|
|
||||||
|
|||||||
@@ -1,821 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "CPButton.j"
|
|
||||||
@import "CPTextField.j"
|
|
||||||
|
|
||||||
|
|
||||||
CPSearchFieldRecentsTitleMenuItemTag = 1000;
|
|
||||||
CPSearchFieldRecentsMenuItemTag = 1001;
|
|
||||||
CPSearchFieldClearRecentsMenuItemTag = 1002;
|
|
||||||
CPSearchFieldNoRecentsMenuItemTag = 1003;
|
|
||||||
CPSearchFieldSeparatorMenuItemTag = 1004;
|
|
||||||
|
|
||||||
var CPSearchFieldSearchImage = nil,
|
|
||||||
CPSearchFieldFindImage = nil,
|
|
||||||
CPSearchFieldCancelImage = nil,
|
|
||||||
CPSearchFieldCancelPressedImage = nil;
|
|
||||||
|
|
||||||
var SEARCH_BUTTON_DEFAULT_WIDTH = 25.0,
|
|
||||||
CANCEL_BUTTON_DEFAULT_WIDTH = 22.0,
|
|
||||||
BUTTON_DEFAULT_HEIGHT = 22.0;
|
|
||||||
|
|
||||||
var RECENT_SEARCH_PREFIX = @" ";
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@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;
|
|
||||||
BOOL _canResignFirstResponder;
|
|
||||||
CPTimer _partialStringTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return @"searchfield"
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self != [CPSearchField class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var bundle = [CPBundle bundleForClass:self];
|
|
||||||
CPSearchFieldSearchImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldSearch.png"] size:_CGSizeMake(SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
|
|
||||||
CPSearchFieldFindImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldFind.png"] size:_CGSizeMake(SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
|
|
||||||
CPSearchFieldCancelImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancel.png"] size:_CGSizeMake(CANCEL_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
|
|
||||||
CPSearchFieldCancelPressedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancelPressed.png"] size:_CGSizeMake(CANCEL_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:frame])
|
|
||||||
{
|
|
||||||
_maximumRecents = 10;
|
|
||||||
_sendsWholeSearchString = NO;
|
|
||||||
_sendsSearchStringImmediately = NO;
|
|
||||||
_recentsAutosaveName = nil;
|
|
||||||
|
|
||||||
[self _init];
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
_cancelButton._DOMElement.style.cursor = "default";
|
|
||||||
_searchButton._DOMElement.style.cursor = "default";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
_recentSearches = [CPArray array];
|
|
||||||
|
|
||||||
[self setBezeled:YES];
|
|
||||||
[self setBezelStyle:CPTextFieldRoundedBezel];
|
|
||||||
[self setBordered:YES];
|
|
||||||
[self setEditable:YES];
|
|
||||||
[self setDelegate:self];
|
|
||||||
[self setContinuous:YES];
|
|
||||||
|
|
||||||
var bounds = [self bounds],
|
|
||||||
cancelButton = [[CPButton alloc] initWithFrame:[self cancelButtonRectForBounds:bounds]],
|
|
||||||
searchButton = [[CPButton alloc] initWithFrame:[self searchButtonRectForBounds:bounds]];
|
|
||||||
|
|
||||||
[self setCancelButton:cancelButton];
|
|
||||||
[self resetCancelButton];
|
|
||||||
|
|
||||||
[self setSearchButton:searchButton];
|
|
||||||
[self resetSearchButton];
|
|
||||||
|
|
||||||
_canResignFirstResponder = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Managing Buttons
|
|
||||||
/*!
|
|
||||||
Sets the button used to display the search-button image
|
|
||||||
@param button The search button.
|
|
||||||
*/
|
|
||||||
- (void)setSearchButton:(CPButton)button
|
|
||||||
{
|
|
||||||
if (button != _searchButton)
|
|
||||||
{
|
|
||||||
[_searchButton removeFromSuperview];
|
|
||||||
_searchButton = button;
|
|
||||||
|
|
||||||
[_searchButton setFrame:[self searchButtonRectForBounds:[self bounds]]];
|
|
||||||
[_searchButton setAutoresizingMask:CPViewMaxXMargin];
|
|
||||||
[self addSubview:_searchButton];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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 button = [self searchButton],
|
|
||||||
searchButtonImage = (_searchMenuTemplate === nil) ? CPSearchFieldSearchImage : CPSearchFieldFindImage;
|
|
||||||
|
|
||||||
[button setBordered:NO];
|
|
||||||
[button setImageScaling:CPScaleToFit];
|
|
||||||
[button setImage:searchButtonImage];
|
|
||||||
[button setAutoresizingMask:CPViewMaxXMargin];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the button object used to display the cancel-button image.
|
|
||||||
@param button The cancel button.
|
|
||||||
*/
|
|
||||||
- (void)setCancelButton:(CPButton)button
|
|
||||||
{
|
|
||||||
if (button != _cancelButton)
|
|
||||||
{
|
|
||||||
[_cancelButton removeFromSuperview];
|
|
||||||
_cancelButton = button;
|
|
||||||
|
|
||||||
[_cancelButton setFrame:[self cancelButtonRectForBounds:[self bounds]]];
|
|
||||||
[_cancelButton setAutoresizingMask:CPViewMinXMargin];
|
|
||||||
[_cancelButton setTarget:self];
|
|
||||||
[_cancelButton setAction:@selector(_searchFieldCancel:)];
|
|
||||||
[self _updateCancelButtonVisibility];
|
|
||||||
[self addSubview:_cancelButton];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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 setBordered:NO];
|
|
||||||
[button setImageScaling:CPScaleToFit];
|
|
||||||
[button setImage:CPSearchFieldCancelImage];
|
|
||||||
[button setAlternateImage:CPSearchFieldCancelPressedImage];
|
|
||||||
[button setAutoresizingMask:CPViewMinXMargin];
|
|
||||||
[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.
|
|
||||||
*/
|
|
||||||
- (CGRect)searchTextRectForBounds:(CGRect)rect
|
|
||||||
{
|
|
||||||
var leftOffset = 0,
|
|
||||||
width = _CGRectGetWidth(rect),
|
|
||||||
bounds = [self bounds];
|
|
||||||
|
|
||||||
if (_searchButton)
|
|
||||||
{
|
|
||||||
var searchBounds = [self searchButtonRectForBounds:bounds];
|
|
||||||
leftOffset = _CGRectGetMaxX(searchBounds) + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_cancelButton)
|
|
||||||
{
|
|
||||||
var cancelRect = [self cancelButtonRectForBounds:bounds];
|
|
||||||
width = _CGRectGetMinX(cancelRect) - leftOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _CGRectMake(leftOffset, _CGRectGetMinY(rect), width, _CGRectGetHeight(rect));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
- (CGRect)searchButtonRectForBounds:(CGRect)rect
|
|
||||||
{
|
|
||||||
return _CGRectMake(5, (_CGRectGetHeight(rect) - BUTTON_DEFAULT_HEIGHT) / 2, SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
- (CGRect)cancelButtonRectForBounds:(CGRect)rect
|
|
||||||
{
|
|
||||||
return _CGRectMake(_CGRectGetWidth(rect) - CANCEL_BUTTON_DEFAULT_WIDTH - 5, (_CGRectGetHeight(rect) - CANCEL_BUTTON_DEFAULT_WIDTH) / 2, BUTTON_DEFAULT_HEIGHT, BUTTON_DEFAULT_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)aMenu
|
|
||||||
{
|
|
||||||
_searchMenuTemplate = aMenu;
|
|
||||||
|
|
||||||
[self resetSearchButton];
|
|
||||||
[self _loadRecentSearchList];
|
|
||||||
[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]),
|
|
||||||
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
|
|
||||||
{
|
|
||||||
if (_recentsAutosaveName != nil)
|
|
||||||
[self _deregisterForAutosaveNotification];
|
|
||||||
|
|
||||||
_recentsAutosaveName = name;
|
|
||||||
|
|
||||||
if (_recentsAutosaveName != nil)
|
|
||||||
[self _registerForAutosaveNotification];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private methods and subclassing
|
|
||||||
|
|
||||||
- (CGRect)contentRectForBounds:(CGRect)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 (![self sendsWholeSearchString])
|
|
||||||
{
|
|
||||||
if ([self 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:(id)sender
|
|
||||||
{
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
|
||||||
{
|
|
||||||
[super sendAction:anAction to:anObject];
|
|
||||||
|
|
||||||
[_partialStringTimer invalidate];
|
|
||||||
|
|
||||||
[self _addStringToRecentSearches:[self stringValue]];
|
|
||||||
[self _updateCancelButtonVisibility];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_addStringToRecentSearches:(CPString)string
|
|
||||||
{
|
|
||||||
if (string === nil || string === @"" || [_recentSearches containsObject:string])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var searches = [CPMutableArray arrayWithArray:_recentSearches];
|
|
||||||
[searches addObject:string];
|
|
||||||
[self setRecentSearches:searches];
|
|
||||||
[self _updateSearchMenu];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)hitTest:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
// Make sure a hit anywhere within the search field returns the search field itself
|
|
||||||
if (_CGRectContainsPoint([self frame], aPoint))
|
|
||||||
return self;
|
|
||||||
else
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)resignFirstResponder
|
|
||||||
{
|
|
||||||
return _canResignFirstResponder && [super resignFirstResponder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
var location = [anEvent locationInWindow],
|
|
||||||
point = [self convertPoint:location fromView:nil];
|
|
||||||
|
|
||||||
if (_CGRectContainsPoint([self searchButtonRectForBounds:[self bounds]], point))
|
|
||||||
{
|
|
||||||
if (_searchMenuTemplate == nil)
|
|
||||||
[self _sendAction:self];
|
|
||||||
else
|
|
||||||
[self _showMenu];
|
|
||||||
}
|
|
||||||
else if (_CGRectContainsPoint([self cancelButtonRectForBounds:[self bounds]], point))
|
|
||||||
[_cancelButton mouseDown:anEvent];
|
|
||||||
else
|
|
||||||
[super mouseDown:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Provides the common case items for a recent searches menu. If there are not recent searches,
|
|
||||||
displays a single disabled item:
|
|
||||||
|
|
||||||
No Recent Searches
|
|
||||||
|
|
||||||
If there are 1 more recent searches, it displays:
|
|
||||||
|
|
||||||
Recent Searches
|
|
||||||
recent search 1
|
|
||||||
recent search 2
|
|
||||||
etc.
|
|
||||||
---------------------
|
|
||||||
Clear Recent Searches
|
|
||||||
|
|
||||||
If you wish to add items before or after the template, you can. If you put items
|
|
||||||
before, a separator will automatically be placed before the default template item.
|
|
||||||
If you add items after the default template, it is your responsibility to add a separator.
|
|
||||||
|
|
||||||
To add a custom item:
|
|
||||||
|
|
||||||
item = [[CPMenuItem alloc] initWithTitle:@"google"
|
|
||||||
action:@selector(google:)
|
|
||||||
keyEquivalent:@""];
|
|
||||||
[item setTag:700];
|
|
||||||
[item setTarget:self];
|
|
||||||
[template addItem:item];
|
|
||||||
|
|
||||||
Be sure that your custom items do not use tags in the range 1000-1004 inclusive.
|
|
||||||
If you wish to maintain state in custom menu items that you add, you will need to maintain
|
|
||||||
the item state yourself, then in the action method of the custom items, modify the items
|
|
||||||
in the search menu template and send [searchField setSearchMenuTemplate:template] to update the menu.
|
|
||||||
*/
|
|
||||||
- (CPMenu)defaultSearchMenuTemplate
|
|
||||||
{
|
|
||||||
var template = [[CPMenu alloc] init],
|
|
||||||
item;
|
|
||||||
|
|
||||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent Searches"
|
|
||||||
action:nil
|
|
||||||
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:nil
|
|
||||||
keyEquivalent:@""];
|
|
||||||
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
|
|
||||||
[item setEnabled:NO];
|
|
||||||
[template addItem:item];
|
|
||||||
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateSearchMenu
|
|
||||||
{
|
|
||||||
if (_searchMenuTemplate === nil)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var menu = [[CPMenu alloc] init],
|
|
||||||
countOfRecents = [_recentSearches count],
|
|
||||||
numberOfItems = [_searchMenuTemplate numberOfItems];
|
|
||||||
|
|
||||||
for (var i = 0; i < numberOfItems; i++)
|
|
||||||
{
|
|
||||||
var item = [[_searchMenuTemplate itemAtIndex:i] copy];
|
|
||||||
|
|
||||||
switch ([item tag])
|
|
||||||
{
|
|
||||||
case CPSearchFieldRecentsTitleMenuItemTag:
|
|
||||||
if (countOfRecents === 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ([menu numberOfItems] > 0)
|
|
||||||
[self _addSeparatorToMenu:menu];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPSearchFieldRecentsMenuItemTag:
|
|
||||||
{
|
|
||||||
var itemAction = @selector(_searchFieldSearch:);
|
|
||||||
|
|
||||||
for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
|
|
||||||
{
|
|
||||||
// RECENT_SEARCH_PREFIX is a hack until CPMenuItem -setIndentationLevel works
|
|
||||||
var recentItem = [[CPMenuItem alloc] initWithTitle:RECENT_SEARCH_PREFIX + [_recentSearches objectAtIndex:recentIndex]
|
|
||||||
action:itemAction
|
|
||||||
keyEquivalent:[item keyEquivalent]];
|
|
||||||
[item setTarget:self];
|
|
||||||
[menu addItem:recentItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CPSearchFieldClearRecentsMenuItemTag:
|
|
||||||
if (countOfRecents === 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ([menu numberOfItems] > 0)
|
|
||||||
[self _addSeparatorToMenu:menu];
|
|
||||||
|
|
||||||
[item setAction:@selector(_searchFieldClearRecents:)];
|
|
||||||
[item setTarget:self];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPSearchFieldNoRecentsMenuItemTag:
|
|
||||||
if (countOfRecents !== 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ([menu numberOfItems] > 0)
|
|
||||||
[self _addSeparatorToMenu:menu];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPSearchFieldSeparatorMenuItemTag:
|
|
||||||
item = [CPMenuItem separatorItem];
|
|
||||||
[item setEnabled:NO];
|
|
||||||
[menu addItem:item];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[item setEnabled:([item isEnabled] && [item action] != nil && [item target] != nil)];
|
|
||||||
[menu addItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
[menu setDelegate:self];
|
|
||||||
|
|
||||||
_searchMenu = menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_addSeparatorToMenu:(CPMenu)aMenu
|
|
||||||
{
|
|
||||||
var separator = [CPMenuItem separatorItem];
|
|
||||||
[separator setEnabled:NO];
|
|
||||||
[aMenu addItem:separator];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)menuWillOpen:(CPMenu)menu
|
|
||||||
{
|
|
||||||
_canResignFirstResponder = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)menuDidClose:(CPMenu)menu
|
|
||||||
{
|
|
||||||
_canResignFirstResponder = YES;
|
|
||||||
|
|
||||||
[self becomeFirstResponder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_showMenu
|
|
||||||
{
|
|
||||||
if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var aFrame = [[self superview] convertRect:[self frame] toView:nil],
|
|
||||||
location = CPMakePoint(aFrame.origin.x + 10, aFrame.origin.y + aFrame.size.height - 4);
|
|
||||||
|
|
||||||
var anEvent = [CPEvent mouseEventWithType:CPRightMouseDown location:location modifierFlags:0 timestamp:[[CPApp currentEvent] timestamp] windowNumber:[[self window] windowNumber] context:nil eventNumber:1 clickCount:1 pressure:0];
|
|
||||||
|
|
||||||
[self selectAll:nil];
|
|
||||||
[CPMenu popUpContextMenu:_searchMenu withEvent:anEvent forView:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_sendPartialString
|
|
||||||
{
|
|
||||||
[super sendAction:[self action] to:[self target]];
|
|
||||||
[_partialStringTimer invalidate];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_searchFieldCancel:(id)sender
|
|
||||||
{
|
|
||||||
[self setObjectValue:@""];
|
|
||||||
[self _sendPartialString];
|
|
||||||
[self _updateCancelButtonVisibility];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_searchFieldSearch:(id)sender
|
|
||||||
{
|
|
||||||
var searchString = [[sender title] substringFromIndex:[RECENT_SEARCH_PREFIX length]];
|
|
||||||
|
|
||||||
if ([sender tag] != CPSearchFieldRecentsMenuItemTag)
|
|
||||||
[self _addStringToRecentSearches:searchString];
|
|
||||||
|
|
||||||
[self setObjectValue:searchString];
|
|
||||||
[self _sendPartialString];
|
|
||||||
[self selectAll:nil];
|
|
||||||
|
|
||||||
[self _updateCancelButtonVisibility];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_searchFieldClearRecents:(id)sender
|
|
||||||
{
|
|
||||||
[self setRecentSearches:[CPArray array]];
|
|
||||||
[self _updateSearchMenu];
|
|
||||||
[self setStringValue:@""];
|
|
||||||
[self _updateCancelButtonVisibility];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_registerForAutosaveNotification
|
|
||||||
{
|
|
||||||
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateAutosavedRecents:) name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_deregisterForAutosaveNotification
|
|
||||||
{
|
|
||||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_autosaveRecentSearchList
|
|
||||||
{
|
|
||||||
if (_recentsAutosaveName != nil)
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:@"CPAutosavedRecentsChangedNotification" object:_recentsAutosaveName];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateAutosavedRecents:(id)notification
|
|
||||||
{
|
|
||||||
var list = [self recentSearches],
|
|
||||||
name = [notification object],
|
|
||||||
bundle_name = [[[CPBundle mainBundle] infoDictionary] objectForKey:"CPBundleName"],
|
|
||||||
cookie_name = [bundle_name lowercaseString] + "." + [notification object],
|
|
||||||
|
|
||||||
cookie = [[CPCookie alloc] initWithName:cookie_name],
|
|
||||||
cookie_value = [list componentsJoinedByString:@","];
|
|
||||||
|
|
||||||
[cookie setValue:cookie_value
|
|
||||||
expires:[[CPDate alloc] initWithTimeIntervalSinceNow:3600*24*365]
|
|
||||||
domain:(window.location.href.hostname)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_loadRecentSearchList
|
|
||||||
{
|
|
||||||
var list,
|
|
||||||
name = [self recentsAutosaveName];
|
|
||||||
|
|
||||||
if (name === nil)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var bundle_name = [[[CPBundle mainBundle] infoDictionary] objectForKey:"CPBundleName"],
|
|
||||||
cookie_name = [bundle_name lowercaseString] + "." + name,
|
|
||||||
|
|
||||||
cookie = [[CPCookie alloc] initWithName:cookie_name];
|
|
||||||
|
|
||||||
if (cookie != nil)
|
|
||||||
{
|
|
||||||
var cookie_value = [cookie value];
|
|
||||||
list = (cookie_value != @"") ? [cookie_value componentsSeparatedByString:@","] : [CPArray array];
|
|
||||||
_recentSearches = list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
|
|
||||||
CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
|
|
||||||
CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
|
|
||||||
CPMaximumRecentsKey = @"CPMaximumRecentsKey",
|
|
||||||
CPSearchMenuTemplateKey = @"CPSearchMenuTemplateKey";
|
|
||||||
|
|
||||||
@implementation CPSearchField (CPCoding)
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)coder
|
|
||||||
{
|
|
||||||
[_searchButton removeFromSuperview];
|
|
||||||
[_cancelButton removeFromSuperview];
|
|
||||||
|
|
||||||
[super encodeWithCoder:coder];
|
|
||||||
|
|
||||||
if (_searchButton)
|
|
||||||
[self addSubview:_searchButton];
|
|
||||||
if (_cancelButton)
|
|
||||||
[self addSubview:_cancelButton];
|
|
||||||
|
|
||||||
[coder encodeBool:_sendsWholeSearchString forKey:CPSendsWholeSearchStringKey];
|
|
||||||
[coder encodeBool:_sendsSearchStringImmediately forKey:CPSendsSearchStringImmediatelyKey];
|
|
||||||
[coder encodeInt:_maximumRecents forKey:CPMaximumRecentsKey];
|
|
||||||
|
|
||||||
if (_recentsAutosaveName)
|
|
||||||
[coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
|
|
||||||
|
|
||||||
if (_searchMenuTemplate)
|
|
||||||
[coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)coder
|
|
||||||
{
|
|
||||||
if (self = [super initWithCoder:coder])
|
|
||||||
{
|
|
||||||
_recentsAutosaveName = [coder decodeObjectForKey:CPRecentsAutosaveNameKey];
|
|
||||||
_sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
|
|
||||||
_sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
|
|
||||||
_maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
|
|
||||||
|
|
||||||
var template = [coder decodeObjectForKey:CPSearchMenuTemplateKey];
|
|
||||||
|
|
||||||
if (template)
|
|
||||||
[self setSearchMenuTemplate:template];
|
|
||||||
|
|
||||||
[self _init];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
@import "CPTextField.j"
|
@import "CPTextField.j"
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
*/
|
*/
|
||||||
|
|||||||
+186
-211
@@ -24,12 +24,25 @@
|
|||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPSegmentSwitchTracking
|
||||||
|
*/
|
||||||
CPSegmentSwitchTrackingSelectOne = 0;
|
CPSegmentSwitchTrackingSelectOne = 0;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPSegmentSwitchTracking
|
||||||
|
*/
|
||||||
CPSegmentSwitchTrackingSelectAny = 1;
|
CPSegmentSwitchTrackingSelectAny = 1;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@group CPSegmentSwitchTracking
|
||||||
|
*/
|
||||||
CPSegmentSwitchTrackingMomentary = 2;
|
CPSegmentSwitchTrackingMomentary = 2;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPSegmentedControl
|
@class CPSegmentedControl
|
||||||
|
|
||||||
@@ -48,14 +61,14 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
BOOL _trackingHighlighted;
|
BOOL _trackingHighlighted;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)themeClass
|
||||||
{
|
{
|
||||||
return "segmented-control";
|
return "segmented-control";
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return [CPDictionary dictionaryWithObjects:[CPCenterTextAlignment, CPCenterVerticalTextAlignment, CPImageLeft, CPScaleNone, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null], 1.0, 24.0]
|
return [CPDictionary dictionaryWithObjects:[CPCenterTextAlignment, CPCenterVerticalTextAlignment, CPImageLeft, CPScaleNone, _CGInsetMakeZero(), _CGInsetMakeZero(), nil, nil, nil, nil, 1.0, 24.0]
|
||||||
forKeys:[@"alignment", @"vertical-alignment", @"image-position", @"image-scaling", @"bezel-inset", @"content-inset", @"left-segment-bezel-color", @"right-segment-bezel-color", @"center-segment-bezel-color", @"divider-bezel-color", @"divider-thickness", @"default-height"]];
|
forKeys:[@"alignment", @"vertical-alignment", @"image-position", @"image-scaling", @"bezel-inset", @"content-inset", @"left-segment-bezel-color", @"right-segment-bezel-color", @"center-segment-bezel-color", @"divider-bezel-color", @"divider-thickness", @"default-height"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,16 +76,16 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
{
|
{
|
||||||
_segments = [];
|
_segments = [];
|
||||||
_themeStates = [];
|
_themeStates = [];
|
||||||
|
|
||||||
self = [super initWithFrame:aRect];
|
self = [super initWithFrame:aRect];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_selectedSegment = -1;
|
_selectedSegment = -1;
|
||||||
|
|
||||||
_trackingMode = CPSegmentSwitchTrackingSelectOne;
|
_trackingMode = CPSegmentSwitchTrackingSelectOne;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +94,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
*/
|
*/
|
||||||
- (int)selectedTag
|
- (int)selectedTag
|
||||||
{
|
{
|
||||||
return [_segments[_selectedSegment] tag];
|
return _segments[_selectedSegment].tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specifying the number of segments
|
// Specifying the number of segments
|
||||||
@@ -93,11 +106,11 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
{
|
{
|
||||||
if (_segments.length == aCount)
|
if (_segments.length == aCount)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var height = CGRectGetHeight([self bounds]);
|
var height = CGRectGetHeight([self bounds]);
|
||||||
|
|
||||||
if (_segments.length < aCount)
|
if (_segments.length < aCount)
|
||||||
{
|
{
|
||||||
for (var index = _segments.length; index < aCount; ++index)
|
for (var index = _segments.length; index < aCount; ++index)
|
||||||
{
|
{
|
||||||
_segments[index] = [[_CPSegmentItem alloc] init];
|
_segments[index] = [[_CPSegmentItem alloc] init];
|
||||||
@@ -106,13 +119,13 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
}
|
}
|
||||||
else if (aCount < _segments.length)
|
else if (aCount < _segments.length)
|
||||||
{
|
{
|
||||||
_segments.length = aCount;
|
for (var index = aCount; index < _segments.length; ++index)
|
||||||
_themeStates.length = aCount;
|
_segments[index] = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_selectedSegment >= _segments.length)
|
if (_selectedSegment < _segments.length)
|
||||||
_selectedSegment = -1;
|
_selectedSegment = -1;
|
||||||
|
|
||||||
[self tileWithChangedSegment:0];
|
[self tileWithChangedSegment:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +141,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
/*!
|
/*!
|
||||||
Selects a segment.
|
Selects a segment.
|
||||||
@param aSegment the segment to select
|
@param aSegment the segment to select
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (void)setSelectedSegment:(unsigned)aSegment
|
- (void)setSelectedSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
@@ -150,15 +163,15 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
- (BOOL)selectSegmentWithTag:(int)aTag
|
- (BOOL)selectSegmentWithTag:(int)aTag
|
||||||
{
|
{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
|
||||||
for (; index < _segments.length; ++index)
|
for (; index < _segments.length; ++index)
|
||||||
if (_segments[index].tag == aTag)
|
if (_segments[index].tag == aTag)
|
||||||
{
|
{
|
||||||
[self setSelectedSegment:index];
|
[self setSelectedSegment:index];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,28 +186,28 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
{
|
{
|
||||||
if (_trackingMode == aTrackingMode)
|
if (_trackingMode == aTrackingMode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_trackingMode = aTrackingMode;
|
_trackingMode = aTrackingMode;
|
||||||
|
|
||||||
if (_trackingMode == CPSegmentSwitchTrackingSelectOne)
|
if (_trackingMode == CPSegmentSwitchTrackingSelectOne)
|
||||||
{
|
{
|
||||||
var index = 0,
|
var index = 0,
|
||||||
selected = NO;
|
selected = NO;
|
||||||
|
|
||||||
for (; index < _segments.length; ++index)
|
for (; index < _segments.length; ++index)
|
||||||
if ([_segments[index] selected])
|
if (_segments[index].selected)
|
||||||
if (selected)
|
if (selected)
|
||||||
[self setSelected:NO forSegment:index];
|
[self setSelected:NO forSegment:index];
|
||||||
else
|
else
|
||||||
selected = YES;
|
selected = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
else if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||||
{
|
{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
|
||||||
for (; index < _segments.length; ++index)
|
for (; index < _segments.length; ++index)
|
||||||
if ([_segments[index] selected])
|
if (_segments[index].selected)
|
||||||
[self setSelected:NO forSegment:index];
|
[self setSelected:NO forSegment:index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,34 +225,36 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
Sets the width of the specified segment.
|
Sets the width of the specified segment.
|
||||||
@param aWidth the new width for the segment
|
@param aWidth the new width for the segment
|
||||||
@param aSegment the segment to set the width for
|
@param aSegment the segment to set the width for
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
|
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
[_segments[aSegment] setWidth:aWidth];
|
_segments[aSegment].width = aWidth;
|
||||||
|
|
||||||
[self tileWithChangedSegment:aSegment];
|
[self tileWithChangedSegment:aSegment];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the width for the specified segment.
|
Returns the width for the specified segment.
|
||||||
@param aSegment the segment to get the width for
|
@param aSegment the segment to get the width for
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (float)widthForSegment:(unsigned)aSegment
|
- (float)widthForSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
return [_segments[aSegment] width];
|
return _segments[aSegment].width;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the image for the specified segment.
|
Sets the image for the specified segment.
|
||||||
@param anImage the image for the segment
|
@param anImage the image for the segment
|
||||||
@param aSegment the segment to set the image on
|
@param aSegment the segment to set the image on
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
|
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
[_segments[aSegment] setImage:anImage];
|
var segment = _segments[aSegment];
|
||||||
|
|
||||||
|
segment.image = anImage;
|
||||||
|
|
||||||
[self tileWithChangedSegment:aSegment];
|
[self tileWithChangedSegment:aSegment];
|
||||||
}
|
}
|
||||||
@@ -247,73 +262,75 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
/*!
|
/*!
|
||||||
Returns the image for the specified segment
|
Returns the image for the specified segment
|
||||||
@param aSegment the segment to obtain the image for
|
@param aSegment the segment to obtain the image for
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (CPImage)imageForSegment:(unsigned)aSegment
|
- (CPImage)imageForSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
return [_segments[aSegment] image];
|
return _segments[aSegment].image;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the label for the specified segment
|
Sets the label for the specified segment
|
||||||
@param aLabel the label for the segment
|
@param aLabel the label for the segment
|
||||||
@param aSegment the segment to label
|
@param aSegment the segment to label
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
|
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
[_segments[aSegment] setLabel:aLabel];
|
var segment = _segments[aSegment];
|
||||||
|
|
||||||
|
_segments[aSegment].label = aLabel;
|
||||||
|
|
||||||
[self tileWithChangedSegment:aSegment];
|
[self tileWithChangedSegment:aSegment];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the label for the specified segment
|
Returns the label for the specified segment
|
||||||
@param the segment to obtain the label for
|
@param the segment to obtain the label for
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (CPString)labelForSegment:(unsigned)aSegment
|
- (CPString)labelForSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
return [_segments[aSegment] label];
|
return _segments[aSegment].label;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the menu for the specified segment
|
Sets the menu for the specified segment
|
||||||
@param aMenu the menu to set
|
@param aMenu the menu to set
|
||||||
@param aSegment the segment to set the menu on
|
@param aSegment the segment to set the menu on
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
|
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
[_segments[aSegment] setMenu:aMenu];
|
_segments[aSegment].menu = aMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the menu for the specified segment.
|
Returns the menu for the specified segment.
|
||||||
@param aSegment the segment to obtain the menu for
|
@param aSegment the segment to obtain the menu for
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (CPMenu)menuForSegment:(unsigned)aSegment
|
- (CPMenu)menuForSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
return [_segments[aSegment] menu];
|
return _segments[aSegment].menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the selection for the specified segment. If only one segment
|
Sets the selection for the specified segment. If only one segment
|
||||||
can be selected at a time, any other segment will be deselected.
|
can be selected at a time, any other segment will be deselected.
|
||||||
@param isSelected \c YES selects the segment. \c NO deselects it.
|
@param isSelected <code>YES</code> selects the segment. <code>NO</code> deselects it.
|
||||||
@param aSegment the segment to set the selection for
|
@param aSegment the segment to set the selection for
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
|
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
var segment = _segments[aSegment];
|
var segment = _segments[aSegment];
|
||||||
|
|
||||||
// If we're already in this state, bail.
|
// If we're already in this state, bail.
|
||||||
if ([segment selected] == isSelected)
|
if (segment.selected == isSelected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[segment setSelected:isSelected];
|
segment.selected = isSelected;
|
||||||
|
|
||||||
_themeStates[aSegment] = isSelected ? CPThemeStateSelected : CPThemeStateNormal;
|
_themeStates[aSegment] = isSelected ? CPThemeStateSelected : CPThemeStateNormal;
|
||||||
|
|
||||||
@@ -321,18 +338,18 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
if (isSelected)
|
if (isSelected)
|
||||||
{
|
{
|
||||||
var oldSelectedSegment = _selectedSegment;
|
var oldSelectedSegment = _selectedSegment;
|
||||||
|
|
||||||
_selectedSegment = aSegment;
|
_selectedSegment = aSegment;
|
||||||
|
|
||||||
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
|
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
|
||||||
{
|
{
|
||||||
[_segments[oldSelectedSegment] setSelected:NO];
|
_segments[oldSelectedSegment].selected = NO;
|
||||||
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
|
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
|
||||||
|
|
||||||
[self drawSegmentBezel:oldSelectedSegment highlight:NO];
|
[self drawSegmentBezel:oldSelectedSegment highlight:NO];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_trackingMode != CPSegmentSwitchTrackingMomentary)
|
if (_trackingMode != CPSegmentSwitchTrackingMomentary)
|
||||||
[self drawSegmentBezel:aSegment highlight:NO];
|
[self drawSegmentBezel:aSegment highlight:NO];
|
||||||
|
|
||||||
@@ -341,42 +358,37 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the specified segment is selected.
|
Returns <code>YES</code> if the specified segment is selected.
|
||||||
@param aSegment the segment to check for selection
|
@param aSegment the segment to check for selection
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (BOOL)isSelectedForSegment:(unsigned)aSegment
|
- (BOOL)isSelectedForSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
return [_segments[aSegment] selected];
|
return _segments[aSegment].selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Enables/diables the specified segment.
|
Enables/diables the specified segment.
|
||||||
@param isEnabled \c YES enables the segment
|
@param isEnabled <code>YES</code> enables the segment
|
||||||
@param aSegment the segment to enable/disble
|
@param aSegment the segment to enable/disble
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
|
- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
[_segments[aSegment] setEnabled:isEnabled];
|
_segments[aSegment].enabled = isEnabled;
|
||||||
|
|
||||||
if (isEnabled)
|
|
||||||
_themeStates[aSegment] &= ~CPThemeStateDisabled;
|
|
||||||
else
|
|
||||||
_themeStates[aSegment] |= CPThemeStateDisabled;
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the specified segment is enabled.
|
Returns <code>YES</code> if the specified segment is enabled.
|
||||||
@param aSegment the segment to check
|
@param aSegment the segment to check
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||||
*/
|
*/
|
||||||
- (BOOL)isEnabledForSegment:(unsigned)aSegment
|
- (BOOL)isEnabledForSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
return [_segments[aSegment] enabled];
|
return _segments[aSegment].enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -386,7 +398,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
*/
|
*/
|
||||||
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment
|
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
[_segments[aSegment] setTag:aTag];
|
_segments[aSegment].tag = aTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -395,14 +407,14 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
*/
|
*/
|
||||||
- (int)tagForSegment:(unsigned)aSegment
|
- (int)tagForSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
return [_segments[aSegment] tag];
|
return _segments[aSegment].tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drawings
|
// Drawings
|
||||||
/*!
|
/*!
|
||||||
Draws the specified segment bezel
|
Draws the specified segment bezel
|
||||||
@param aSegment the segment to draw the bezel for
|
@param aSegment the segment to draw the bezel for
|
||||||
@param shouldHighlight \c YES highlights the bezel
|
@param shouldHighlight <code>YES</code> highlights the bezel
|
||||||
*/
|
*/
|
||||||
- (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight
|
- (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight
|
||||||
{
|
{
|
||||||
@@ -427,16 +439,6 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
return [self _leftOffsetForSegment:segment - 1] + [self widthForSegment:segment - 1] + thickness;
|
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
|
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||||
{
|
{
|
||||||
var height = [self currentValueForThemeAttribute:@"default-height"],
|
var height = [self currentValueForThemeAttribute:@"default-height"],
|
||||||
@@ -450,28 +452,25 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
}
|
}
|
||||||
else if (aName === "right-segment-bezel")
|
else if (aName === "right-segment-bezel")
|
||||||
{
|
{
|
||||||
return CPRectMake(CGRectGetWidth([self bounds]) - contentInset.right,
|
return CGRectMake(CGRectGetMaxX(bounds) - contentInset.right - bezelInset.right, bezelInset.top, contentInset.right, height);
|
||||||
bezelInset.top,
|
|
||||||
contentInset.right,
|
|
||||||
height);
|
|
||||||
}
|
}
|
||||||
else if (aName.indexOf("segment-bezel") === 0)
|
else if (aName.substring(0, "segment-bezel".length) == "segment-bezel")
|
||||||
{
|
{
|
||||||
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
|
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
|
||||||
frame = CGRectCreateCopy([_segments[segment] frame]);
|
frame = CGRectCreateCopy(_segments[segment].frame);
|
||||||
|
|
||||||
if (segment === 0)
|
if (segment == 0)
|
||||||
{
|
{
|
||||||
frame.origin.x += contentInset.left;
|
frame.origin.x += contentInset.left;
|
||||||
frame.size.width -= contentInset.left;
|
frame.size.width -= contentInset.left;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment === _segments.length - 1)
|
if (segment == _segments.length - 1)
|
||||||
frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
|
frame.size.width -= contentInset.right;
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
else if (aName.indexOf("divider-bezel") === 0)
|
else if (aName.substring(0, "divider-bezel".length) == "divider-bezel")
|
||||||
{
|
{
|
||||||
var segment = parseInt(aName.substring("divider-bezel-".length), 10),
|
var segment = parseInt(aName.substring("divider-bezel-".length), 10),
|
||||||
width = [self widthForSegment:segment],
|
width = [self widthForSegment:segment],
|
||||||
@@ -480,7 +479,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
|
|
||||||
return CGRectMake(left + width, bezelInset.top, thickness, height);
|
return CGRectMake(left + width, bezelInset.top, thickness, height);
|
||||||
}
|
}
|
||||||
else if (aName.indexOf("segment-content") === 0)
|
else if (aName.substring(0, "segment-content".length) == "segment-content")
|
||||||
{
|
{
|
||||||
var segment = parseInt(aName.substring("segment-content-".length), 10);
|
var segment = parseInt(aName.substring("segment-content-".length), 10);
|
||||||
|
|
||||||
@@ -492,7 +491,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
|
|
||||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||||
{
|
{
|
||||||
if ([aName hasPrefix:@"segment-content"])
|
if (aName.substring(0, "segment-content".length) == "segment-content")
|
||||||
return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
@@ -500,59 +499,50 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
|
|
||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
{
|
{
|
||||||
if (_segments.length <= 0)
|
var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
|
||||||
return;
|
inState:_themeStates[0]];
|
||||||
|
|
||||||
var themeState = _themeStates[0];
|
var leftBezelView = [self layoutEphemeralSubviewNamed:@"left-segment-bezel"
|
||||||
|
|
||||||
themeState |= _themeState & CPThemeStateDisabled;
|
|
||||||
|
|
||||||
var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
|
|
||||||
inState:themeState],
|
|
||||||
|
|
||||||
leftBezelView = [self layoutEphemeralSubviewNamed:@"left-segment-bezel"
|
|
||||||
positioned:CPWindowBelow
|
positioned:CPWindowBelow
|
||||||
relativeToEphemeralSubviewNamed:nil];
|
relativeToEphemeralSubviewNamed:nil];
|
||||||
|
|
||||||
[leftBezelView setBackgroundColor:leftCapColor];
|
[leftBezelView setBackgroundColor:leftCapColor];
|
||||||
|
|
||||||
var themeState = _themeStates[_themeStates.length - 1];
|
var rightCapColor = [self valueForThemeAttribute:@"right-segment-bezel-color"
|
||||||
|
inState:_themeStates[_themeStates.length - 1]];
|
||||||
|
|
||||||
themeState |= _themeState & CPThemeStateDisabled;
|
var rightBezelView = [self layoutEphemeralSubviewNamed:@"right-segment-bezel"
|
||||||
|
|
||||||
var rightCapColor = [self valueForThemeAttribute:@"right-segment-bezel-color"
|
|
||||||
inState:themeState],
|
|
||||||
|
|
||||||
rightBezelView = [self layoutEphemeralSubviewNamed:@"right-segment-bezel"
|
|
||||||
positioned:CPWindowBelow
|
positioned:CPWindowBelow
|
||||||
relativeToEphemeralSubviewNamed:nil];
|
relativeToEphemeralSubviewNamed:nil];
|
||||||
|
|
||||||
[rightBezelView setBackgroundColor:rightCapColor];
|
[rightBezelView setBackgroundColor:rightCapColor];
|
||||||
|
|
||||||
for (var i = 0, count = _themeStates.length; i < count; i++)
|
for (var i=0, count = _themeStates.length; i<count; i++)
|
||||||
{
|
{
|
||||||
var themeState = _themeStates[i];
|
var themeState = _themeStates[i];
|
||||||
|
|
||||||
themeState |= _themeState & CPThemeStateDisabled;
|
themeState |= _themeState & CPThemeStateDisabled;
|
||||||
|
|
||||||
var bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color"
|
var bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color"
|
||||||
inState:themeState],
|
inState:themeState];
|
||||||
|
|
||||||
bezelView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
|
var bezelView = [self layoutEphemeralSubviewNamed:"segment-bezel-"+i
|
||||||
positioned:CPWindowBelow
|
positioned:CPWindowBelow
|
||||||
relativeToEphemeralSubviewNamed:nil];
|
relativeToEphemeralSubviewNamed:nil];
|
||||||
|
|
||||||
[bezelView setBackgroundColor:bezelColor];
|
[bezelView setBackgroundColor:bezelColor];
|
||||||
|
|
||||||
|
|
||||||
// layout image/title views
|
//layout image/title views
|
||||||
var segment = _segments[i],
|
var segment = _segments[i];
|
||||||
contentView = [self layoutEphemeralSubviewNamed:@"segment-content-" + i
|
|
||||||
positioned:CPWindowAbove
|
|
||||||
relativeToEphemeralSubviewNamed:@"segment-bezel-" + i];
|
|
||||||
|
|
||||||
[contentView setText:[segment label]];
|
|
||||||
[contentView setImage:[segment image]];
|
var contentView = [self layoutEphemeralSubviewNamed:@"segment-content-"+i
|
||||||
|
positioned:CPWindowAbove
|
||||||
|
relativeToEphemeralSubviewNamed:@"segment-bezel-"+i];
|
||||||
|
|
||||||
|
[contentView setText:segment.label];
|
||||||
|
[contentView setImage:segment.image];
|
||||||
|
|
||||||
[contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
|
[contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
|
||||||
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:themeState]];
|
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:themeState]];
|
||||||
@@ -561,27 +551,27 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
[contentView setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode" inState:themeState]];
|
[contentView setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode" inState:themeState]];
|
||||||
[contentView setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color" inState:themeState]];
|
[contentView setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color" inState:themeState]];
|
||||||
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
|
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
|
||||||
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
|
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
|
||||||
|
|
||||||
if ([segment image] && [segment label])
|
if (segment.image && segment.label)
|
||||||
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
|
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
|
||||||
else if ([segment image])
|
else if (segment.image)
|
||||||
[contentView setImagePosition:CPImageOnly];
|
[contentView setImagePosition:CPImageOnly];
|
||||||
|
|
||||||
if (i == count - 1)
|
if (i == count - 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var borderState = _themeStates[i] | _themeStates[i + 1];
|
var borderState = _themeStates[i] | _themeStates[i+1];
|
||||||
|
|
||||||
borderState = (borderState & CPThemeStateSelected & ~CPThemeStateHighlighted) ? CPThemeStateSelected : CPThemeStateNormal;
|
borderState = (borderState & CPThemeStateSelected & ~CPThemeStateHighlighted) ? CPThemeStateSelected : CPThemeStateNormal;
|
||||||
|
|
||||||
borderState |= _themeState & CPThemeStateDisabled;
|
borderState |= _themeState & CPThemeStateDisabled;
|
||||||
|
|
||||||
var borderColor = [self valueForThemeAttribute:@"divider-bezel-color"
|
var borderColor = [self valueForThemeAttribute:@"divider-bezel-color"
|
||||||
inState:borderState],
|
inState:borderState];
|
||||||
|
|
||||||
borderView = [self layoutEphemeralSubviewNamed:"divider-bezel-" + i
|
var borderView = [self layoutEphemeralSubviewNamed:"divider-bezel-"+i
|
||||||
positioned:CPWindowBelow
|
positioned:CPWindowBelow
|
||||||
relativeToEphemeralSubviewNamed:nil];
|
relativeToEphemeralSubviewNamed:nil];
|
||||||
|
|
||||||
[borderView setBackgroundColor:borderColor];
|
[borderView setBackgroundColor:borderColor];
|
||||||
@@ -592,7 +582,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
/*!
|
/*!
|
||||||
Draws the specified segment
|
Draws the specified segment
|
||||||
@param aSegment the segment to draw
|
@param aSegment the segment to draw
|
||||||
@param shouldHighlight \c YES highlights the bezel
|
@param shouldHighlight <code>YES</code> highlights the bezel
|
||||||
*/
|
*/
|
||||||
- (void)drawSegment:(int)aSegment highlight:(BOOL)shouldHighlight
|
- (void)drawSegment:(int)aSegment highlight:(BOOL)shouldHighlight
|
||||||
{
|
{
|
||||||
@@ -604,53 +594,48 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var segment = _segments[aSegment],
|
var segment = _segments[aSegment],
|
||||||
segmentWidth = [segment width],
|
segmentWidth = segment.width,
|
||||||
themeState = _themeStates[aSegment] | (_themeState & CPThemeStateDisabled),
|
themeState = _themeStates[aSegment] | (_themeState & CPThemeStateDisabled),
|
||||||
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
|
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
|
||||||
font = [self valueForThemeAttribute:@"font" inState:themeState];
|
font = [self valueForThemeAttribute:@"font" inState:themeState];
|
||||||
|
|
||||||
if (!segmentWidth)
|
if (!segmentWidth)
|
||||||
{
|
{
|
||||||
if ([segment image] && [segment label])
|
if (segment.image && segment.label)
|
||||||
segmentWidth = [[segment label] sizeWithFont:font].width + [[segment image] size].width + contentInset.left + contentInset.right;
|
segmentWidth = [segment.label sizeWithFont:font].width + [segment.image size].width + contentInset.left + contentInset.right;
|
||||||
else if (segment.image)
|
else if (segment.image)
|
||||||
segmentWidth = [[segment image] size].width + contentInset.left + contentInset.right;
|
segmentWidth = [segment.image size].width + contentInset.left + contentInset.right;
|
||||||
else if (segment.label)
|
else if (segment.label)
|
||||||
segmentWidth = [[segment label] sizeWithFont:font].width + contentInset.left + contentInset.right;
|
segmentWidth = [segment.label sizeWithFont:font].width + contentInset.left + contentInset.right;
|
||||||
else
|
else
|
||||||
segmentWidth = 0.0;
|
segmentWidth = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var delta = segmentWidth - CGRectGetWidth([segment frame]);
|
var delta = segmentWidth - CGRectGetWidth(segment.frame);
|
||||||
|
|
||||||
if (!delta)
|
if (!delta)
|
||||||
{
|
|
||||||
[self setNeedsLayout];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Update control size
|
// Update Contorl Size
|
||||||
var frame = [self frame];
|
var frame = [self frame];
|
||||||
|
|
||||||
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
|
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
|
||||||
|
|
||||||
// Update segment width
|
// Update Segment Width
|
||||||
[segment setWidth:segmentWidth];
|
segment.width = segmentWidth;
|
||||||
[segment setFrame:[self frameForSegment:aSegment]];
|
segment.frame = [self frameForSegment:aSegment];;
|
||||||
|
|
||||||
// Update following segments widths
|
// Update Following Segments Widths
|
||||||
var index = aSegment + 1;
|
var index = aSegment + 1;
|
||||||
|
|
||||||
for (; index < _segments.length; ++index)
|
for (; index < _segments.length; ++index)
|
||||||
{
|
{
|
||||||
[_segments[index] frame].origin.x += delta;
|
_segments[index].frame.origin.x += delta;
|
||||||
|
|
||||||
[self drawSegmentBezel:index highlight:NO];
|
[self drawSegmentBezel:index highlight:NO];
|
||||||
[self drawSegment:index highlight:NO];
|
[self drawSegment:index highlight:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self drawSegmentBezel:aSegment highlight:NO];
|
[self drawSegmentBezel:aSegment highlight:NO];
|
||||||
[self drawSegment:aSegment highlight:NO];
|
[self drawSegment:aSegment highlight:NO];
|
||||||
|
|
||||||
@@ -696,20 +681,11 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
{
|
{
|
||||||
var location = [self convertPoint:aPoint fromView:nil],
|
var location = [self convertPoint:aPoint fromView:nil],
|
||||||
count = _segments.length;
|
count = _segments.length;
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
if (CGRectContainsPoint([_segments[count] frame], aPoint))
|
if (CGRectContainsPoint(_segments[count].frame, aPoint))
|
||||||
return count;
|
return count;
|
||||||
|
|
||||||
if (_segments.length)
|
|
||||||
{
|
|
||||||
var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
|
|
||||||
adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
|
|
||||||
|
|
||||||
if (CGRectContainsPoint(adjustedLastFrame, aPoint))
|
|
||||||
return _segments.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,7 +693,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
{
|
{
|
||||||
if (![self isEnabled])
|
if (![self isEnabled])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self trackSegment:anEvent];
|
[self trackSegment:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,76 +707,74 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
@param anEvent the event to handle
|
@param anEvent the event to handle
|
||||||
*/
|
*/
|
||||||
- (void)trackSegment:(CPEvent)anEvent
|
- (void)trackSegment:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var type = [anEvent type],
|
var type = [anEvent type],
|
||||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
|
|
||||||
if (type == CPLeftMouseUp)
|
if (type == CPLeftMouseUp)
|
||||||
{
|
{
|
||||||
if (_trackingSegment == -1)
|
if (_trackingSegment == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_trackingSegment === [self testSegment:location])
|
if (CGRectContainsPoint(_segments[_trackingSegment].frame, location))
|
||||||
{
|
{
|
||||||
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
||||||
{
|
{
|
||||||
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
|
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
|
||||||
|
|
||||||
// With ANY, _selectedSegment means last pressed.
|
// With ANY, _selectedSegment means last pressed.
|
||||||
_selectedSegment = _trackingSegment;
|
_selectedSegment = _trackingSegment;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[self setSelected:YES forSegment:_trackingSegment];
|
[self setSelected:YES forSegment:_trackingSegment];
|
||||||
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
[self sendAction:[self action] to:[self target]];
|
||||||
|
|
||||||
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||||
{
|
{
|
||||||
[self setSelected:NO forSegment:_trackingSegment];
|
[self setSelected:NO forSegment:_trackingSegment];
|
||||||
|
|
||||||
_selectedSegment = -1;
|
_selectedSegment = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||||
|
|
||||||
_trackingSegment = -1;
|
_trackingSegment = -1;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == CPLeftMouseDown)
|
if (type == CPLeftMouseDown)
|
||||||
{
|
{
|
||||||
var trackingSegment = [self testSegment:location];
|
_trackingHighlighted = YES;
|
||||||
if (trackingSegment > -1 && [self isEnabledForSegment:trackingSegment])
|
_trackingSegment = [self testSegment:location];
|
||||||
{
|
|
||||||
_trackingHighlighted = YES;
|
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||||
_trackingSegment = trackingSegment;
|
|
||||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == CPLeftMouseDragged)
|
else if (type == CPLeftMouseDragged)
|
||||||
{
|
{
|
||||||
if (_trackingSegment == -1)
|
if (_trackingSegment == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var highlighted = [self testSegment:location] === _trackingSegment;
|
var highlighted = CGRectContainsPoint(_segments[_trackingSegment].frame, location);
|
||||||
|
|
||||||
if (highlighted != _trackingHighlighted)
|
if (highlighted != _trackingHighlighted)
|
||||||
{
|
{
|
||||||
_trackingHighlighted = highlighted;
|
_trackingHighlighted = highlighted;
|
||||||
|
|
||||||
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
|
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
- (void)setFont:(CPFont)aFont
|
||||||
{
|
{
|
||||||
[super setFont:aFont];
|
[super setFont:aFont];
|
||||||
|
|
||||||
[self tileWithChangedSegment:0];
|
[self tileWithChangedSegment:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,12 +790,12 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
|||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super initWithCoder:aCoder];
|
self = [super initWithCoder:aCoder];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
var frame = [self frame],
|
var frame = [self frame],
|
||||||
originalWidth = frame.size.width;
|
originalWidth = frame.size.width;
|
||||||
|
|
||||||
frame.size.width = 0;
|
frame.size.width = 0;
|
||||||
|
|
||||||
[self setFrame:frame];
|
[self setFrame:frame];
|
||||||
@@ -832,38 +806,38 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
|||||||
|
|
||||||
if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
|
if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
|
||||||
_selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
|
_selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
|
||||||
else
|
else
|
||||||
_selectedSegment = -1;
|
_selectedSegment = -1;
|
||||||
|
|
||||||
if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey])
|
if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey])
|
||||||
_trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
|
_trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
|
||||||
else
|
else
|
||||||
_trackingMode = CPSegmentSwitchTrackingSelectOne;
|
_trackingMode = CPSegmentSwitchTrackingSelectOne;
|
||||||
|
|
||||||
// HACK
|
// HACK
|
||||||
|
|
||||||
for (var i = 0; i < _segments.length; i++)
|
for (var i = 0; i < _segments.length; i++)
|
||||||
{
|
{
|
||||||
_themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal;
|
_themeStates[i] = _segments[i].selected ? CPThemeStateSelected : CPThemeStateNormal;
|
||||||
[self tileWithChangedSegment:i];
|
[self tileWithChangedSegment:i];
|
||||||
}
|
}
|
||||||
|
|
||||||
var difference = MAX(originalWidth - [self frame].size.width, 0.0),
|
var difference = MAX(originalWidth - [self frame].size.width, 0.0),
|
||||||
remainingWidth = FLOOR(difference / _segments.length);
|
remainingWidth = FLOOR(difference / _segments.length);
|
||||||
|
|
||||||
for (var i = 0; i < _segments.length; i++)
|
for (var i=0; i < _segments.length; i++)
|
||||||
[self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
|
[self setWidth:_segments[i].width + remainingWidth forSegment:i];
|
||||||
|
|
||||||
[self tileWithChangedSegment:0];
|
[self tileWithChangedSegment:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
[aCoder encodeObject:_segments forKey:CPSegmentedControlSegmentsKey];
|
[aCoder encodeObject:_segments forKey:CPSegmentedControlSegmentsKey];
|
||||||
[aCoder encodeInt:_selectedSegment forKey:CPSegmentedControlSelectedKey];
|
[aCoder encodeInt:_selectedSegment forKey:CPSegmentedControlSelectedKey];
|
||||||
[aCoder encodeInt:_segmentStyle forKey:CPSegmentedControlSegmentStyleKey];
|
[aCoder encodeInt:_segmentStyle forKey:CPSegmentedControlSegmentStyleKey];
|
||||||
@@ -875,15 +849,15 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
|||||||
|
|
||||||
@implementation _CPSegmentItem : CPObject
|
@implementation _CPSegmentItem : CPObject
|
||||||
{
|
{
|
||||||
CPImage image @accessors;
|
CPImage image;
|
||||||
CPString label @accessors;
|
CPString label;
|
||||||
CPMenu menu @accessors;
|
CPMenu menu;
|
||||||
BOOL selected @accessors;
|
BOOL selected;
|
||||||
BOOL enabled @accessors;
|
BOOL enabled;
|
||||||
int tag @accessors;
|
int tag;
|
||||||
int width @accessors;
|
int width;
|
||||||
|
|
||||||
CGRect frame @accessors;
|
CGRect frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
@@ -894,8 +868,8 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
|||||||
label = @"";
|
label = @"";
|
||||||
menu = nil;
|
menu = nil;
|
||||||
selected = NO;
|
selected = NO;
|
||||||
enabled = YES;
|
enabled = NO;
|
||||||
tag = -1;
|
tag = 0;
|
||||||
width = 0;
|
width = 0;
|
||||||
|
|
||||||
frame = CGRectMakeZero();
|
frame = CGRectMakeZero();
|
||||||
@@ -918,7 +892,7 @@ var CPSegmentItemImageKey = "CPSegmentItemImageKey",
|
|||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
image = [aCoder decodeObjectForKey:CPSegmentItemImageKey];
|
image = [aCoder decodeObjectForKey:CPSegmentItemImageKey];
|
||||||
@@ -931,7 +905,7 @@ var CPSegmentItemImageKey = "CPSegmentItemImageKey",
|
|||||||
|
|
||||||
frame = CGRectMakeZero();
|
frame = CGRectMakeZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -947,3 +921,4 @@ var CPSegmentItemImageKey = "CPSegmentItemImageKey",
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
+18
-41
@@ -28,17 +28,19 @@
|
|||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
CPLightShadow = 0;
|
CPLightShadow = 0;
|
||||||
CPHeavyShadow = 1;
|
CPHeavyShadow = 1;
|
||||||
|
|
||||||
var CPShadowViewLightBackgroundColor = nil,
|
var CPShadowViewLightBackgroundColor = nil,
|
||||||
CPShadowViewHeavyBackgroundColor = nil;
|
CPShadowViewHeavyBackgroundColor = nil;
|
||||||
|
|
||||||
var LIGHT_LEFT_INSET = 3.0,
|
var LIGHT_LEFT_INSET = 3.0,
|
||||||
LIGHT_RIGHT_INSET = 3.0,
|
LIGHT_RIGHT_INSET = 3.0,
|
||||||
LIGHT_TOP_INSET = 3.0,
|
LIGHT_TOP_INSET = 3.0,
|
||||||
LIGHT_BOTTOM_INSET = 5.0,
|
LIGHT_BOTTOM_INSET = 5.0,
|
||||||
|
|
||||||
HEAVY_LEFT_INSET = 7.0,
|
HEAVY_LEFT_INSET = 7.0,
|
||||||
HEAVY_RIGHT_INSET = 7.0,
|
HEAVY_RIGHT_INSET = 7.0,
|
||||||
HEAVY_TOP_INSET = 5.0,
|
HEAVY_TOP_INSET = 5.0,
|
||||||
@@ -59,76 +61,51 @@ var LIGHT_LEFT_INSET = 3.0,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var bundle = [CPBundle bundleForClass:[self class]];
|
var bundle = [CPBundle bundleForClass:[self class]];
|
||||||
|
|
||||||
CPShadowViewLightBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
CPShadowViewLightBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||||
[
|
[
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopLeft.png"] size:CGSizeMake(9.0, 9.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopLeft.png"] size:CGSizeMake(9.0, 9.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTop.png"] size:CGSizeMake(1.0, 9.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTop.png"] size:CGSizeMake(1.0, 9.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopRight.png"] size:CGSizeMake(9.0, 9.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopRight.png"] size:CGSizeMake(9.0, 9.0)],
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightLeft.png"] size:CGSizeMake(9.0, 1.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightLeft.png"] size:CGSizeMake(9.0, 1.0)],
|
||||||
nil,
|
nil,
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightRight.png"] size:CGSizeMake(9.0, 1.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightRight.png"] size:CGSizeMake(9.0, 1.0)],
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomLeft.png"] size:CGSizeMake(9.0, 9.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomLeft.png"] size:CGSizeMake(9.0, 9.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottom.png"] size:CGSizeMake(1.0, 9.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottom.png"] size:CGSizeMake(1.0, 9.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomRight.png"] size:CGSizeMake(9.0, 9.0)]
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomRight.png"] size:CGSizeMake(9.0, 9.0)]
|
||||||
]]];
|
]]];
|
||||||
|
|
||||||
CPShadowViewHeavyBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
CPShadowViewHeavyBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||||
[
|
[
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopLeft.png"] size:CGSizeMake(17.0, 17.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopLeft.png"] size:CGSizeMake(17.0, 17.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTop.png"] size:CGSizeMake(1.0, 17.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTop.png"] size:CGSizeMake(1.0, 17.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopRight.png"] size:CGSizeMake(17.0, 17.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopRight.png"] size:CGSizeMake(17.0, 17.0)],
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyLeft.png"] size:CGSizeMake(17.0, 1.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyLeft.png"] size:CGSizeMake(17.0, 1.0)],
|
||||||
nil,
|
nil,
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyRight.png"] size:CGSizeMake(17.0, 1.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyRight.png"] size:CGSizeMake(17.0, 1.0)],
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomLeft.png"] size:CGSizeMake(17.0, 17.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomLeft.png"] size:CGSizeMake(17.0, 17.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottom.png"] size:CGSizeMake(1.0, 17.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottom.png"] size:CGSizeMake(1.0, 17.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomRight.png"] size:CGSizeMake(17.0, 17.0)]
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomRight.png"] size:CGSizeMake(17.0, 17.0)]
|
||||||
]]];
|
]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)shadowViewEnclosingView:(CPView)aView
|
|
||||||
{
|
|
||||||
return [self shadowViewEnclosingView:aView withWeight:CPLightShadow];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)shadowViewEnclosingView:(CPView)aView withWeight:(CPShadowWeight)aWeight
|
|
||||||
{
|
|
||||||
var shadowView = [[CPShadowView alloc] initWithFrame:[aView frame]];
|
|
||||||
[shadowView setWeight:aWeight];
|
|
||||||
|
|
||||||
var size = [shadowView frame].size,
|
|
||||||
width = size.width - [shadowView leftInset] - [shadowView rightInset],
|
|
||||||
height = size.height - [shadowView topInset] - [shadowView bottomInset],
|
|
||||||
enclosingView = [aView superview];
|
|
||||||
|
|
||||||
[shadowView setHitTests:[aView hitTests]];
|
|
||||||
[shadowView setAutoresizingMask:[aView autoresizingMask]];
|
|
||||||
[aView removeFromSuperview];
|
|
||||||
[shadowView addSubview:aView];
|
|
||||||
[aView setFrame:CGRectMake([shadowView leftInset], [shadowView topInset], width, height)]
|
|
||||||
[enclosingView addSubview:shadowView];
|
|
||||||
|
|
||||||
return shadowView;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_weight = CPLightShadow;
|
_weight = CPLightShadow;
|
||||||
|
|
||||||
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
|
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
|
||||||
|
|
||||||
[self setHitTests:NO];
|
[self setHitTests:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,9 +113,9 @@ var LIGHT_LEFT_INSET = 3.0,
|
|||||||
{
|
{
|
||||||
if (_weight == aWeight)
|
if (_weight == aWeight)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_weight = aWeight;
|
_weight = aWeight;
|
||||||
|
|
||||||
if (_weight == CPLightShadow)
|
if (_weight == CPLightShadow)
|
||||||
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
|
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
|
||||||
|
|
||||||
@@ -170,7 +147,7 @@ var LIGHT_LEFT_INSET = 3.0,
|
|||||||
{
|
{
|
||||||
if (_weight == CPLightShadow)
|
if (_weight == CPLightShadow)
|
||||||
return LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET;
|
return LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET;
|
||||||
|
|
||||||
return HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET;
|
return HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +155,7 @@ var LIGHT_LEFT_INSET = 3.0,
|
|||||||
{
|
{
|
||||||
if (_weight == CPLightShadow)
|
if (_weight == CPLightShadow)
|
||||||
return LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET;
|
return LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET;
|
||||||
|
|
||||||
return HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET;
|
return HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+41
-59
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
/*! SLIDER STATES */
|
/*! SLIDER STATES */
|
||||||
|
|
||||||
@@ -41,14 +42,14 @@ CPCircularSlider = 1;
|
|||||||
BOOL _isVertical;
|
BOOL _isVertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)themeClass
|
||||||
{
|
{
|
||||||
return "slider";
|
return "slider";
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], _CGSizeMakeZero(), 0.0, [CPNull null]]
|
return [CPDictionary dictionaryWithObjects:[nil, _CGSizeMakeZero(), 0.0, nil]
|
||||||
forKeys:[@"knob-color", @"knob-size", @"track-width", @"track-color"]];
|
forKeys:[@"knob-color", @"knob-size", @"track-width", @"track-color"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,14 +61,14 @@ CPCircularSlider = 1;
|
|||||||
{
|
{
|
||||||
_minValue = 0.0;
|
_minValue = 0.0;
|
||||||
_maxValue = 100.0;
|
_maxValue = 100.0;
|
||||||
|
|
||||||
[self setObjectValue:50.0];
|
[self setObjectValue:50.0];
|
||||||
|
|
||||||
[self setContinuous:YES];
|
[self setContinuous:YES];
|
||||||
|
|
||||||
[self _recalculateIsVertical];
|
[self _recalculateIsVertical];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,17 +76,13 @@ CPCircularSlider = 1;
|
|||||||
{
|
{
|
||||||
if (_minValue === aMinimumValue)
|
if (_minValue === aMinimumValue)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_minValue = aMinimumValue;
|
_minValue = aMinimumValue;
|
||||||
|
|
||||||
var doubleValue = [self doubleValue];
|
var doubleValue = [self doubleValue];
|
||||||
|
|
||||||
if (doubleValue < _minValue)
|
if (doubleValue < _minValue)
|
||||||
[self setDoubleValue:_minValue];
|
[self setDoubleValue:_minValue];
|
||||||
|
|
||||||
// The relative position may have (did) change.
|
|
||||||
[self setNeedsLayout];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)minValue
|
- (float)minValue
|
||||||
@@ -97,17 +94,13 @@ CPCircularSlider = 1;
|
|||||||
{
|
{
|
||||||
if (_maxValue === aMaximumValue)
|
if (_maxValue === aMaximumValue)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_maxValue = aMaximumValue;
|
_maxValue = aMaximumValue;
|
||||||
|
|
||||||
var doubleValue = [self doubleValue];
|
var doubleValue = [self doubleValue];
|
||||||
|
|
||||||
if (doubleValue > _maxValue)
|
if (doubleValue > _maxValue)
|
||||||
[self setDoubleValue:_maxValue];
|
[self setDoubleValue:_maxValue];
|
||||||
|
|
||||||
// The relative position may have (did) change.
|
|
||||||
[self setNeedsLayout];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)maxValue
|
- (float)maxValue
|
||||||
@@ -168,27 +161,27 @@ CPCircularSlider = 1;
|
|||||||
bounds.size.height = trackWidth;
|
bounds.size.height = trackWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)knobRectForBounds:(CGRect)bounds
|
- (CGRect)knobRectForBounds:(CGRect)bounds
|
||||||
{
|
{
|
||||||
var knobSize = [self currentValueForThemeAttribute:@"knob-size"];
|
var knobSize = [self currentValueForThemeAttribute:@"knob-size"];
|
||||||
|
|
||||||
if (knobSize.width <= 0 || knobSize.height <= 0)
|
if (knobSize.width <= 0 || knobSize.height <= 0)
|
||||||
return _CGRectMakeZero();
|
return _CGRectMakeZero();
|
||||||
|
|
||||||
var knobRect = _CGRectMake(0.0, 0.0, knobSize.width, knobSize.height),
|
var knobRect = _CGRectMake(0.0, 0.0, knobSize.width, knobSize.height),
|
||||||
trackRect = [self trackRectForBounds:bounds];
|
trackRect = [self trackRectForBounds:bounds];
|
||||||
|
|
||||||
// No track, do our best to approximate a place for this thing.
|
// No track, do our best to approximate a place for this thing.
|
||||||
if (!trackRect || _CGRectIsEmpty(trackRect))
|
if (!trackRect || _CGRectIsEmpty(trackRect))
|
||||||
trackRect = bounds;
|
trackRect = bounds;
|
||||||
|
|
||||||
if ([self hasThemeState:CPThemeStateCircular])
|
if ([self hasThemeState:CPThemeStateCircular])
|
||||||
{
|
{
|
||||||
var angle = 3 * PI_2 - (1.0 - [self doubleValue] - _minValue) / (_maxValue - _minValue) * PI2,
|
var angle = 3*PI_2 - (1.0 - [self doubleValue] - _minValue) / (_maxValue - _minValue) * PI2,
|
||||||
radius = CGRectGetWidth(trackRect) / 2.0 - 8.0;
|
radius = CGRectGetWidth(trackRect) / 2.0 - 8.0;
|
||||||
|
|
||||||
knobRect.origin.x = radius * COS(angle) + CGRectGetMidX(trackRect) - 3.0;
|
knobRect.origin.x = radius * COS(angle) + CGRectGetMidX(trackRect) - 3.0;
|
||||||
@@ -197,7 +190,7 @@ CPCircularSlider = 1;
|
|||||||
else if ([self isVertical])
|
else if ([self isVertical])
|
||||||
{
|
{
|
||||||
knobRect.origin.x = _CGRectGetMidX(trackRect) - knobSize.width / 2.0;
|
knobRect.origin.x = _CGRectGetMidX(trackRect) - knobSize.width / 2.0;
|
||||||
knobRect.origin.y = ((_maxValue - [self doubleValue]) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height);
|
knobRect.origin.y = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -212,10 +205,10 @@ CPCircularSlider = 1;
|
|||||||
{
|
{
|
||||||
if (aName === "track-view")
|
if (aName === "track-view")
|
||||||
return [self trackRectForBounds:[self bounds]];
|
return [self trackRectForBounds:[self bounds]];
|
||||||
|
|
||||||
else if (aName === "knob-view")
|
else if (aName === "knob-view")
|
||||||
return [self knobRectForBounds:[self bounds]];
|
return [self knobRectForBounds:[self bounds]];
|
||||||
|
|
||||||
return [super rectForEphemeralSubviewNamed:aName];
|
return [super rectForEphemeralSubviewNamed:aName];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,12 +217,12 @@ CPCircularSlider = 1;
|
|||||||
if (aName === "track-view" || aName === "knob-view")
|
if (aName === "track-view" || aName === "knob-view")
|
||||||
{
|
{
|
||||||
var view = [[CPView alloc] init];
|
var view = [[CPView alloc] init];
|
||||||
|
|
||||||
[view setHitTests:NO];
|
[view setHitTests:NO];
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [super createEphemeralSubviewNamed:aName];
|
return [super createEphemeralSubviewNamed:aName];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +248,7 @@ CPCircularSlider = 1;
|
|||||||
var bounds = [self bounds],
|
var bounds = [self bounds],
|
||||||
width = _CGRectGetWidth(bounds),
|
width = _CGRectGetWidth(bounds),
|
||||||
height = _CGRectGetHeight(bounds);
|
height = _CGRectGetHeight(bounds);
|
||||||
|
|
||||||
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
||||||
|
|
||||||
if (_isVertical === 1)
|
if (_isVertical === 1)
|
||||||
@@ -281,7 +274,7 @@ CPCircularSlider = 1;
|
|||||||
var knobView = [self layoutEphemeralSubviewNamed:@"knob-view"
|
var knobView = [self layoutEphemeralSubviewNamed:@"knob-view"
|
||||||
positioned:CPWindowAbove
|
positioned:CPWindowAbove
|
||||||
relativeToEphemeralSubviewNamed:@"track-view"];
|
relativeToEphemeralSubviewNamed:@"track-view"];
|
||||||
|
|
||||||
if (knobView)
|
if (knobView)
|
||||||
[knobView setBackgroundColor:[self currentValueForThemeAttribute:"knob-color"]];
|
[knobView setBackgroundColor:[self currentValueForThemeAttribute:"knob-color"]];
|
||||||
}
|
}
|
||||||
@@ -319,7 +312,7 @@ CPCircularSlider = 1;
|
|||||||
|
|
||||||
var minValue = [self minValue];
|
var minValue = [self minValue];
|
||||||
|
|
||||||
return MAX(0.0, MIN(1.0, (_CGRectGetMaxY(trackRect) - aPoint.y) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue;
|
return MAX(0.0, MIN(1.0, (aPoint.y - _CGRectGetMinY(trackRect)) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -338,43 +331,44 @@ CPCircularSlider = 1;
|
|||||||
{
|
{
|
||||||
var bounds = [self bounds],
|
var bounds = [self bounds],
|
||||||
knobRect = [self knobRectForBounds:_CGRectMakeCopy(bounds)];
|
knobRect = [self knobRectForBounds:_CGRectMakeCopy(bounds)];
|
||||||
|
|
||||||
if (_CGRectContainsPoint(knobRect, aPoint))
|
if (_CGRectContainsPoint(knobRect, aPoint))
|
||||||
_dragOffset = _CGSizeMake(_CGRectGetMidX(knobRect) - aPoint.x, _CGRectGetMidY(knobRect) - aPoint.y);
|
_dragOffset = _CGSizeMake(_CGRectGetMidX(knobRect) - aPoint.x, _CGRectGetMidY(knobRect) - aPoint.y);
|
||||||
else
|
|
||||||
|
else
|
||||||
{
|
{
|
||||||
var trackRect = [self trackRectForBounds:bounds];
|
var trackRect = [self trackRectForBounds:bounds];
|
||||||
|
|
||||||
if (trackRect && _CGRectContainsPoint(trackRect, aPoint))
|
if (trackRect && _CGRectContainsPoint(trackRect, aPoint))
|
||||||
{
|
{
|
||||||
_dragOffset = _CGSizeMakeZero();
|
_dragOffset = _CGSizeMakeZero();
|
||||||
|
|
||||||
[self setObjectValue:[self _valueAtPoint:aPoint]];
|
[self setObjectValue:[self _valueAtPoint:aPoint]];
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self setHighlighted:YES];
|
[self setHighlighted:YES];
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
|
- (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
[self setObjectValue:[self _valueAtPoint:_CGPointMake(aPoint.x + _dragOffset.width, aPoint.y + _dragOffset.height)]];
|
[self setObjectValue:[self _valueAtPoint:_CGPointMake(aPoint.x + _dragOffset.width, aPoint.y + _dragOffset.height)]];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
||||||
{
|
{
|
||||||
[self setHighlighted:NO];
|
[self setHighlighted:NO];
|
||||||
|
|
||||||
if ([_target respondsToSelector:@selector(sliderDidFinish:)])
|
if ([_target respondsToSelector:@selector(sliderDidFinish:)])
|
||||||
[_target sliderDidFinish:self];
|
[_target sliderDidFinish:self];
|
||||||
|
|
||||||
@@ -390,22 +384,10 @@ CPCircularSlider = 1;
|
|||||||
{
|
{
|
||||||
if (flag)
|
if (flag)
|
||||||
_sendActionOn |= CPLeftMouseDraggedMask;
|
_sendActionOn |= CPLeftMouseDraggedMask;
|
||||||
else
|
else
|
||||||
_sendActionOn &= ~CPLeftMouseDraggedMask;
|
_sendActionOn &= ~CPLeftMouseDraggedMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
|
||||||
{
|
|
||||||
var count = objects.length,
|
|
||||||
value = [objects[0] valueForKeyPath:aKeyPath];
|
|
||||||
|
|
||||||
[self setObjectValue:value];
|
|
||||||
|
|
||||||
while (count-- > 1)
|
|
||||||
if (value !== ([objects[count] valueForKeyPath:aKeyPath]))
|
|
||||||
return [self setFloatValue:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPSliderMinValueKey = "CPSliderMinValueKey",
|
var CPSliderMinValueKey = "CPSliderMinValueKey",
|
||||||
@@ -452,14 +434,14 @@ var CPSliderMinValueKey = "CPSliderMinValueKey",
|
|||||||
- (id)value
|
- (id)value
|
||||||
{
|
{
|
||||||
CPLog.warn("[CPSlider value] is deprecated, use doubleValue or objectValue instead.");
|
CPLog.warn("[CPSlider value] is deprecated, use doubleValue or objectValue instead.");
|
||||||
|
|
||||||
return [self doubleValue];
|
return [self doubleValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setValue:(id)aValue
|
- (void)setValue:(id)aValue
|
||||||
{
|
{
|
||||||
CPLog.warn("[CPSlider setValue:] is deprecated, use setDoubleValue: or setObjectValue: instead.");
|
CPLog.warn("[CPSlider setValue:] is deprecated, use setDoubleValue: or setObjectValue: instead.");
|
||||||
|
|
||||||
[self setObjectValue:aValue];
|
[self setObjectValue:aValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+153
-105
@@ -20,10 +20,8 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPView.j"
|
|
||||||
@import "CPColorPicker.j"
|
@import "CPColorPicker.j"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ignore
|
@ignore
|
||||||
*/
|
*/
|
||||||
@@ -47,15 +45,14 @@
|
|||||||
CPTextField _saturationLabel;
|
CPTextField _saturationLabel;
|
||||||
CPTextField _brightnessLabel;
|
CPTextField _brightnessLabel;
|
||||||
CPTextField _hexLabel;
|
CPTextField _hexLabel;
|
||||||
CPTextField _hexValue;
|
|
||||||
|
DOMElement _redValue;
|
||||||
CPTextField _hexValue;
|
DOMElement _greenValue;
|
||||||
CPTextField _redValue;
|
DOMElement _blueValue;
|
||||||
CPTextField _greenValue;
|
DOMElement _hueValue;
|
||||||
CPTextField _blueValue;
|
DOMElement _saturationValue;
|
||||||
CPTextField _hueValue;
|
DOMElement _brightnessValue;
|
||||||
CPTextField _saturationValue;
|
DOMElement _hexValue;
|
||||||
CPTextField _brightnessValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
||||||
@@ -84,14 +81,72 @@
|
|||||||
[_redSlider setTarget: self];
|
[_redSlider setTarget: self];
|
||||||
[_redSlider setAction: @selector(sliderChanged:)];
|
[_redSlider setAction: @selector(sliderChanged:)];
|
||||||
[_redSlider setAutoresizingMask: CPViewWidthSizable];
|
[_redSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
|
var updateFunction = function(aDOMEvent)
|
||||||
|
{
|
||||||
|
if(isNaN(this.value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch(this)
|
||||||
|
{
|
||||||
|
case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||||
|
//[self sliderChanged: _redSlider];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||||
|
//[self sliderChanged: _greenSlider];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||||
|
//[self sliderChanged: _blueSlider];
|
||||||
|
break;
|
||||||
|
|
||||||
// red value input box
|
case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(this.value), 360), 0)];
|
||||||
_redValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 30, 45, 29)];
|
//[self sliderChanged: _hueSlider];
|
||||||
[_redValue setAutoresizingMask: CPViewMinXMargin];
|
break;
|
||||||
[_redValue setEditable: YES];
|
|
||||||
[_redValue setBezeled: YES];
|
case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(this.value), 100), 0)];
|
||||||
[_redValue setDelegate: self];
|
//[self sliderChanged: _saturationSlider];
|
||||||
[_contentView addSubview: _redValue];
|
break;
|
||||||
|
|
||||||
|
case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(this.value), 100), 0)];
|
||||||
|
//[self sliderChanged: _brightnessSlider];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blur();
|
||||||
|
};
|
||||||
|
|
||||||
|
var keypressFunction = function(aDOMEvent)
|
||||||
|
{
|
||||||
|
aDOMEvent = aDOMEvent || window.event;
|
||||||
|
if (aDOMEvent.keyCode == 13)
|
||||||
|
{
|
||||||
|
updateFunction(aDOMEvent);
|
||||||
|
|
||||||
|
if(aDOMEvent.preventDefault)
|
||||||
|
aDOMEvent.preventDefault();
|
||||||
|
else if(aDOMEvent.stopPropagation)
|
||||||
|
aDOMEvent.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//red value input box
|
||||||
|
var redValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 35, 45, 20)];
|
||||||
|
[redValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
|
|
||||||
|
_redValue = document.createElement("input");
|
||||||
|
_redValue.style.width = "40px";
|
||||||
|
_redValue.style.backgroundColor = "transparent";
|
||||||
|
_redValue.style.border = "1px solid black";
|
||||||
|
_redValue.style.color = "black";
|
||||||
|
_redValue.style.position = "absolute";
|
||||||
|
_redValue.style.top = "0px";
|
||||||
|
_redValue.style.left = "0px";
|
||||||
|
_redValue.onchange = updateFunction;
|
||||||
|
|
||||||
|
redValue._DOMElement.appendChild(_redValue);
|
||||||
|
[_contentView addSubview: redValue];
|
||||||
|
|
||||||
_greenLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 58, 15, 20)];
|
_greenLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 58, 15, 20)];
|
||||||
[_greenLabel setStringValue: "G"];
|
[_greenLabel setStringValue: "G"];
|
||||||
@@ -104,13 +159,15 @@
|
|||||||
[_greenSlider setAction: @selector(sliderChanged:)];
|
[_greenSlider setAction: @selector(sliderChanged:)];
|
||||||
[_greenSlider setAutoresizingMask: CPViewWidthSizable];
|
[_greenSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
// green value input box
|
//green value input box
|
||||||
_greenValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 53, 45, 29)];
|
var greenValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 58, 45, 20)];
|
||||||
[_greenValue setAutoresizingMask: CPViewMinXMargin];
|
[greenValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_greenValue setEditable: YES];
|
|
||||||
[_greenValue setBezeled: YES];
|
_greenValue = _redValue.cloneNode(false);
|
||||||
[_greenValue setDelegate: self];
|
_greenValue.onchange = updateFunction;
|
||||||
[_contentView addSubview: _greenValue];
|
|
||||||
|
greenValue._DOMElement.appendChild(_greenValue);
|
||||||
|
[_contentView addSubview: greenValue];
|
||||||
|
|
||||||
_blueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 81, 15, 20)];
|
_blueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 81, 15, 20)];
|
||||||
[_blueLabel setStringValue: "B"];
|
[_blueLabel setStringValue: "B"];
|
||||||
@@ -123,13 +180,15 @@
|
|||||||
[_blueSlider setAction: @selector(sliderChanged:)];
|
[_blueSlider setAction: @selector(sliderChanged:)];
|
||||||
[_blueSlider setAutoresizingMask: CPViewWidthSizable];
|
[_blueSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
// blue value input box
|
//blue value input box
|
||||||
_blueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 76, 45, 29)];
|
var blueValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 81, 45, 20)];
|
||||||
[_blueValue setAutoresizingMask: CPViewMinXMargin];
|
[blueValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_blueValue setEditable: YES];
|
|
||||||
[_blueValue setBezeled: YES];
|
_blueValue = _redValue.cloneNode(false);
|
||||||
[_blueValue setDelegate: self];
|
_blueValue.onchange = updateFunction;
|
||||||
[_contentView addSubview: _blueValue];
|
|
||||||
|
blueValue._DOMElement.appendChild(_blueValue);
|
||||||
|
[_contentView addSubview: blueValue];
|
||||||
|
|
||||||
_hsbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 120, 190, 20)];
|
_hsbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 120, 190, 20)];
|
||||||
[_hsbLabel setStringValue: "Hue, Saturation, Brightness"];
|
[_hsbLabel setStringValue: "Hue, Saturation, Brightness"];
|
||||||
@@ -146,14 +205,16 @@
|
|||||||
[_hueSlider setAction: @selector(sliderChanged:)];
|
[_hueSlider setAction: @selector(sliderChanged:)];
|
||||||
[_hueSlider setAutoresizingMask: CPViewWidthSizable];
|
[_hueSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
// hue value input box
|
//red value input box
|
||||||
_hueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 140, 45, 29)];
|
var hueValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 145, 45, 20)];
|
||||||
[_hueValue setAutoresizingMask: CPViewMinXMargin];
|
[hueValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_hueValue setEditable: YES];
|
|
||||||
[_hueValue setBezeled: YES];
|
|
||||||
[_hueValue setDelegate: self];
|
|
||||||
[_contentView addSubview: _hueValue];
|
|
||||||
|
|
||||||
|
_hueValue = _redValue.cloneNode(false);
|
||||||
|
_hueValue.onchange = updateFunction;
|
||||||
|
|
||||||
|
hueValue._DOMElement.appendChild(_hueValue);
|
||||||
|
[_contentView addSubview: hueValue];
|
||||||
|
|
||||||
_saturationLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 168, 15, 20)];
|
_saturationLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 168, 15, 20)];
|
||||||
[_saturationLabel setStringValue: "S"];
|
[_saturationLabel setStringValue: "S"];
|
||||||
[_saturationLabel setTextColor:[CPColor blackColor]];
|
[_saturationLabel setTextColor:[CPColor blackColor]];
|
||||||
@@ -165,13 +226,15 @@
|
|||||||
[_saturationSlider setAction: @selector(sliderChanged:)];
|
[_saturationSlider setAction: @selector(sliderChanged:)];
|
||||||
[_saturationSlider setAutoresizingMask: CPViewWidthSizable];
|
[_saturationSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
// saturation value input box
|
//green value input box
|
||||||
_saturationValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 163, 45, 29)];
|
var saturationValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 168, 45, 20)];
|
||||||
[_saturationValue setAutoresizingMask: CPViewMinXMargin];
|
[saturationValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_saturationValue setEditable: YES];
|
|
||||||
[_saturationValue setBezeled: YES];
|
_saturationValue = _redValue.cloneNode(false);
|
||||||
[_saturationValue setDelegate: self];
|
_saturationValue.onchange = updateFunction;
|
||||||
[_contentView addSubview: _saturationValue];
|
|
||||||
|
saturationValue._DOMElement.appendChild(_saturationValue);
|
||||||
|
[_contentView addSubview: saturationValue];
|
||||||
|
|
||||||
_brightnessLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 191, 15, 20)];
|
_brightnessLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 191, 15, 20)];
|
||||||
[_brightnessLabel setStringValue: "B"];
|
[_brightnessLabel setStringValue: "B"];
|
||||||
@@ -184,25 +247,49 @@
|
|||||||
[_brightnessSlider setAction: @selector(sliderChanged:)];
|
[_brightnessSlider setAction: @selector(sliderChanged:)];
|
||||||
[_brightnessSlider setAutoresizingMask: CPViewWidthSizable];
|
[_brightnessSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
// brightness value input box
|
//blue value input box
|
||||||
_brightnessValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 186, 45, 29)];
|
var brightnessValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 191, 45, 20)];
|
||||||
[_brightnessValue setAutoresizingMask: CPViewMinXMargin];
|
[brightnessValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_brightnessValue setEditable: YES];
|
|
||||||
[_brightnessValue setBezeled: YES];
|
_brightnessValue = _redValue.cloneNode(false);
|
||||||
[_brightnessValue setDelegate: self];
|
_brightnessValue.onchange = updateFunction;
|
||||||
[_contentView addSubview: _brightnessValue];
|
|
||||||
|
brightnessValue._DOMElement.appendChild(_brightnessValue);
|
||||||
|
[_contentView addSubview: brightnessValue];
|
||||||
|
|
||||||
_hexLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 230, 30, 20)];
|
_hexLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 230, 30, 20)];
|
||||||
[_hexLabel setStringValue: "Hex"];
|
[_hexLabel setStringValue: "Hex"];
|
||||||
[_hexLabel setTextColor:[CPColor blackColor]];
|
[_hexLabel setTextColor:[CPColor blackColor]];
|
||||||
|
|
||||||
//hex input box
|
//hex input box
|
||||||
_hexValue = [[CPTextField alloc] initWithFrame: CGRectMake(32, 225, 80, 29)];
|
_hexValue = _redValue.cloneNode(false);
|
||||||
[_hexValue setEditable: YES];
|
_hexValue.style.top = "228px";
|
||||||
[_hexValue setBezeled: YES];
|
_hexValue.style.width = "80px";
|
||||||
[_hexValue setDelegate: self];
|
_hexValue.style.left = "35px";
|
||||||
[_contentView addSubview: _hexValue];
|
_hexValue.onkeypress = function(aDOMEvent)
|
||||||
|
{
|
||||||
|
aDOMEvent = aDOMEvent || window.event;
|
||||||
|
if (aDOMEvent.keyCode == 13)
|
||||||
|
{
|
||||||
|
var newColor = [CPColor colorWithHexString: this.value];
|
||||||
|
|
||||||
|
if(newColor)
|
||||||
|
{
|
||||||
|
[self setColor: newColor];
|
||||||
|
[[self colorPanel] setColor: newColor];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(aDOMEvent.preventDefault)
|
||||||
|
aDOMEvent.preventDefault();
|
||||||
|
else if(aDOMEvent.stopPropagation)
|
||||||
|
aDOMEvent.stopPropagation();
|
||||||
|
|
||||||
|
this.blur();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_contentView._DOMElement.appendChild(_hexValue);
|
||||||
|
|
||||||
[_contentView addSubview: _rgbLabel];
|
[_contentView addSubview: _rgbLabel];
|
||||||
[_contentView addSubview: _redLabel];
|
[_contentView addSubview: _redLabel];
|
||||||
[_contentView addSubview: _greenLabel];
|
[_contentView addSubview: _greenLabel];
|
||||||
@@ -293,7 +380,7 @@
|
|||||||
|
|
||||||
- (void)updateHex:(CPColor)aColor
|
- (void)updateHex:(CPColor)aColor
|
||||||
{
|
{
|
||||||
[_hexValue setStringValue:[aColor hexString]];
|
_hexValue.value = [aColor hexString];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateRGBSliders:(CPColor)aColor
|
- (void)updateRGBSliders:(CPColor)aColor
|
||||||
@@ -307,13 +394,13 @@
|
|||||||
|
|
||||||
- (void)updateLabels
|
- (void)updateLabels
|
||||||
{
|
{
|
||||||
[_hueValue setStringValue: ROUND([_hueSlider floatValue])];
|
_hueValue.value = ROUND([_hueSlider floatValue]);
|
||||||
[_saturationValue setStringValue: ROUND([_saturationSlider floatValue])];
|
_saturationValue.value = ROUND([_saturationSlider floatValue]);
|
||||||
[_brightnessValue setStringValue: ROUND([_brightnessSlider floatValue])];
|
_brightnessValue.value = ROUND([_brightnessSlider floatValue]);
|
||||||
|
|
||||||
[_redValue setStringValue: ROUND([_redSlider floatValue] * 255)];
|
_redValue.value = ROUND([_redSlider floatValue] * 255);
|
||||||
[_greenValue setStringValue: ROUND([_greenSlider floatValue] * 255)];
|
_greenValue.value = ROUND([_greenSlider floatValue] * 255);
|
||||||
[_blueValue setStringValue: ROUND([_blueSlider floatValue] * 255)];
|
_blueValue.value = ROUND([_blueSlider floatValue] * 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPImage)provideNewButtonImage
|
- (CPImage)provideNewButtonImage
|
||||||
@@ -326,43 +413,4 @@
|
|||||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:CPColorPicker] pathForResource:"slider_button_h.png"] size:CGSizeMake(32, 32)];
|
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:CPColorPicker] pathForResource:"slider_button_h.png"] size:CGSizeMake(32, 32)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)controlTextDidEndEditing:(CPNotification)aNotification
|
|
||||||
{
|
|
||||||
var field = [aNotification object],
|
|
||||||
value = [[field stringValue] stringByTrimmingWhitespace];
|
|
||||||
if (field === _hexValue) {
|
|
||||||
var newColor = [CPColor colorWithHexString: value];
|
|
||||||
if (newColor) {
|
|
||||||
[self setColor: newColor];
|
|
||||||
[[self colorPanel] setColor: newColor];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch(field) {
|
|
||||||
case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
|
||||||
[self sliderChanged: _redSlider];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
|
||||||
[self sliderChanged: _greenSlider];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
|
||||||
[self sliderChanged: _blueSlider];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(value), 360), 0)];
|
|
||||||
[self sliderChanged: _hueSlider];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
|
|
||||||
[self sliderChanged: _saturationSlider];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
|
|
||||||
[self sliderChanged: _brightnessSlider];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+114
-283
@@ -20,10 +20,13 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPButtonBar.j"
|
|
||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
#include "Platform/Platform.h"
|
||||||
|
#include "Platform/DOM/CPDOMDisplayServer.h"
|
||||||
|
|
||||||
|
|
||||||
CPSplitViewDidResizeSubviewsNotification = @"CPSplitViewDidResizeSubviewsNotification";
|
CPSplitViewDidResizeSubviewsNotification = @"CPSplitViewDidResizeSubviewsNotification";
|
||||||
CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotification";
|
CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotification";
|
||||||
@@ -40,32 +43,18 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
id _delegate;
|
id _delegate;
|
||||||
BOOL _isVertical;
|
BOOL _isVertical;
|
||||||
BOOL _isPaneSplitter;
|
BOOL _isPaneSplitter;
|
||||||
|
|
||||||
int _currentDivider;
|
int _currentDivider;
|
||||||
float _initialOffset;
|
float _initialOffset;
|
||||||
float _preCollapsePosition;
|
|
||||||
|
|
||||||
CPString _originComponent;
|
CPString _originComponent;
|
||||||
CPString _sizeComponent;
|
CPString _sizeComponent;
|
||||||
|
|
||||||
CPArray _DOMDividerElements;
|
CPArray _DOMDividerElements;
|
||||||
CPString _dividerImagePath;
|
CPString _dividerImagePath;
|
||||||
int _drawingDivider;
|
int _drawingDivider;
|
||||||
|
|
||||||
BOOL _needsResizeSubviews;
|
BOOL _needsResizeSubviews;
|
||||||
|
|
||||||
CPArray _buttonBars;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return @"splitview";
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return [CPDictionary dictionaryWithObjects:[10.0, 1.0, [CPColor grayColor]]
|
|
||||||
forKeys:[@"divider-thickness", @"pane-divider-thickness", @"pane-divider-color"]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -86,19 +75,18 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
if (self = [super initWithFrame:aFrame])
|
if (self = [super initWithFrame:aFrame])
|
||||||
{
|
{
|
||||||
_currentDivider = CPNotFound;
|
_currentDivider = CPNotFound;
|
||||||
|
|
||||||
_DOMDividerElements = [];
|
_DOMDividerElements = [];
|
||||||
_buttonBars = [];
|
|
||||||
|
|
||||||
[self _setVertical:YES];
|
[self _setVertical:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)dividerThickness
|
- (float)dividerThickness
|
||||||
{
|
{
|
||||||
return [self currentValueForThemeAttribute:[self isPaneSplitter] ? @"pane-divider-thickness" : @"divider-thickness"];
|
return _isPaneSplitter ? 1.0 : 10.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isVertical
|
- (BOOL)isVertical
|
||||||
@@ -110,13 +98,13 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
{
|
{
|
||||||
if (![self _setVertical:shouldBeVertical])
|
if (![self _setVertical:shouldBeVertical])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Just re-adjust evenly.
|
// Just re-adjust evenly.
|
||||||
var frame = [self frame],
|
var frame = [self frame],
|
||||||
dividerThickness = [self dividerThickness];
|
dividerThickness = [self dividerThickness];
|
||||||
|
|
||||||
[self _postNotificationWillResize];
|
[self _postNotificationWillResize];
|
||||||
|
|
||||||
var eachSize = ROUND((frame.size[_sizeComponent] - dividerThickness * (_subviews.length - 1)) / _subviews.length),
|
var eachSize = ROUND((frame.size[_sizeComponent] - dividerThickness * (_subviews.length - 1)) / _subviews.length),
|
||||||
index = 0,
|
index = 0,
|
||||||
count = _subviews.length;
|
count = _subviews.length;
|
||||||
@@ -127,9 +115,9 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
else
|
else
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
[_subviews[index] setFrame:CGRectMake(0, ROUND((eachSize + dividerThickness) * index), frame.size.width, eachSize)];
|
[_subviews[index] setFrame:CGRectMake(0, ROUND((eachSize + dividerThickness) * index), frame.size.width, eachSize)];
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
|
|
||||||
[self _postNotificationDidResize];
|
[self _postNotificationDidResize];
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -137,13 +125,13 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
- (BOOL)_setVertical:(BOOL)shouldBeVertical
|
- (BOOL)_setVertical:(BOOL)shouldBeVertical
|
||||||
{
|
{
|
||||||
var changed = (_isVertical != shouldBeVertical);
|
var changed = (_isVertical != shouldBeVertical);
|
||||||
|
|
||||||
_isVertical = shouldBeVertical;
|
_isVertical = shouldBeVertical;
|
||||||
|
|
||||||
_originComponent = [self isVertical] ? "x" : "y";
|
_originComponent = [self isVertical] ? "x" : "y";
|
||||||
_sizeComponent = [self isVertical] ? "width" : "height";
|
_sizeComponent = [self isVertical] ? "width" : "height";
|
||||||
_dividerImagePath = [self isVertical] ? [CPSplitViewVerticalImage filename] : [CPSplitViewHorizontalImage filename];
|
_dividerImagePath = [self isVertical] ? [CPSplitViewVerticalImage filename] : [CPSplitViewHorizontalImage filename];
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,21 +144,20 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
{
|
{
|
||||||
if (_isPaneSplitter == shouldBePaneSplitter)
|
if (_isPaneSplitter == shouldBePaneSplitter)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_isPaneSplitter = shouldBePaneSplitter;
|
_isPaneSplitter = shouldBePaneSplitter;
|
||||||
|
|
||||||
if (_DOMDividerElements[_drawingDivider])
|
#if PLATFORM(DOM)
|
||||||
[self _setupDOMDivider];
|
_DOMDividerElements = [];
|
||||||
|
#endif
|
||||||
|
|
||||||
// The divider changes size when pane splitter mode is toggled, so the
|
|
||||||
// subviews need to change size too.
|
|
||||||
_needsResizeSubviews = YES;
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didAddSubview:(CPView)aSubview
|
- (void)didAddSubview:(CPView)aSubview
|
||||||
{
|
{
|
||||||
_needsResizeSubviews = YES;
|
_needsResizeSubviews = YES;
|
||||||
|
// [self adjustSubviews];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isSubviewCollapsed:(CPView)subview
|
- (BOOL)isSubviewCollapsed:(CPView)subview
|
||||||
@@ -182,9 +169,9 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
{
|
{
|
||||||
var frame = [_subviews[aDivider] frame],
|
var frame = [_subviews[aDivider] frame],
|
||||||
rect = CGRectMakeZero();
|
rect = CGRectMakeZero();
|
||||||
|
|
||||||
rect.size = [self frame].size;
|
rect.size = [self frame].size;
|
||||||
|
|
||||||
rect.size[_sizeComponent] = [self dividerThickness];
|
rect.size[_sizeComponent] = [self dividerThickness];
|
||||||
rect.origin[_originComponent] = frame.origin[_originComponent] + frame.size[_sizeComponent];
|
rect.origin[_originComponent] = frame.origin[_originComponent] + frame.size[_sizeComponent];
|
||||||
|
|
||||||
@@ -193,19 +180,20 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
|
|
||||||
- (CGRect)effectiveRectOfDividerAtIndex:(int)aDivider
|
- (CGRect)effectiveRectOfDividerAtIndex:(int)aDivider
|
||||||
{
|
{
|
||||||
var realRect = [self rectOfDividerAtIndex:aDivider],
|
var realRect = [self rectOfDividerAtIndex:aDivider];
|
||||||
padding = 2;
|
|
||||||
|
var padding = 2;
|
||||||
|
|
||||||
realRect.size[_sizeComponent] += padding * 2;
|
realRect.size[_sizeComponent] += padding * 2;
|
||||||
realRect.origin[_originComponent] -= padding;
|
realRect.origin[_originComponent] -= padding;
|
||||||
|
|
||||||
return realRect;
|
return realRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)rect
|
- (void)drawRect:(CGRect)rect
|
||||||
{
|
{
|
||||||
var count = [_subviews count] - 1;
|
var count = [_subviews count] - 1;
|
||||||
|
|
||||||
while ((count--) > 0)
|
while ((count--) > 0)
|
||||||
{
|
{
|
||||||
_drawingDivider = count;
|
_drawingDivider = count;
|
||||||
@@ -219,33 +207,29 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
if (!_DOMDividerElements[_drawingDivider])
|
if (!_DOMDividerElements[_drawingDivider])
|
||||||
{
|
{
|
||||||
_DOMDividerElements[_drawingDivider] = document.createElement("div");
|
_DOMDividerElements[_drawingDivider] = document.createElement("div");
|
||||||
|
_DOMDividerElements[_drawingDivider].style.cursor = "move";
|
||||||
_DOMDividerElements[_drawingDivider].style.position = "absolute";
|
_DOMDividerElements[_drawingDivider].style.position = "absolute";
|
||||||
_DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
|
_DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
|
||||||
|
|
||||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
|
CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
|
||||||
}
|
|
||||||
|
|
||||||
[self _setupDOMDivider];
|
if (_isPaneSplitter)
|
||||||
|
{
|
||||||
|
_DOMDividerElements[_drawingDivider].style.backgroundColor = "#A5A5A5";
|
||||||
|
_DOMDividerElements[_drawingDivider].style.backgroundImage = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_DOMDividerElements[_drawingDivider].style.backgroundColor = "";
|
||||||
|
_DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect));
|
CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect));
|
||||||
CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
|
CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_setupDOMDivider
|
|
||||||
{
|
|
||||||
if (_isPaneSplitter)
|
|
||||||
{
|
|
||||||
_DOMDividerElements[_drawingDivider].style.backgroundColor = [[self currentValueForThemeAttribute:@"pane-divider-color"] cssString];
|
|
||||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_DOMDividerElements[_drawingDivider].style.backgroundColor = "";
|
|
||||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillDraw
|
- (void)viewWillDraw
|
||||||
{
|
{
|
||||||
[self _adjustSubviewsWithCalculatedSize];
|
[self _adjustSubviewsWithCalculatedSize];
|
||||||
@@ -255,13 +239,13 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
{
|
{
|
||||||
if (!_needsResizeSubviews)
|
if (!_needsResizeSubviews)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_needsResizeSubviews = NO;
|
_needsResizeSubviews = NO;
|
||||||
|
|
||||||
var subviews = [self subviews],
|
var subviews = [self subviews],
|
||||||
count = subviews.length,
|
count = subviews.length,
|
||||||
oldSize = CGSizeMakeZero();
|
oldSize = CGSizeMakeZero();
|
||||||
|
|
||||||
if ([self isVertical])
|
if ([self isVertical])
|
||||||
{
|
{
|
||||||
oldSize.width += [self dividerThickness] * (count - 1);
|
oldSize.width += [self dividerThickness] * (count - 1);
|
||||||
@@ -272,10 +256,10 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
oldSize.width = CGRectGetWidth([self frame]);
|
oldSize.width = CGRectGetWidth([self frame]);
|
||||||
oldSize.height += [self dividerThickness] * (count - 1);
|
oldSize.height += [self dividerThickness] * (count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
oldSize[_sizeComponent] += [subviews[count] frame].size[_sizeComponent];
|
oldSize[_sizeComponent] += [subviews[count] frame].size[_sizeComponent];
|
||||||
|
|
||||||
[self resizeSubviewsWithOldSize:oldSize];
|
[self resizeSubviewsWithOldSize:oldSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,41 +268,31 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
var frame = [_subviews[anIndex] frame],
|
var frame = [_subviews[anIndex] frame],
|
||||||
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent],
|
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent],
|
||||||
effectiveRect = [self effectiveRectOfDividerAtIndex:anIndex],
|
effectiveRect = [self effectiveRectOfDividerAtIndex:anIndex],
|
||||||
buttonBar = _buttonBars[anIndex],
|
|
||||||
buttonBarRect = null,
|
|
||||||
additionalRect = null;
|
additionalRect = null;
|
||||||
|
|
||||||
if (buttonBar != null)
|
|
||||||
{
|
|
||||||
buttonBarRect = [buttonBar resizeControlFrame];
|
|
||||||
buttonBarRect.origin = [self convertPoint:buttonBarRect.origin fromView:buttonBar];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:effectiveRect:forDrawnRect:ofDividerAtIndex:)])
|
if ([_delegate respondsToSelector:@selector(splitView:effectiveRect:forDrawnRect:ofDividerAtIndex:)])
|
||||||
effectiveRect = [_delegate splitView:self effectiveRect:effectiveRect forDrawnRect:effectiveRect ofDividerAtIndex:anIndex];
|
effectiveRect = [_delegate splitView:self effectiveRect:effectiveRect forDrawnRect:effectiveRect ofDividerAtIndex:anIndex];
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:additionalEffectiveRectOfDividerAtIndex:)])
|
if ([_delegate respondsToSelector:@selector(splitView:additionalEffectiveRectOfDividerAtIndex:)])
|
||||||
additionalRect = [_delegate splitView:self additionalEffectiveRectOfDividerAtIndex:anIndex];
|
additionalRect = [_delegate splitView:self additionalEffectiveRectOfDividerAtIndex:anIndex];
|
||||||
|
|
||||||
return CGRectContainsPoint(effectiveRect, aPoint) ||
|
return CGRectContainsPoint(effectiveRect, aPoint) || (additionalRect && CGRectContainsPoint(additionalRect, aPoint));
|
||||||
(additionalRect && CGRectContainsPoint(additionalRect, aPoint)) ||
|
|
||||||
(buttonBarRect && CGRectContainsPoint(buttonBarRect, aPoint));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPView)hitTest:(CGPoint)aPoint
|
- (CPView)hitTest:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
if ([self isHidden] || ![self hitTests] || !CGRectContainsPoint([self frame], aPoint))
|
if ([self isHidden] || ![self hitTests] || !CGRectContainsPoint([self frame], aPoint))
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
var point = [self convertPoint:aPoint fromView:[self superview]],
|
var point = [self convertPoint:aPoint fromView:[self superview]];
|
||||||
count = [_subviews count] - 1;
|
|
||||||
|
var count = [_subviews count] - 1;
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
if ([self cursorAtPoint:point hitDividerAtIndex:i])
|
if ([self cursorAtPoint:point hitDividerAtIndex:i])
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [super hitTest:aPoint];
|
return [super hitTest:aPoint];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,19 +303,18 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
- (void)trackDivider:(CPEvent)anEvent
|
- (void)trackDivider:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var type = [anEvent type];
|
var type = [anEvent type];
|
||||||
|
|
||||||
if (type == CPLeftMouseUp)
|
if (type == CPLeftMouseUp)
|
||||||
{
|
{
|
||||||
if (_currentDivider != CPNotFound)
|
if (_currentDivider != CPNotFound)
|
||||||
{
|
{
|
||||||
_currentDivider = CPNotFound;
|
_currentDivider = CPNotFound;
|
||||||
[self _updateResizeCursor:anEvent];
|
|
||||||
[self _postNotificationDidResize];
|
[self _postNotificationDidResize];
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == CPLeftMouseDown)
|
if (type == CPLeftMouseDown)
|
||||||
{
|
{
|
||||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
@@ -358,21 +331,21 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
if ([anEvent clickCount] == 2 &&
|
if ([anEvent clickCount] == 2 &&
|
||||||
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
|
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
|
||||||
[_delegate respondsToSelector:@selector(splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex:)])
|
[_delegate respondsToSelector:@selector(splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex:)])
|
||||||
{
|
{
|
||||||
var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
|
var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
|
||||||
maxPosition = [self maxPossiblePositionOfDividerAtIndex:i];
|
maxPosition = [self maxPossiblePositionOfDividerAtIndex:i];
|
||||||
|
|
||||||
if ([_delegate splitView:self canCollapseSubview:_subviews[i]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
|
if ([_delegate splitView:self canCollapseSubview:_subviews[i]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
|
||||||
{
|
{
|
||||||
if ([self isSubviewCollapsed:_subviews[i]])
|
if ([self isSubviewCollapsed:_subviews[i]])
|
||||||
[self setPosition:_preCollapsePosition ? _preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
[self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||||
else
|
else
|
||||||
[self setPosition:minPosition ofDividerAtIndex:i];
|
[self setPosition:minPosition ofDividerAtIndex:i];
|
||||||
}
|
}
|
||||||
else if ([_delegate splitView:self canCollapseSubview:_subviews[i + 1]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i + 1] forDoubleClickOnDividerAtIndex:i])
|
else if ([_delegate splitView:self canCollapseSubview:_subviews[i+1]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i+1] forDoubleClickOnDividerAtIndex:i])
|
||||||
{
|
{
|
||||||
if ([self isSubviewCollapsed:_subviews[i + 1]])
|
if ([self isSubviewCollapsed:_subviews[i+1]])
|
||||||
[self setPosition:_preCollapsePosition ? _preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
[self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||||
else
|
else
|
||||||
[self setPosition:maxPosition ofDividerAtIndex:i];
|
[self setPosition:maxPosition ofDividerAtIndex:i];
|
||||||
}
|
}
|
||||||
@@ -386,20 +359,15 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentDivider === CPNotFound)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
|
else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
|
||||||
{
|
{
|
||||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
|
|
||||||
[self setPosition:(point[_originComponent] + _initialOffset) ofDividerAtIndex:_currentDivider];
|
[self setPosition:(point[_originComponent] + _initialOffset) ofDividerAtIndex:_currentDivider];
|
||||||
// Cursor might change if we reach a resize limit.
|
|
||||||
[self _updateResizeCursor:anEvent];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackDivider:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
[CPApp setTarget:self selector:@selector(trackDivider:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,89 +377,13 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
[self trackDivider:anEvent];
|
[self trackDivider:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidMoveToWindow
|
|
||||||
{
|
|
||||||
// Enable split view resize cursors. Commented out pending CPTrackingArea implementation.
|
|
||||||
//[[self window] setAcceptsMouseMovedEvents:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseEntered:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
// Tracking code handles cursor by itself.
|
|
||||||
if (_currentDivider == CPNotFound)
|
|
||||||
[self _updateResizeCursor:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseMoved:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
if (_currentDivider == CPNotFound)
|
|
||||||
[self _updateResizeCursor:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseExited:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
if (_currentDivider == CPNotFound)
|
|
||||||
// FIXME: we should use CPCursor push/pop (if previous currentCursor != arrow).
|
|
||||||
[[CPCursor arrowCursor] set];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateResizeCursor:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
|
||||||
|
|
||||||
if ([anEvent type] === CPLeftMouseUp && ![[self window] acceptsMouseMovedEvents])
|
|
||||||
{
|
|
||||||
[[CPCursor arrowCursor] set];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0, count = [_subviews count] - 1; i < count; i++)
|
|
||||||
{
|
|
||||||
// If we are currently tracking, keep the resize cursor active even outside of hit areas.
|
|
||||||
if (_currentDivider === i || (_currentDivider == CPNotFound && [self cursorAtPoint:point hitDividerAtIndex:i]))
|
|
||||||
{
|
|
||||||
var frame = [_subviews[i] frame],
|
|
||||||
size = frame.size[_sizeComponent],
|
|
||||||
startPosition = frame.origin[_originComponent] + size,
|
|
||||||
canShrink = [self _realPositionForPosition:startPosition - 1 ofDividerAtIndex:i] < startPosition,
|
|
||||||
canGrow = [self _realPositionForPosition:startPosition + 1 ofDividerAtIndex:i] > startPosition,
|
|
||||||
cursor = [CPCursor arrowCursor];
|
|
||||||
|
|
||||||
if (size === 0)
|
|
||||||
canGrow = YES; // Subview is collapsed.
|
|
||||||
else if (!canShrink &&
|
|
||||||
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
|
|
||||||
[_delegate splitView:self canCollapseSubview:_subviews[i]])
|
|
||||||
canShrink = YES; // Subview is collapsible.
|
|
||||||
|
|
||||||
if (_isVertical && canShrink && canGrow)
|
|
||||||
cursor = [CPCursor resizeLeftRightCursor];
|
|
||||||
else if (_isVertical && canShrink)
|
|
||||||
cursor = [CPCursor resizeLeftCursor];
|
|
||||||
else if (_isVertical && canGrow)
|
|
||||||
cursor = [CPCursor resizeRightCursor];
|
|
||||||
else if (canShrink && canGrow)
|
|
||||||
cursor = [CPCursor resizeUpDownCursor];
|
|
||||||
else if (canShrink)
|
|
||||||
cursor = [CPCursor resizeUpCursor];
|
|
||||||
else if (canGrow)
|
|
||||||
cursor = [CPCursor resizeDownCursor];
|
|
||||||
|
|
||||||
[cursor set];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[CPCursor arrowCursor] set];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)maxPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
- (float)maxPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
||||||
{
|
{
|
||||||
var frame = [_subviews[dividerIndex + 1] frame];
|
var frame = [_subviews[dividerIndex + 1] frame];
|
||||||
|
|
||||||
if (dividerIndex + 1 < [_subviews count] - 1)
|
if (dividerIndex + 1 < [_subviews count] - 1)
|
||||||
return frame.origin[_originComponent] + frame.size[_sizeComponent] - [self dividerThickness];
|
return frame.origin[_originComponent] + frame.size[_sizeComponent] - [self dividerThickness];
|
||||||
else
|
else
|
||||||
return [self frame].size[_sizeComponent] - [self dividerThickness];
|
return [self frame].size[_sizeComponent] - [self dividerThickness];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,15 +392,17 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
if (dividerIndex > 0)
|
if (dividerIndex > 0)
|
||||||
{
|
{
|
||||||
var frame = [_subviews[dividerIndex - 1] frame];
|
var frame = [_subviews[dividerIndex - 1] frame];
|
||||||
|
|
||||||
return frame.origin[_originComponent] + frame.size[_sizeComponent] + [self dividerThickness];
|
return frame.origin[_originComponent] + frame.size[_sizeComponent] + [self dividerThickness];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)_realPositionForPosition:(float)position ofDividerAtIndex:(int)dividerIndex
|
- (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
|
||||||
{
|
{
|
||||||
|
[self _adjustSubviewsWithCalculatedSize];
|
||||||
|
|
||||||
// not sure where this should override other positions?
|
// not sure where this should override other positions?
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:constrainSplitPosition:ofSubviewAt:)])
|
if ([_delegate respondsToSelector:@selector(splitView:constrainSplitPosition:ofSubviewAt:)])
|
||||||
position = [_delegate splitView:self constrainSplitPosition:position ofSubviewAt:dividerIndex];
|
position = [_delegate splitView:self constrainSplitPosition:position ofSubviewAt:dividerIndex];
|
||||||
@@ -517,64 +411,47 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
proposedMin = [self minPossiblePositionOfDividerAtIndex:dividerIndex],
|
proposedMin = [self minPossiblePositionOfDividerAtIndex:dividerIndex],
|
||||||
actualMax = proposedMax,
|
actualMax = proposedMax,
|
||||||
actualMin = proposedMin;
|
actualMin = proposedMin;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
|
if([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
|
||||||
actualMin = [_delegate splitView:self constrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex];
|
actualMin = [_delegate splitView:self constrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex];
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
|
if([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
|
||||||
actualMax = [_delegate splitView:self constrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
|
actualMax = [_delegate splitView:self constrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
|
||||||
|
|
||||||
var viewA = _subviews[dividerIndex],
|
var frame = [self frame],
|
||||||
realPosition = MAX(MIN(position, actualMax), actualMin);
|
viewA = _subviews[dividerIndex],
|
||||||
|
frameA = [viewA frame],
|
||||||
|
viewB = _subviews[dividerIndex + 1],
|
||||||
|
frameB = [viewB frame];
|
||||||
|
|
||||||
|
var realPosition = MAX(MIN(position, actualMax), actualMin);
|
||||||
|
|
||||||
if (position < proposedMin + (actualMin - proposedMin) / 2)
|
if (position < proposedMin + (actualMin - proposedMin) / 2)
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
|
if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
|
||||||
if ([_delegate splitView:self canCollapseSubview:viewA])
|
if ([_delegate splitView:self canCollapseSubview:viewA])
|
||||||
realPosition = proposedMin;
|
realPosition = proposedMin;
|
||||||
|
|
||||||
return realPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
|
|
||||||
{
|
|
||||||
[self _adjustSubviewsWithCalculatedSize];
|
|
||||||
|
|
||||||
var realPosition = [self _realPositionForPosition:position ofDividerAtIndex:dividerIndex];
|
|
||||||
|
|
||||||
var viewA = _subviews[dividerIndex],
|
|
||||||
frameA = [viewA frame],
|
|
||||||
viewB = _subviews[dividerIndex + 1],
|
|
||||||
frameB = [viewB frame];
|
|
||||||
|
|
||||||
_preCollapsePosition = 0;
|
|
||||||
|
|
||||||
var preSize = frameA.size[_sizeComponent];
|
|
||||||
frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
|
frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
|
||||||
if (preSize !== 0 && frameA.size[_sizeComponent] === 0)
|
|
||||||
_preCollapsePosition = preSize;
|
|
||||||
[_subviews[dividerIndex] setFrame:frameA];
|
[_subviews[dividerIndex] setFrame:frameA];
|
||||||
|
|
||||||
preSize = frameB.size[_sizeComponent];
|
|
||||||
frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
|
frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
|
||||||
if (preSize !== 0 && frameB.size[_sizeComponent] === 0)
|
|
||||||
_preCollapsePosition = preSize;
|
|
||||||
frameB.origin[_originComponent] = realPosition + [self dividerThickness];
|
frameB.origin[_originComponent] = realPosition + [self dividerThickness];
|
||||||
[_subviews[dividerIndex + 1] setFrame:frameB];
|
[_subviews[dividerIndex + 1] setFrame:frameB];
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFrameSize:(CGSize)aSize
|
- (void)setFrameSize:(CGSize)aSize
|
||||||
{
|
{
|
||||||
[self _adjustSubviewsWithCalculatedSize];
|
[self _adjustSubviewsWithCalculatedSize];
|
||||||
|
|
||||||
[super setFrameSize:aSize];
|
[super setFrameSize:aSize];
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resizeSubviewsWithOldSize:(CPSize)oldSize
|
- (void)resizeSubviewsWithOldSize:(CPSize)oldSize
|
||||||
{
|
{
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:resizeSubviewsWithOldSize:)])
|
if ([_delegate respondsToSelector:@selector(splitView:resizeSubviewsWithOldSize:)])
|
||||||
{
|
{
|
||||||
[_delegate splitView:self resizeSubviewsWithOldSize:oldSize];
|
[_delegate splitView:self resizeSubviewsWithOldSize:oldSize];
|
||||||
@@ -582,7 +459,7 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self _postNotificationWillResize];
|
[self _postNotificationWillResize];
|
||||||
|
|
||||||
var index = 0,
|
var index = 0,
|
||||||
count = [_subviews count],
|
count = [_subviews count],
|
||||||
bounds = [self bounds],
|
bounds = [self bounds],
|
||||||
@@ -611,9 +488,8 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
totalSizableSpace = 0;
|
totalSizableSpace = 0;
|
||||||
|
|
||||||
var nonSizableSpace = totalSizableSpace ? bounds.size[_sizeComponent] - totalSizableSpace : 0,
|
var nonSizableSpace = totalSizableSpace ? bounds.size[_sizeComponent] - totalSizableSpace : 0,
|
||||||
remainingFlexibleSpace = bounds.size[_sizeComponent] - oldSize[_sizeComponent],
|
ratio = (bounds.size[_sizeComponent] - totalDividers*dividerThickness - nonSizableSpace) / (oldSize[_sizeComponent]- totalDividers*dividerThickness - nonSizableSpace),
|
||||||
oldDimension = (oldSize[_sizeComponent] - totalDividers * dividerThickness - nonSizableSpace),
|
remainingFlexibleSpace = bounds.size[_sizeComponent] - oldSize[_sizeComponent];
|
||||||
ratio = oldDimension <= 0 ? 0 : (bounds.size[_sizeComponent] - totalDividers * dividerThickness - nonSizableSpace) / oldDimension;
|
|
||||||
|
|
||||||
for (index = 0; index < count; ++index)
|
for (index = 0; index < count; ++index)
|
||||||
{
|
{
|
||||||
@@ -632,8 +508,10 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
}
|
}
|
||||||
else if (totalSizableSpace && !isSizable)
|
else if (totalSizableSpace && !isSizable)
|
||||||
viewFrame.size[_sizeComponent] = [view frame].size[_sizeComponent];
|
viewFrame.size[_sizeComponent] = [view frame].size[_sizeComponent];
|
||||||
|
else
|
||||||
bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness;
|
alert("SHOULD NEVER GET HERE");
|
||||||
|
|
||||||
|
bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness;
|
||||||
|
|
||||||
[view setFrame:viewFrame];
|
[view setFrame:viewFrame];
|
||||||
}
|
}
|
||||||
@@ -647,7 +525,7 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewDidResizeSubviewsNotification object:self];
|
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewDidResizeSubviewsNotification object:self];
|
||||||
if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
|
if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
|
||||||
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewWillResizeSubviewsNotification object:self];
|
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewWillResizeSubviewsNotification object:self];
|
||||||
|
|
||||||
_delegate = delegate;
|
_delegate = delegate;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
||||||
@@ -662,47 +540,6 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
object:self];
|
object:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the button bar who's resize control should act as a control for this splitview.
|
|
||||||
Each divider can have at most one button bar assigned to it, and that button bar must be
|
|
||||||
a subview of one of the split view's subviews.
|
|
||||||
|
|
||||||
Calling this method with nil as the button bar will remove any currently assigned button bar
|
|
||||||
for the divider at that index. Indexes will not be adjusted as new subviews are added, so you
|
|
||||||
should usually call this method after adding all the desired subviews to the split view.
|
|
||||||
|
|
||||||
This method will automatically configure the hasResizeControl and resizeControlIsLeftAligned
|
|
||||||
parameters of the button bar, and will override any currently set values.
|
|
||||||
*/
|
|
||||||
- (void)setButtonBar:(CPButtonBar)aButtonBar forDividerAtIndex:(unsigned)dividerIndex
|
|
||||||
{
|
|
||||||
if (!aButtonBar)
|
|
||||||
{
|
|
||||||
_buttonBars[dividerIndex] = nil;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var view = [aButtonBar superview],
|
|
||||||
subview = aButtonBar;
|
|
||||||
|
|
||||||
while (view && view !== self)
|
|
||||||
{
|
|
||||||
subview = view;
|
|
||||||
view = [view superview];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (view !== self)
|
|
||||||
[CPException raise:CPInvalidArgumentException
|
|
||||||
reason:@"CPSplitView button bar must be a subview of the split view."];
|
|
||||||
|
|
||||||
var viewIndex = [[self subviews] indexOfObject:subview];
|
|
||||||
|
|
||||||
[aButtonBar setHasResizeControl:YES];
|
|
||||||
[aButtonBar setResizeControlIsLeftAligned:dividerIndex < viewIndex];
|
|
||||||
|
|
||||||
_buttonBars[dividerIndex] = aButtonBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_postNotificationWillResize
|
- (void)_postNotificationWillResize
|
||||||
{
|
{
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification object:self];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification object:self];
|
||||||
@@ -717,33 +554,30 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
|
|
||||||
var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
||||||
CPSplitViewIsVerticalKey = "CPSplitViewIsVerticalKey",
|
CPSplitViewIsVerticalKey = "CPSplitViewIsVerticalKey",
|
||||||
CPSplitViewIsPaneSplitterKey = "CPSplitViewIsPaneSplitterKey",
|
CPSplitViewIsPaneSplitterKey = "CPSplitViewIsPaneSplitterKey";
|
||||||
CPSplitViewButtonBarsKey = "CPSplitViewButtonBarsKey";
|
|
||||||
|
|
||||||
@implementation CPSplitView (CPCoding)
|
@implementation CPSplitView (CPCoding)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initializes the split view by unarchiving data from \c aCoder.
|
Initializes the split view by unarchiving data from <code>aCoder</code>.
|
||||||
@param aCoder the coder containing the archived CPSplitView.
|
@param aCoder the coder containing the archived CPSplitView.
|
||||||
*/
|
*/
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super initWithCoder:aCoder];
|
self = [super initWithCoder:aCoder];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_currentDivider = CPNotFound;
|
_currentDivider = CPNotFound;
|
||||||
|
|
||||||
_DOMDividerElements = [];
|
_DOMDividerElements = [];
|
||||||
|
|
||||||
_buttonBars = [aCoder decodeObjectForKey:CPSplitViewButtonBarsKey] || [];
|
_delegate = [aCoder decodeObjectForKey:CPSplitViewDelegateKey];;
|
||||||
|
|
||||||
_delegate = [aCoder decodeObjectForKey:CPSplitViewDelegateKey];
|
|
||||||
|
|
||||||
_isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
|
_isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
|
||||||
[self _setVertical:[aCoder decodeBoolForKey:CPSplitViewIsVerticalKey]];
|
[self _setVertical:[aCoder decodeBoolForKey:CPSplitViewIsVerticalKey]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -754,12 +588,9 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
|||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
//FIXME how should we handle this?
|
|
||||||
//[aCoder encodeObject:_buttonBars forKey:CPSplitViewButtonBarsKey];
|
|
||||||
|
|
||||||
[aCoder encodeConditionalObject:_delegate forKey:CPSplitViewDelegateKey];
|
[aCoder encodeConditionalObject:_delegate forKey:CPSplitViewDelegateKey];
|
||||||
|
|
||||||
[aCoder encodeBool:_isVertical forKey:CPSplitViewIsVerticalKey];
|
[aCoder encodeBool:_isVertical forKey:CPSplitViewIsVerticalKey];
|
||||||
[aCoder encodeBool:_isPaneSplitter forKey:CPSplitViewIsPaneSplitterKey];
|
[aCoder encodeBool:_isPaneSplitter forKey:CPSplitViewIsPaneSplitterKey];
|
||||||
}
|
}
|
||||||
|
|||||||
+70
-22
@@ -22,23 +22,15 @@
|
|||||||
|
|
||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
|
|
||||||
@import "CPPlatformString.j"
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
#include "Platform/Platform.h"
|
||||||
|
|
||||||
|
|
||||||
var CPStringSizeWithFontInWidthCache = {};
|
var CPStringReferenceElement = nil,
|
||||||
|
CPStringDefaultFont = nil;
|
||||||
CPStringSizeCachingEnabled = YES;
|
|
||||||
|
|
||||||
@implementation CPString (CPStringDrawing)
|
@implementation CPString (CPStringDrawing)
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a dictionary with the items "ascender", "descender", "lineHeight"
|
|
||||||
*/
|
|
||||||
+ (CPDictionary)metricsOfFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
return [CPPlatformString metricsOfFont:aFont];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the string
|
Returns the string
|
||||||
*/
|
*/
|
||||||
@@ -54,19 +46,75 @@ CPStringSizeCachingEnabled = YES;
|
|||||||
|
|
||||||
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||||
{
|
{
|
||||||
if (!CPStringSizeCachingEnabled)
|
#if PLATFORM(DOM)
|
||||||
return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
if (!CPStringReferenceElement)
|
||||||
|
|
||||||
var cacheKey = self + [aFont cssString] + aWidth,
|
|
||||||
size = CPStringSizeWithFontInWidthCache[cacheKey];
|
|
||||||
|
|
||||||
if (size === undefined)
|
|
||||||
{
|
{
|
||||||
size = [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
CPStringReferenceElement = document.createElement("span");
|
||||||
CPStringSizeWithFontInWidthCache[cacheKey] = size;
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CGSizeMakeCopy(size);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+542
-204
@@ -1,8 +1,32 @@
|
|||||||
@import "CPBox.j"
|
/*
|
||||||
@import "CPSegmentedControl.j"
|
* CPTabView.j
|
||||||
|
* AppKit
|
||||||
|
*
|
||||||
|
* Created by Francisco Tolmasky.
|
||||||
|
* Copyright 2008, 280 North, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import "CPImageView.j"
|
||||||
@import "CPTabViewItem.j"
|
@import "CPTabViewItem.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
|
#include "CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Places tabs on top with a bezeled border.
|
Places tabs on top with a bezeled border.
|
||||||
@global
|
@global
|
||||||
@@ -10,7 +34,7 @@
|
|||||||
*/
|
*/
|
||||||
CPTopTabsBezelBorder = 0;
|
CPTopTabsBezelBorder = 0;
|
||||||
//CPLeftTabsBezelBorder = 1;
|
//CPLeftTabsBezelBorder = 1;
|
||||||
CPBottomTabsBezelBorder = 2;
|
//CPBottomTabsBezelBorder = 2;
|
||||||
//CPRightTabsBezelBorder = 3;
|
//CPRightTabsBezelBorder = 3;
|
||||||
/*
|
/*
|
||||||
Displays no tabs and has a bezeled border.
|
Displays no tabs and has a bezeled border.
|
||||||
@@ -31,55 +55,173 @@ CPNoTabsLineBorder = 5;
|
|||||||
*/
|
*/
|
||||||
CPNoTabsNoBorder = 6;
|
CPNoTabsNoBorder = 6;
|
||||||
|
|
||||||
|
var CPTabViewBezelBorderLeftImage = nil,
|
||||||
|
CPTabViewBackgroundCenterImage = nil,
|
||||||
|
CPTabViewBezelBorderRightImage = nil,
|
||||||
|
CPTabViewBezelBorderColor = nil,
|
||||||
|
CPTabViewBezelBorderBackgroundColor = nil;
|
||||||
|
|
||||||
|
var LEFT_INSET = 7.0,
|
||||||
|
RIGHT_INSET = 7.0;
|
||||||
|
|
||||||
var CPTabViewDidSelectTabViewItemSelector = 1,
|
var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||||
CPTabViewShouldSelectTabViewItemSelector = 2,
|
CPTabViewShouldSelectTabViewItemSelector = 2,
|
||||||
CPTabViewWillSelectTabViewItemSelector = 4,
|
CPTabViewWillSelectTabViewItemSelector = 4,
|
||||||
CPTabViewDidChangeNumberOfTabViewItemsSelector = 8;
|
CPTabViewDidChangeNumberOfTabViewItemsSelector = 8;
|
||||||
|
|
||||||
var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
/*!
|
||||||
|
@ingroup appkit
|
||||||
|
@class CPTabView
|
||||||
|
|
||||||
|
This class represents a view that has multiple subviews (CPTabViewItem) presented as individual tabs.
|
||||||
|
Only one CPTabViewItem is shown at a time, and other CPTabViewItems can be made visible
|
||||||
|
(one at a time) by clicking on the CPTabViewItem's tab at the top of the tab view.
|
||||||
|
|
||||||
|
THe currently selected CPTabViewItem is the view that is displayed.
|
||||||
|
*/
|
||||||
@implementation CPTabView : CPView
|
@implementation CPTabView : CPView
|
||||||
{
|
{
|
||||||
CPArray items;
|
CPView _labelsView;
|
||||||
|
CPView _backgroundView;
|
||||||
|
CPView _separatorView;
|
||||||
|
|
||||||
|
CPView _auxiliaryView;
|
||||||
|
CPView _contentView;
|
||||||
|
|
||||||
|
CPArray _tabViewItems;
|
||||||
|
CPTabViewItem _selectedTabViewItem;
|
||||||
|
|
||||||
CPSegmentedControl tabs;
|
CPTabViewType _tabViewType;
|
||||||
CPBox box;
|
|
||||||
|
id _delegate;
|
||||||
|
unsigned _delegateSelectors;
|
||||||
|
}
|
||||||
|
|
||||||
CPNumber selectedIndex;
|
/*
|
||||||
|
@ignore
|
||||||
|
*/
|
||||||
|
+ (void)initialize
|
||||||
|
{
|
||||||
|
if (self != CPTabView)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var bundle = [CPBundle bundleForClass:self],
|
||||||
|
|
||||||
|
emptyImage = [[CPImage alloc] initByReferencingFile:@"" size:CGSizeMake(7.0, 0.0)],
|
||||||
|
backgroundImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBackgroundCenter.png"] size:CGSizeMake(1.0, 1.0)],
|
||||||
|
|
||||||
|
bezelBorderLeftImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorderLeft.png"] size:CGSizeMake(7.0, 1.0)],
|
||||||
|
bezerBorderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorder.png"] size:CGSizeMake(1.0, 1.0)],
|
||||||
|
bezelBorderRightImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorderRight.png"] size:CGSizeMake(7.0, 1.0)];
|
||||||
|
|
||||||
|
CPTabViewBezelBorderBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||||
|
[
|
||||||
|
emptyImage,
|
||||||
|
emptyImage,
|
||||||
|
emptyImage,
|
||||||
|
|
||||||
CPTabViewType type;
|
bezelBorderLeftImage,
|
||||||
|
backgroundImage,
|
||||||
|
bezelBorderRightImage,
|
||||||
|
|
||||||
id delegate;
|
bezelBorderLeftImage,
|
||||||
unsigned delegateSelectors;
|
bezerBorderImage,
|
||||||
|
bezelBorderRightImage
|
||||||
|
]]];
|
||||||
|
|
||||||
|
CPTabViewBezelBorderColor = [CPColor colorWithPatternImage:bezerBorderImage];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@ignore
|
||||||
|
*/
|
||||||
|
+ (CPColor)bezelBorderColor
|
||||||
|
{
|
||||||
|
return CPTabViewBezelBorderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
items = [CPArray array];
|
_tabViewType = CPTopTabsBezelBorder;
|
||||||
|
_tabViewItems = [];
|
||||||
tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, HEIGHT_OF_SEGMENTED_CONTROL)];
|
|
||||||
[tabs setHitTests:NO];
|
|
||||||
|
|
||||||
box = [[CPBox alloc] initWithFrame:CGRectMake(0, HEIGHT_OF_SEGMENTED_CONTROL / 2, CGRectGetWidth(aFrame),
|
|
||||||
CGRectGetHeight(aFrame) - HEIGHT_OF_SEGMENTED_CONTROL)];
|
|
||||||
|
|
||||||
selectedIndex = CPNotFound;
|
|
||||||
|
|
||||||
[self setTabViewType:CPTopTabsBezelBorder];
|
|
||||||
[self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
|
|
||||||
|
|
||||||
[self addSubview:box];
|
|
||||||
[self addSubview:tabs];
|
|
||||||
|
|
||||||
[box setAutoresizingMask:CPViewWidthSizable];
|
|
||||||
[tabs setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)viewDidMoveToWindow
|
||||||
|
{
|
||||||
|
if (_tabViewType != CPTopTabsBezelBorder || _labelsView)
|
||||||
|
return;
|
||||||
|
|
||||||
|
[self _createBezelBorder];
|
||||||
|
[self layoutSubviews];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @ignore */
|
||||||
|
- (void)_createBezelBorder
|
||||||
|
{
|
||||||
|
var bounds = [self bounds];
|
||||||
|
|
||||||
|
_labelsView = [[_CPTabLabelsView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), 0.0)];
|
||||||
|
|
||||||
|
[_labelsView setTabView:self];
|
||||||
|
[_labelsView setAutoresizingMask:CPViewWidthSizable];
|
||||||
|
|
||||||
|
[self addSubview:_labelsView];
|
||||||
|
|
||||||
|
_backgroundView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
|
[_backgroundView setBackgroundColor:CPTabViewBezelBorderBackgroundColor];
|
||||||
|
|
||||||
|
[_backgroundView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
|
|
||||||
|
[self addSubview:_backgroundView];
|
||||||
|
|
||||||
|
_separatorView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
|
[_separatorView setBackgroundColor:[[self class] bezelBorderColor]];
|
||||||
|
[_separatorView setAutoresizingMask:CPViewWidthSizable | CPViewMaxYMargin];
|
||||||
|
|
||||||
|
[self addSubview:_separatorView];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Lays out the subviews
|
||||||
|
@ignore
|
||||||
|
*/
|
||||||
|
- (void)layoutSubviews
|
||||||
|
{
|
||||||
|
if (_tabViewType == CPTopTabsBezelBorder)
|
||||||
|
{
|
||||||
|
var backgroundRect = [self bounds],
|
||||||
|
labelsViewHeight = [_CPTabLabelsView height];
|
||||||
|
|
||||||
|
backgroundRect.origin.y += labelsViewHeight;
|
||||||
|
backgroundRect.size.height -= labelsViewHeight;
|
||||||
|
|
||||||
|
[_backgroundView setFrame:backgroundRect];
|
||||||
|
|
||||||
|
var auxiliaryViewHeight = 5.0;
|
||||||
|
|
||||||
|
if (_auxiliaryView)
|
||||||
|
{
|
||||||
|
auxiliaryViewHeight = CGRectGetHeight([_auxiliaryView frame]);
|
||||||
|
|
||||||
|
[_auxiliaryView setFrame:CGRectMake(LEFT_INSET, labelsViewHeight, CGRectGetWidth(backgroundRect) - LEFT_INSET - RIGHT_INSET, auxiliaryViewHeight)];
|
||||||
|
}
|
||||||
|
|
||||||
|
[_separatorView setFrame:CGRectMake(LEFT_INSET, labelsViewHeight + auxiliaryViewHeight, CGRectGetWidth(backgroundRect) - LEFT_INSET - RIGHT_INSET, 1.0)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPNoTabsNoBorder
|
||||||
|
[_contentView setFrame:[self contentRect]];
|
||||||
|
}
|
||||||
|
|
||||||
// Adding and Removing Tabs
|
// Adding and Removing Tabs
|
||||||
/*!
|
/*!
|
||||||
Adds a CPTabViewItem to the tab view.
|
Adds a CPTabViewItem to the tab view.
|
||||||
@@ -87,7 +229,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
|
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
{
|
{
|
||||||
[self insertTabViewItem:aTabViewItem atIndex:[items count]];
|
[self insertTabViewItem:aTabViewItem atIndex:[_tabViewItems count]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -98,13 +240,18 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex
|
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
[items insertObject:aTabViewItem atIndex:anIndex];
|
if (!_labelsView && _tabViewType == CPTopTabsBezelBorder)
|
||||||
|
[self _createBezelBorder];
|
||||||
|
|
||||||
|
[_tabViewItems insertObject:aTabViewItem atIndex:anIndex];
|
||||||
|
|
||||||
|
[_labelsView tabView:self didAddTabViewItem:aTabViewItem];
|
||||||
|
|
||||||
|
if ([_tabViewItems count] == 1)
|
||||||
|
[self selectFirstTabViewItem:self];
|
||||||
|
|
||||||
[self _updateItems];
|
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||||
[self _repositionTabs];
|
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||||
|
|
||||||
if (delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
|
||||||
[delegate tabViewDidChangeNumberOfTabViewItems:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -113,19 +260,12 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
|
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
{
|
{
|
||||||
for (var i = 0; i < [items count]; i++)
|
[_tabViewItems removeObjectIdenticalTo:aTabViewItem];
|
||||||
{
|
|
||||||
if ([items objectAtIndex:i] === aTabViewItem)
|
|
||||||
{
|
|
||||||
[items removeObjectAtIndex:i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[self _updateItems];
|
[_labelsView tabView:self didRemoveTabViewItem:aTabViewItem];
|
||||||
[self _repositionTabs];
|
|
||||||
|
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||||
if (delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||||
[delegate tabViewDidChangeNumberOfTabViewItems:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessing Tabs
|
// Accessing Tabs
|
||||||
@@ -135,7 +275,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
|
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
{
|
{
|
||||||
return [items indexOfObjectIdenticalTo:aTabViewItem];
|
return [_tabViewItems indexOfObjectIdenticalTo:aTabViewItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -144,11 +284,14 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
|
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
|
||||||
{
|
{
|
||||||
for (var index = [items count]; index >= 0; index--)
|
var index = 0,
|
||||||
if ([[items[index] identifier] isEqual:anIdentifier])
|
count = [_tabViewItems count];
|
||||||
|
|
||||||
|
for (; index < count; ++index)
|
||||||
|
if ([[_tabViewItems[index] identifier] isEqual:anIdentifier])
|
||||||
return index;
|
return index;
|
||||||
|
|
||||||
return CPNotFound;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -156,7 +299,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (unsigned)numberOfTabViewItems
|
- (unsigned)numberOfTabViewItems
|
||||||
{
|
{
|
||||||
return [items count];
|
return [_tabViewItems count];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -164,7 +307,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (CPTabViewItem)tabViewItemAtIndex:(unsigned)anIndex
|
- (CPTabViewItem)tabViewItemAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
return [items objectAtIndex:anIndex];
|
return _tabViewItems[anIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -172,7 +315,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (CPArray)tabViewItems
|
- (CPArray)tabViewItems
|
||||||
{
|
{
|
||||||
return [items copy]; // Copy?
|
return _tabViewItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selecting a Tab
|
// Selecting a Tab
|
||||||
@@ -182,10 +325,10 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)selectFirstTabViewItem:(id)aSender
|
- (void)selectFirstTabViewItem:(id)aSender
|
||||||
{
|
{
|
||||||
if ([items count] === 0)
|
var count = [_tabViewItems count];
|
||||||
return; // throw?
|
|
||||||
|
if (count)
|
||||||
[self selectTabViewItemAtIndex:0];
|
[self selectTabViewItemAtIndex:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -194,10 +337,10 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)selectLastTabViewItem:(id)aSender
|
- (void)selectLastTabViewItem:(id)aSender
|
||||||
{
|
{
|
||||||
if ([items count] === 0)
|
var count = [_tabViewItems count];
|
||||||
return; // throw?
|
|
||||||
|
if (count)
|
||||||
[self selectTabViewItemAtIndex:[items count] - 1];
|
[self selectTabViewItemAtIndex:count - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -206,16 +349,13 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)selectNextTabViewItem:(id)aSender
|
- (void)selectNextTabViewItem:(id)aSender
|
||||||
{
|
{
|
||||||
if (selectedIndex === CPNotFound)
|
if (!_selectedTabViewItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var nextIndex = selectedIndex + 1;
|
var index = [self indexOfTabViewItem:_selectedTabViewItem],
|
||||||
|
count = [_tabViewItems count];
|
||||||
if (nextIndex === [items count])
|
|
||||||
// does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:)
|
[self selectTabViewItemAtIndex:index + 1 % count];
|
||||||
return;
|
|
||||||
|
|
||||||
[self selectTabViewItemAtIndex:nextIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -224,15 +364,13 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)selectPreviousTabViewItem:(id)aSender
|
- (void)selectPreviousTabViewItem:(id)aSender
|
||||||
{
|
{
|
||||||
if (selectedIndex === CPNotFound)
|
if (!_selectedTabViewItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var previousIndex = selectedIndex - 1;
|
var index = [self indexOfTabViewItem:_selectedTabViewItem],
|
||||||
|
count = [_tabViewItems count];
|
||||||
if (previousIndex < 0)
|
|
||||||
return; // does nothing. See above.
|
[self selectTabViewItemAtIndex:index == 0 ? count : index - 1];
|
||||||
|
|
||||||
[self selectTabViewItemAtIndex:previousIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -241,7 +379,41 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)selectTabViewItem:(CPTabViewItem)aTabViewItem
|
- (void)selectTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
{
|
{
|
||||||
[self selectTabViewItemAtIndex:[self indexOfTabViewItem:aTabViewItem]];
|
if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
||||||
|
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
||||||
|
|
||||||
|
if (_selectedTabViewItem)
|
||||||
|
{
|
||||||
|
_selectedTabViewItem._tabState = CPBackgroundTab;
|
||||||
|
[_labelsView tabView:self didChangeStateOfTabViewItem:_selectedTabViewItem];
|
||||||
|
|
||||||
|
[_contentView removeFromSuperview];
|
||||||
|
[_auxiliaryView removeFromSuperview];
|
||||||
|
}
|
||||||
|
_selectedTabViewItem = aTabViewItem;
|
||||||
|
|
||||||
|
_selectedTabViewItem._tabState = CPSelectedTab;
|
||||||
|
|
||||||
|
_contentView = [_selectedTabViewItem view];
|
||||||
|
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
|
|
||||||
|
_auxiliaryView = [_selectedTabViewItem auxiliaryView];
|
||||||
|
[_auxiliaryView setAutoresizingMask:CPViewWidthSizable];
|
||||||
|
|
||||||
|
[self addSubview:_contentView];
|
||||||
|
|
||||||
|
if (_auxiliaryView)
|
||||||
|
[self addSubview:_auxiliaryView];
|
||||||
|
|
||||||
|
[_labelsView tabView:self didChangeStateOfTabViewItem:_selectedTabViewItem];
|
||||||
|
|
||||||
|
[self layoutSubviews];
|
||||||
|
|
||||||
|
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
||||||
|
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -250,24 +422,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)selectTabViewItemAtIndex:(unsigned)anIndex
|
- (void)selectTabViewItemAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
var aTabViewItem = [items objectAtIndex:anIndex];
|
[self selectTabViewItem:_tabViewItems[anIndex]];
|
||||||
|
|
||||||
if (anIndex === selectedIndex)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
|
|
||||||
|
|
||||||
if ((delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
|
||||||
[delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
|
||||||
|
|
||||||
[tabs selectSegmentWithTag:anIndex];
|
|
||||||
[self _setSelectedIndex:anIndex];
|
|
||||||
|
|
||||||
if (delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
|
||||||
[delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -275,43 +430,32 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (CPTabViewItem)selectedTabViewItem
|
- (CPTabViewItem)selectedTabViewItem
|
||||||
{
|
{
|
||||||
return [items objectAtIndex:selectedIndex];
|
return _selectedTabViewItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
/*!
|
/*!
|
||||||
Sets the tab view type.
|
Sets the tab view type.
|
||||||
@param aTabViewType the view type
|
@param aTabViewType the view type
|
||||||
*/
|
*/
|
||||||
- (void)setTabViewType:(CPTabViewType)aTabViewType
|
- (void)setTabViewType:(CPTabViewType)aTabViewType
|
||||||
{
|
{
|
||||||
if (type === aTabViewType)
|
if (_tabViewType == aTabViewType)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((type === CPTopTabsBezelBorder || type === CPBottomTabsBezelBorder)
|
_tabViewType = aTabViewType;
|
||||||
&& (aTabViewType !== CPTopTabsBezelBorder && aTabViewType !== CPBottomTabsBezelBorder))
|
|
||||||
[tabs removeFromSuperview];
|
if (_tabViewType == CPNoTabsBezelBorder || _tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder)
|
||||||
|
[_labelsView removeFromSuperview];
|
||||||
if ((type === CPNoTabsBezelBorder || type === CPNoTabsLineBorder || type === CPNoTabsNoBorder)
|
else if (![_labelsView superview])
|
||||||
&& (aTabViewType !== CPNoTabsBezelBorder && aTabViewType !== CPNoTabsBezelBorder && aTabViewType !== CPNoTabsNoBorder))
|
[self addSubview:_labelsView];
|
||||||
[self addSubview:tabs];
|
|
||||||
|
if (_tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder)
|
||||||
type = aTabViewType;
|
[_backgroundView removeFromSuperview];
|
||||||
|
else if (![_backgroundView superview])
|
||||||
switch (type)
|
[self addSubview:_backgroundView];
|
||||||
{
|
|
||||||
case CPTopTabsBezelBorder:
|
[self layoutSubviews];
|
||||||
case CPBottomTabsBezelBorder:
|
|
||||||
case CPNoTabsBezelBorder:
|
|
||||||
[box setBorderType:CPBezelBorder];
|
|
||||||
break;
|
|
||||||
case CPNoTabsLineBorder:
|
|
||||||
[box setBorderType:CPLineBorder];
|
|
||||||
break;
|
|
||||||
case CPNoTabsNoBorder:
|
|
||||||
[box setBorderType:CPNoBorder];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -319,15 +463,31 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (CPTabViewType)tabViewType
|
- (CPTabViewType)tabViewType
|
||||||
{
|
{
|
||||||
return type;
|
return _tabViewType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determining the Size
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's delegate.
|
Returns the content rectangle.
|
||||||
*/
|
*/
|
||||||
- (id)delegate
|
- (CGRect)contentRect
|
||||||
{
|
{
|
||||||
return delegate;
|
var contentRect = CGRectMakeCopy([self bounds]);
|
||||||
|
|
||||||
|
if (_tabViewType == CPTopTabsBezelBorder)
|
||||||
|
{
|
||||||
|
var labelsViewHeight = [_CPTabLabelsView height],
|
||||||
|
auxiliaryViewHeight = _auxiliaryView ? CGRectGetHeight([_auxiliaryView frame]) : 5.0,
|
||||||
|
separatorViewHeight = 1.0;
|
||||||
|
|
||||||
|
contentRect.origin.y += labelsViewHeight + auxiliaryViewHeight + separatorViewHeight;
|
||||||
|
contentRect.size.height -= labelsViewHeight + auxiliaryViewHeight + separatorViewHeight * 2.0; // 2 for the bottom border as well.
|
||||||
|
|
||||||
|
contentRect.origin.x += LEFT_INSET;
|
||||||
|
contentRect.size.width -= LEFT_INSET + RIGHT_INSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contentRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -336,74 +496,35 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24;
|
|||||||
*/
|
*/
|
||||||
- (void)setDelegate:(id)aDelegate
|
- (void)setDelegate:(id)aDelegate
|
||||||
{
|
{
|
||||||
if (delegate == aDelegate)
|
if (_delegate == aDelegate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_delegate = aDelegate;
|
||||||
|
|
||||||
|
_delegateSelectors = 0;
|
||||||
|
|
||||||
delegate = aDelegate;
|
if ([_delegate respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)])
|
||||||
|
_delegateSelectors |= CPTabViewShouldSelectTabViewItemSelector;
|
||||||
|
|
||||||
delegateSelectors = 0;
|
if ([_delegate respondsToSelector:@selector(tabView:willSelectTabViewItem:)])
|
||||||
|
_delegateSelectors |= CPTabViewWillSelectTabViewItemSelector;
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)])
|
if ([_delegate respondsToSelector:@selector(tabView:didSelectTabViewItem:)])
|
||||||
delegateSelectors |= CPTabViewShouldSelectTabViewItemSelector;
|
_delegateSelectors |= CPTabViewDidSelectTabViewItemSelector;
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(tabView:willSelectTabViewItem:)])
|
if ([_delegate respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)])
|
||||||
delegateSelectors |= CPTabViewWillSelectTabViewItemSelector;
|
_delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector;
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(tabView:didSelectTabViewItem:)])
|
|
||||||
delegateSelectors |= CPTabViewDidSelectTabViewItemSelector;
|
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)])
|
|
||||||
delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setBackgroundColor:(CPColor)aColor
|
//
|
||||||
{
|
|
||||||
[box setBackgroundColor:aColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var segmentIndex = [tabs testSegment:[tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
|
var location = [_labelsView convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||||
|
tabViewItem = [_labelsView representedTabViewItemAtPoint:location];
|
||||||
if (segmentIndex != CPNotFound)
|
|
||||||
{
|
if (tabViewItem)
|
||||||
[self selectTabViewItemAtIndex:segmentIndex];
|
[self selectTabViewItem:tabViewItem];
|
||||||
[tabs trackSegment:anEvent];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_repositionTabs
|
|
||||||
{
|
|
||||||
var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2,
|
|
||||||
verticalCenterOfTabs = CGRectGetHeight([tabs bounds]) / 2;
|
|
||||||
|
|
||||||
if (type === CPBottomTabsBezelBorder)
|
|
||||||
[tabs setCenter:CGPointMake(horizontalCenterOfSelf, CGRectGetHeight([self bounds]) - verticalCenterOfTabs)];
|
|
||||||
else
|
|
||||||
[tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setSelectedIndex:(CPNumber)index
|
|
||||||
{
|
|
||||||
selectedIndex = index;
|
|
||||||
|
|
||||||
[box setContentView:[[items objectAtIndex:selectedIndex] view]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateItems
|
|
||||||
{
|
|
||||||
[tabs setSegmentCount:[items count]];
|
|
||||||
|
|
||||||
for (var i = 0; i < [items count]; i++)
|
|
||||||
{
|
|
||||||
[tabs setLabel:[[items objectAtIndex:i] label] forSegment:i];
|
|
||||||
[tabs setTag:i forSegment:i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedIndex === CPNotFound)
|
|
||||||
{
|
|
||||||
[self selectFirstTabViewItem:self];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -419,17 +540,20 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
|||||||
{
|
{
|
||||||
if (self = [super initWithCoder:aCoder])
|
if (self = [super initWithCoder:aCoder])
|
||||||
{
|
{
|
||||||
type = [aCoder decodeIntForKey:CPTabViewTypeKey];
|
_tabViewType = [aCoder decodeIntForKey:CPTabViewTypeKey];
|
||||||
items = [];
|
_tabViewItems = [];
|
||||||
|
|
||||||
var encodedItems = [aCoder decodeObjectForKey:CPTabViewItemsKey];
|
// FIXME: this is somewhat hacky
|
||||||
for (var i = 0; encodedItems && i < encodedItems.length; i++)
|
[self _createBezelBorder];
|
||||||
[self insertTabViewItem:encodedItems[i] atIndex:i];
|
|
||||||
|
var items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
|
||||||
|
for (var i = 0; items && i < items.length; i++)
|
||||||
|
[self insertTabViewItem:items[i] atIndex:i];
|
||||||
|
|
||||||
var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
||||||
if (selected)
|
if (selected)
|
||||||
[self selectTabViewItem:selected];
|
[self selectTabViewItem:selected];
|
||||||
|
|
||||||
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,14 +562,228 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
|||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
|
var actualSubviews = _subviews;
|
||||||
|
_subviews = [];
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
_subviews = actualSubviews;
|
||||||
[aCoder encodeObject:items forKey:CPTabViewItemsKey];;
|
|
||||||
[aCoder encodeObject:[self selectedTabViewItem] forKey:CPTabViewSelectedItemKey];
|
[aCoder encodeObject:_tabViewItems forKey:CPTabViewItemsKey];;
|
||||||
|
[aCoder encodeObject:_selectedTabViewItem forKey:CPTabViewSelectedItemKey];
|
||||||
[aCoder encodeInt:type forKey:CPTabViewTypeKey];
|
|
||||||
|
[aCoder encodeInt:_tabViewType forKey:CPTabViewTypeKey];
|
||||||
[aCoder encodeConditionalObject:delegate forKey:CPTabViewDelegateKey];
|
|
||||||
|
[aCoder encodeConditionalObject:_delegate forKey:CPTabViewDelegateKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
var _CPTabLabelsViewBackgroundColor = nil,
|
||||||
|
_CPTabLabelsViewInsideMargin = 10.0,
|
||||||
|
_CPTabLabelsViewOutsideMargin = 15.0;
|
||||||
|
|
||||||
|
/* @ignore */
|
||||||
|
@implementation _CPTabLabelsView : CPView
|
||||||
|
{
|
||||||
|
CPTabView _tabView;
|
||||||
|
CPDictionary _tabLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)initialize
|
||||||
|
{
|
||||||
|
if (self != [_CPTabLabelsView class])
|
||||||
|
return;
|
||||||
|
|
||||||
|
var bundle = [CPBundle bundleForClass:self];
|
||||||
|
|
||||||
|
_CPTabLabelsViewBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
||||||
|
[
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewLeft.png"] size:CGSizeMake(12.0, 26.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewCenter.png"] size:CGSizeMake(1.0, 26.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewRight.png"] size:CGSizeMake(12.0, 26.0)]
|
||||||
|
] isVertical:NO]];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (float)height
|
||||||
|
{
|
||||||
|
return 26.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
|
{
|
||||||
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
_tabLabels = [];
|
||||||
|
|
||||||
|
[self setBackgroundColor:_CPTabLabelsViewBackgroundColor];
|
||||||
|
|
||||||
|
[self setFrameSize:CGSizeMake(CGRectGetWidth(aFrame), 26.0)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setTabView:(CPTabView)aTabView
|
||||||
|
{
|
||||||
|
_tabView = aTabView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CPTabView)tabView
|
||||||
|
{
|
||||||
|
return _tabView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tabView:(CPTabView)aTabView didAddTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
|
{
|
||||||
|
var label = [[_CPTabLabel alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
|
[label setTabViewItem:aTabViewItem];
|
||||||
|
|
||||||
|
_tabLabels.push(label);
|
||||||
|
|
||||||
|
[self addSubview:label];
|
||||||
|
|
||||||
|
[self layoutSubviews];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tabView:(CPTabView)aTabView didRemoveTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
|
{
|
||||||
|
var index = [aTabView indexOfTabViewItem:aTabViewItem],
|
||||||
|
label = _tabLabels[index];
|
||||||
|
|
||||||
|
[_tabLabels removeObjectAtIndex:index];
|
||||||
|
|
||||||
|
[label removeFromSuperview];
|
||||||
|
|
||||||
|
[self layoutSubviews];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)tabView:(CPTabView)aTabView didChangeStateOfTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
|
{
|
||||||
|
[_tabLabels[[aTabView indexOfTabViewItem:aTabViewItem]] setTabState:[aTabViewItem tabState]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CPTabViewItem)representedTabViewItemAtPoint:(CGPoint)aPoint
|
||||||
|
{
|
||||||
|
var index = 0,
|
||||||
|
count = _tabLabels.length;
|
||||||
|
|
||||||
|
for (; index < count; ++index)
|
||||||
|
{
|
||||||
|
var label = _tabLabels[index];
|
||||||
|
|
||||||
|
if (CGRectContainsPoint([label frame], aPoint))
|
||||||
|
return [label tabViewItem];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layoutSubviews
|
||||||
|
{
|
||||||
|
var index = 0,
|
||||||
|
count = _tabLabels.length,
|
||||||
|
width = (_CGRectGetWidth([self bounds]) - (count - 1) * _CPTabLabelsViewInsideMargin - 2 * _CPTabLabelsViewOutsideMargin) / count,
|
||||||
|
x = _CPTabLabelsViewOutsideMargin;
|
||||||
|
|
||||||
|
for (; index < count; ++index)
|
||||||
|
{
|
||||||
|
var label = _tabLabels[index],
|
||||||
|
frame = _CGRectMake(x, 8.0, width, 18.0);
|
||||||
|
|
||||||
|
[label setFrame:frame];
|
||||||
|
|
||||||
|
x = _CGRectGetMaxX(frame) + _CPTabLabelsViewInsideMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFrameSize:(CGSize)aSize
|
||||||
|
{
|
||||||
|
if (CGSizeEqualToSize([self frame].size, aSize))
|
||||||
|
return;
|
||||||
|
|
||||||
|
[super setFrameSize:aSize];
|
||||||
|
|
||||||
|
[self layoutSubviews];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
var _CPTabLabelBackgroundColor = nil,
|
||||||
|
_CPTabLabelSelectedBackgroundColor = nil;
|
||||||
|
|
||||||
|
/* @ignore */
|
||||||
|
@implementation _CPTabLabel : CPView
|
||||||
|
{
|
||||||
|
CPTabViewItem _tabViewItem;
|
||||||
|
CPTextField _labelField;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)initialize
|
||||||
|
{
|
||||||
|
if (self != [_CPTabLabel class])
|
||||||
|
return;
|
||||||
|
|
||||||
|
var bundle = [CPBundle bundleForClass:self];
|
||||||
|
|
||||||
|
_CPTabLabelBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
||||||
|
[
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundLeft.png"] size:CGSizeMake(6.0, 18.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundCenter.png"] size:CGSizeMake(1.0, 18.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundRight.png"] size:CGSizeMake(6.0, 18.0)]
|
||||||
|
] isVertical:NO]];
|
||||||
|
|
||||||
|
_CPTabLabelSelectedBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
||||||
|
[
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedLeft.png"] size:CGSizeMake(3.0, 18.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedCenter.png"] size:CGSizeMake(1.0, 18.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedRight.png"] size:CGSizeMake(3.0, 18.0)]
|
||||||
|
] isVertical:NO]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
|
{
|
||||||
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
_labelField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
|
[_labelField setAlignment:CPCenterTextAlignment];
|
||||||
|
[_labelField setFrame:CGRectMake(5.0, 0.0, CGRectGetWidth(aFrame) - 10.0, 20.0)];
|
||||||
|
[_labelField setAutoresizingMask:CPViewWidthSizable];
|
||||||
|
[_labelField setFont:[CPFont boldSystemFontOfSize:11.0]];
|
||||||
|
|
||||||
|
[self addSubview:_labelField];
|
||||||
|
|
||||||
|
[self setTabState:CPBackgroundTab];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setTabState:(CPTabState)aTabState
|
||||||
|
{
|
||||||
|
[self setBackgroundColor:aTabState == CPSelectedTab ? _CPTabLabelSelectedBackgroundColor : _CPTabLabelBackgroundColor];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
|
{
|
||||||
|
_tabViewItem = aTabViewItem;
|
||||||
|
|
||||||
|
[self update];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CPTabViewItem)tabViewItem
|
||||||
|
{
|
||||||
|
return _tabViewItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)update
|
||||||
|
{
|
||||||
|
[_labelField setStringValue:[_tabViewItem label]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+1
-16
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
|
||||||
@import "CPView.j"
|
@import <AppKit/CPView.j>
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -58,13 +58,6 @@ CPPressedTab = 2;
|
|||||||
|
|
||||||
CPView _view;
|
CPView _view;
|
||||||
CPView _auxiliaryView;
|
CPView _auxiliaryView;
|
||||||
|
|
||||||
CPTabView _tabView;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
return [self initWithIdentifier:@""];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -170,14 +163,6 @@ CPPressedTab = 2;
|
|||||||
return _tabView;
|
return _tabView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_setTabView:(CPTabView)aView
|
|
||||||
{
|
|
||||||
_tabView = aView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||||
|
|||||||
+314
-393
@@ -3,7 +3,7 @@
|
|||||||
* AppKit
|
* AppKit
|
||||||
*
|
*
|
||||||
* Created by Francisco Tolmasky.
|
* Created by Francisco Tolmasky.
|
||||||
* Copyright 2009, 280 North, Inc.
|
* Copyright 2008, 280 North, Inc.
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@@ -20,477 +20,436 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPDictionary.j>
|
@import <Foundation/Foundation.j>
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
@import <Foundation/CPIndexSet.j>
|
|
||||||
@import <Foundation/CPSortDescriptor.j>
|
|
||||||
@import <Foundation/CPString.j>
|
|
||||||
|
|
||||||
@import "CPTableHeaderView.j"
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
@ignore
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@class CPTableColumn
|
||||||
|
*/
|
||||||
CPTableColumnNoResizing = 0;
|
CPTableColumnNoResizing = 0;
|
||||||
CPTableColumnAutoresizingMask = 1 << 0;
|
/*
|
||||||
CPTableColumnUserResizingMask = 1 << 1;
|
@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
|
@implementation CPTableColumn : CPObject
|
||||||
{
|
{
|
||||||
CPTableView _tableView;
|
CPString _identifier;
|
||||||
CPView _headerView;
|
CPView _headerView;
|
||||||
CPView _dataView;
|
|
||||||
Object _dataViewData;
|
CPTableView _tableView;
|
||||||
|
|
||||||
|
float _width;
|
||||||
|
float _minWidth;
|
||||||
|
float _maxWidth;
|
||||||
|
|
||||||
|
unsigned _resizingMask;
|
||||||
|
|
||||||
float _width;
|
CPView _dataView; // default data view for this column
|
||||||
float _minWidth;
|
|
||||||
float _maxWidth;
|
|
||||||
unsigned _resizingMask;
|
|
||||||
|
|
||||||
id _identifier;
|
Object _dataViewData; // cache of data view archives (key=data view hash, value=data view archive)
|
||||||
BOOL _isEditable;
|
Object _dataViewForView; // mapping from view instances back to their data view prototype (key=view instance hash, value=data view)
|
||||||
CPSortDescriptor _sortDescriptorPrototype;
|
Object _purgableInfosForDataView; // (key=data view hash, value=)
|
||||||
BOOL _isHidden;
|
|
||||||
CPString _headerToolTip;
|
|
||||||
|
|
||||||
BOOL _disableResizingPosting @accessors(property=disableResizingPosting);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init
|
/*!
|
||||||
{
|
Initializes the table column with the specified identifier.
|
||||||
return [self initWithIdentifier:@""];
|
@param anIdentifier the identifier
|
||||||
}
|
@return the initialized table column
|
||||||
|
*/
|
||||||
- (id)initWithIdentifier:(id)anIdentifier
|
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_dataViewData = { };
|
[self _init];
|
||||||
|
|
||||||
_width = 100.0;
|
_identifier = anIdentifier;
|
||||||
_minWidth = 10.0;
|
|
||||||
_maxWidth = 1000000.0;
|
_width = 40.0;
|
||||||
_resizingMask = CPTableColumnAutoresizingMask | CPTableColumnUserResizingMask;
|
_minWidth = 8.0;
|
||||||
_disableResizingPosting = NO;
|
_maxWidth = 1000.0;
|
||||||
|
|
||||||
[self setIdentifier:anIdentifier];
|
var dataView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||||
|
[dataView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateHighlighted];
|
||||||
var header = [[_CPTableColumnHeaderView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[self setHeaderView:header];
|
[self setDataView:dataView];
|
||||||
|
|
||||||
[self setDataView:[CPTextField new]];
|
_headerView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||||
|
[_headerView setBackgroundColor:[CPColor greenColor]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
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
|
- (void)setTableView:(CPTableView)aTableView
|
||||||
{
|
{
|
||||||
_tableView = aTableView;
|
_tableView = aTableView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the column's table view.
|
||||||
|
*/
|
||||||
- (CPTableView)tableView
|
- (CPTableView)tableView
|
||||||
{
|
{
|
||||||
return _tableView;
|
return _tableView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Controlling size
|
||||||
|
/*!
|
||||||
|
Sets the column's width.
|
||||||
|
@param aWidth the new column width
|
||||||
|
*/
|
||||||
- (void)setWidth:(float)aWidth
|
- (void)setWidth:(float)aWidth
|
||||||
{
|
{
|
||||||
aWidth = +aWidth;
|
_width = 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],
|
|
||||||
dirtyTableColumnRangeIndex = tableView._dirtyTableColumnRangeIndex;
|
|
||||||
|
|
||||||
if (dirtyTableColumnRangeIndex < 0)
|
|
||||||
tableView._dirtyTableColumnRangeIndex = index;
|
|
||||||
else
|
|
||||||
tableView._dirtyTableColumnRangeIndex = MIN(index, tableView._dirtyTableColumnRangeIndex);
|
|
||||||
|
|
||||||
var rows = tableView._exposedRows,
|
|
||||||
columns = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(index, [tableView._exposedColumns lastIndex] - index + 1)];
|
|
||||||
|
|
||||||
// FIXME: Would be faster with some sort of -setNeedsDisplayInColumns: that updates a dirtyTableColumnForDisplay cache; then marked columns would relayout their data views at display time.
|
|
||||||
[tableView _layoutDataViewsInRows:rows columns:columns];
|
|
||||||
[tableView tile];
|
|
||||||
|
|
||||||
if (!_disableResizingPosting)
|
|
||||||
[self _postDidResizeNotificationWithOldWidth:oldWidth];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the column's width
|
||||||
|
*/
|
||||||
- (float)width
|
- (float)width
|
||||||
{
|
{
|
||||||
return _width;
|
return _width;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setMinWidth:(float)aMinWidth
|
/*!
|
||||||
|
Sets column's minimum width.
|
||||||
|
@param aWidth the new minimum column width
|
||||||
|
*/
|
||||||
|
- (void)setMinWidth:(float)aWidth
|
||||||
{
|
{
|
||||||
aMinWidth = +aMinWidth;
|
if (_width < (_minWidth = aWidth))
|
||||||
|
[self setWidth:_minWidth];
|
||||||
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
|
- (float)minWidth
|
||||||
{
|
{
|
||||||
return _minWidth;
|
return _minWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setMaxWidth:(float)aMaxWidth
|
/*!
|
||||||
|
Sets the column's maximum width.
|
||||||
|
@param aWidth the new maximum width
|
||||||
|
*/
|
||||||
|
- (void)setMaxWidth:(float)aWidth
|
||||||
{
|
{
|
||||||
aMaxWidth = +aMaxWidth;
|
if (_width > (_maxmimumWidth = aWidth))
|
||||||
|
[self setWidth:_maxWidth];
|
||||||
if (_maxWidth === aMaxWidth)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_maxWidth = aMaxWidth;
|
|
||||||
|
|
||||||
var width = [self width],
|
|
||||||
newWidth = MIN(width, [self maxWidth]);
|
|
||||||
|
|
||||||
if (width !== newWidth)
|
|
||||||
[self setWidth:newWidth];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)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
|
||||||
{
|
{
|
||||||
return _maxWidth;
|
_resizingMask = aMask;
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setResizingMask:(unsigned)aResizingMask
|
|
||||||
{
|
|
||||||
_resizingMask = aResizingMask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the column's resizing mask. One of:
|
||||||
|
<pre>
|
||||||
|
CPTableColumnNoResizing;
|
||||||
|
CPTableColumnAutoresizingMask;
|
||||||
|
CPTableColumnUserResizingMask;
|
||||||
|
</pre>
|
||||||
|
*/
|
||||||
- (unsigned)resizingMask
|
- (unsigned)resizingMask
|
||||||
{
|
{
|
||||||
return _resizingMask;
|
return _resizingMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Resizes the column according to the min, max and set width.
|
||||||
|
*/
|
||||||
- (void)sizeToFit
|
- (void)sizeToFit
|
||||||
{
|
{
|
||||||
var width = _CGRectGetWidth([_headerView frame]);
|
var width = CPRectGetWidth([_headerView frame]);
|
||||||
|
|
||||||
if (width < [self minWidth])
|
if (width < _minWidth)
|
||||||
[self setMinWidth:width];
|
[self setMinWidth:width];
|
||||||
else if (width > [self maxWidth])
|
else if (width > _maxWidth)
|
||||||
[self setMaxWidth:width]
|
[self setMaxWidth:width]
|
||||||
|
|
||||||
if (_width !== width)
|
if (_width != width)
|
||||||
[self setWidth:width];
|
[self setWidth:width];
|
||||||
}
|
}
|
||||||
|
|
||||||
//Setting Component Cells
|
/*!
|
||||||
- (void)setHeaderView:(CPView)aView
|
Sets whether the column in this data is editable.
|
||||||
|
@param aFlag <code>YES</code> means the column data is editable
|
||||||
|
*/
|
||||||
|
- (void)setEditable:(BOOL)aFlag
|
||||||
{
|
{
|
||||||
if (!aView)
|
_isEditable = aFlag;
|
||||||
[CPException raise:CPInvalidArgumentException reason:@"Attempt to set nil header view on " + [self description]];
|
|
||||||
|
|
||||||
_headerView = aView;
|
|
||||||
|
|
||||||
var tableHeaderView = [_tableView headerView];
|
|
||||||
|
|
||||||
[tableHeaderView setNeedsLayout];
|
|
||||||
[tableHeaderView setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)headerView
|
|
||||||
{
|
|
||||||
return _headerView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
This method set's the "prototype" view which will be used to create all table cells in this column.
|
Returns <code>YES</code> if the column data is editable.
|
||||||
|
|
||||||
It creates a snapshot of aView, using keyed archiving, which is then copied over and over for each
|
|
||||||
individual cell that is shown. As a result, changes made after calling this method won't be reflected.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
[tableColumn setDataView:someView]; // snapshot taken
|
|
||||||
[[tableColumn dataView] setSomething:x]; //won't work
|
|
||||||
|
|
||||||
This doesn't work because the snapshot is taken before the new property is applied. Instead, do:
|
|
||||||
|
|
||||||
[someView setSomething:x];
|
|
||||||
[tableColumn setDataView:someView];
|
|
||||||
*/
|
|
||||||
- (void)setDataView:(CPView)aView
|
|
||||||
{
|
|
||||||
if (_dataView)
|
|
||||||
_dataViewData[[_dataView UID]] = nil;
|
|
||||||
|
|
||||||
[aView setThemeState:CPThemeStateTableDataView];
|
|
||||||
|
|
||||||
_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;
|
|
||||||
|
|
||||||
// make sure only we have control over the size and placement
|
|
||||||
[newDataView setAutoresizingMask:CPViewNotSizable];
|
|
||||||
|
|
||||||
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
|
- (BOOL)isEditable
|
||||||
{
|
{
|
||||||
return _isEditable;
|
return _isEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Sorting
|
//Setting the column header view
|
||||||
- (void)setSortDescriptorPrototype:(CPSortDescriptor)aSortDescriptor
|
|
||||||
|
/*!
|
||||||
|
Sets the view that draws the column's header.
|
||||||
|
@param aHeaderView the view that will draws the column header
|
||||||
|
*/
|
||||||
|
|
||||||
|
- (void)setHeaderView:(CPView)aView
|
||||||
{
|
{
|
||||||
_sortDescriptorPrototype = aSortDescriptor;
|
_headerView = aView;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPSortDescriptor)sortDescriptorPrototype
|
/*!
|
||||||
|
Return the view that draws the column's header
|
||||||
|
*/
|
||||||
|
- (CPView)headerView
|
||||||
{
|
{
|
||||||
return _sortDescriptorPrototype;
|
return _headerView;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Setting Column Visibility
|
/*!
|
||||||
|
Sets the data cell that draws rows in this column.
|
||||||
- (void)setHidden:(BOOL)shouldBeHidden
|
*/
|
||||||
|
- (void)setDataCell:(CPView <CPCoding>)aView
|
||||||
{
|
{
|
||||||
shouldBeHidden = !!shouldBeHidden
|
[self setDataView:aView];
|
||||||
if (_isHidden === shouldBeHidden)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_isHidden = shouldBeHidden;
|
|
||||||
|
|
||||||
[[self headerView] setHidden:shouldBeHidden];
|
|
||||||
[[self tableView] _tableColumnVisibilityDidChange:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isHidden
|
|
||||||
{
|
|
||||||
return _isHidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Setting Tool Tips
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Sets the tooltip string that is displayed when the cursor pauses over the
|
Sets the data view that draws rows in this column.
|
||||||
header cell of the receiver.
|
|
||||||
*/
|
*/
|
||||||
- (void)setHeaderToolTip:(CPString)aToolTip
|
- (void)setDataView:(CPView <CPCoding>)aView
|
||||||
{
|
{
|
||||||
_headerToolTip = aToolTip;
|
if (_dataView)
|
||||||
|
_dataViewData[[_dataView hash]] = nil;
|
||||||
|
|
||||||
|
_dataView = aView;
|
||||||
|
_dataViewData[[aView hash]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPString)headerToolTip
|
/*!
|
||||||
|
Returns the data cell that draws rows in this column
|
||||||
|
*/
|
||||||
|
- (CPCell)dataCell
|
||||||
{
|
{
|
||||||
return _headerToolTip;
|
return _dataView;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_postDidResizeNotificationWithOldWidth:(float)oldWidth
|
/*
|
||||||
|
Returns the data view that draws rows in this column
|
||||||
|
*/
|
||||||
|
- (CPView)dataView
|
||||||
{
|
{
|
||||||
[[CPNotificationCenter defaultCenter]
|
return [self dataCell];
|
||||||
postNotificationName:CPTableViewColumnDidResizeNotification
|
|
||||||
object:[self tableView]
|
|
||||||
userInfo:[CPDictionary dictionaryWithObjects:[self, oldWidth] forKeys:[@"CPTableColumn", "CPOldWidth"]]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
/*!
|
||||||
|
By default returns the value from <code>dataCell</code>. This can
|
||||||
@implementation CPTableColumn (Bindings)
|
be overridden by a subclass to return different cells for different
|
||||||
|
rows.
|
||||||
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
|
@param aRowIndex the index of the row to obtain the cell for
|
||||||
|
*/
|
||||||
|
- (CPCell)dataCellForRow:(int)aRowIndex
|
||||||
{
|
{
|
||||||
[super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
|
return [self dataView];
|
||||||
|
|
||||||
if (![aBinding isEqual:@"someListOfExceptedBindings(notAcceptedBindings)"])
|
|
||||||
[[self tableView] _establishBindingsIfUnbound:anObject];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareDataView:(CPView)aDataView forRow:(unsigned)aRow
|
- (CPView)dataViewForRow:(int)aRowIndex
|
||||||
{
|
{
|
||||||
var bindingsDictionary = [CPKeyValueBinding allBindingsForObject:self],
|
return [self dataCellForRow:aRowIndex];
|
||||||
keys = [bindingsDictionary allKeys];
|
}
|
||||||
|
/*
|
||||||
for (var i = 0, count = [keys count]; i < count; i++)
|
- (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])
|
||||||
{
|
{
|
||||||
var bindingName = keys[i],
|
if (!isPurgable)
|
||||||
bindingPath = [aDataView _replacementKeyPathForBinding:bindingName],
|
return;
|
||||||
binding = [bindingsDictionary objectForKey:bindingName],
|
|
||||||
bindingInfo = binding._info,
|
_purgableInfosForDataView[dataViewHash] = {};
|
||||||
destination = [bindingInfo objectForKey:CPObservedObjectKey],
|
}
|
||||||
keyPath = [bindingInfo objectForKey:CPObservedKeyPathKey],
|
|
||||||
dotIndex = keyPath.lastIndexOf("."),
|
if (!isPurgable) {
|
||||||
value;
|
if (_purgableInfosForDataView[dataViewHash][viewHash])
|
||||||
|
CPLog.warn("removing unpurgable " + _purgableInfosForDataView[dataViewHash][viewHash]);
|
||||||
|
delete _purgableInfosForDataView[dataViewHash][viewHash];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_purgableInfosForDataView[dataViewHash][viewHash] = PurgableInfoMake(aView, aRow);
|
||||||
|
}
|
||||||
|
|
||||||
if (dotIndex === CPNotFound)
|
- (CPView)_newDataViewForRow:(int)aRowIndex avoidingRows:(CPRange)rows
|
||||||
value = [[destination valueForKeyPath:keyPath] objectAtIndex:aRow];
|
{
|
||||||
else
|
var view = [self dataViewForRow:aRowIndex],
|
||||||
|
viewHash = [view hash],
|
||||||
|
purgableInfos = _purgableInfosForDataView[viewHash];
|
||||||
|
|
||||||
|
if (purgableInfos)
|
||||||
|
{
|
||||||
|
for (var key in purgableInfos)
|
||||||
{
|
{
|
||||||
/*
|
var info = purgableInfos[key];
|
||||||
Optimize the prototypical use case where the key path describes a value
|
//if (!CPLocationInRange(PurgableInfoRow(info), rows))
|
||||||
in an array. Without this optimization, we call CPArray's valueForKey
|
//{
|
||||||
which generates as many values as objects in the array, of which we then
|
//CPLog.debug("yes, a purged view is usable, its called: " + PurgableInfoView(info));
|
||||||
pick one and throw away the rest.
|
delete purgableInfos[key];
|
||||||
|
return PurgableInfoView(info);
|
||||||
The optimization is to get the array and access the value directly. This
|
//}
|
||||||
turns the operation into a single access regardless of how long the model
|
//else
|
||||||
array is.
|
// CPLog.warn("avoiding");
|
||||||
*/
|
|
||||||
|
|
||||||
var firstPart = keyPath.substring(0, dotIndex),
|
|
||||||
secondPart = keyPath.substring(dotIndex + 1),
|
|
||||||
firstValue = [destination valueForKeyPath:firstPart];
|
|
||||||
|
|
||||||
if ([firstValue isKindOfClass:CPArray])
|
|
||||||
value = [[firstValue objectAtIndex:aRow] valueForKeyPath:secondPart];
|
|
||||||
else
|
|
||||||
value = [[firstValue valueForKeyPath:secondPart] objectAtIndex:aRow];
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we haven't cached an archive of the data view, do it now
|
||||||
|
if (!_dataViewData[viewHash])
|
||||||
|
_dataViewData[viewHash] = [CPKeyedArchiver archivedDataWithRootObject:view];
|
||||||
|
|
||||||
value = [binding transformValue:value withOptions:[bindingInfo objectForKey:CPOptionsKey]];
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
// console.log(bindingName+" : "+keyPath+" : "+aRow+" : "+[[destination valueForKeyPath:keyPath] objectAtIndex:aRow]);
|
- (void)_purge
|
||||||
[aDataView setValue:value forKey:bindingPath];
|
{
|
||||||
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//- (void)objectValue
|
|
||||||
//{
|
|
||||||
// return nil;
|
|
||||||
//}
|
|
||||||
|
|
||||||
- (void)setValue:(CPArray)content
|
|
||||||
{
|
|
||||||
[[self tableView] reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||||
CPTableColumnHeaderViewKey = @"CPTableColumnHeaderViewKey",
|
CPTableColumnHeaderViewKey = @"CPTableColumnHeaderViewKey",
|
||||||
CPTableColumnDataViewKey = @"CPTableColumnDataViewKey",
|
CPTableColumnDataViewKey = @"CPTableColumnDataViewKey",
|
||||||
CPTableColumnWidthKey = @"CPTableColumnWidthKey",
|
CPTableColumnWidthKey = @"CPTableColumnWidthKey",
|
||||||
CPTableColumnMinWidthKey = @"CPTableColumnMinWidthKey",
|
CPTableColumnMinWidthKey = @"CPTableColumnMinWidthKey",
|
||||||
CPTableColumnMaxWidthKey = @"CPTableColumnMaxWidthKey",
|
CPTableColumnMaxWidthKey = @"CPTableColumnMaxWidthKey",
|
||||||
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey",
|
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey";
|
||||||
CPTableColumnIsHiddenKey = @"CPTableColumnIsHiddenKey",
|
|
||||||
CPSortDescriptorPrototypeKey = @"CPSortDescriptorPrototypeKey",
|
|
||||||
CPTableColumnIsEditableKey = @"CPTableColumnIsEditableKey";
|
|
||||||
|
|
||||||
@implementation CPTableColumn (CPCoding)
|
@implementation CPTableColumn (CPCoding)
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super init];
|
[self _init];
|
||||||
|
|
||||||
|
_identifier = [aCoder decodeObjectForKey:CPTableColumnIdentifierKey];
|
||||||
|
|
||||||
if (self)
|
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||||
{
|
[self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||||
_dataViewData = { };
|
|
||||||
|
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
||||||
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
||||||
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
_maxWidth = [aCoder decodeFloatForKey:CPTableColumnMaxWidthKey];
|
||||||
_maxWidth = [aCoder decodeFloatForKey:CPTableColumnMaxWidthKey];
|
|
||||||
|
_resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
|
||||||
[self setIdentifier:[aCoder decodeObjectForKey:CPTableColumnIdentifierKey]];
|
|
||||||
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
|
||||||
[self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
|
||||||
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
|
||||||
|
|
||||||
_resizingMask = [aCoder decodeIntForKey:CPTableColumnResizingMaskKey];
|
|
||||||
_isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenKey];
|
|
||||||
_isEditable = [aCoder decodeBoolForKey:CPTableColumnIsEditableKey];
|
|
||||||
|
|
||||||
_sortDescriptorPrototype = [aCoder decodeObjectForKey:CPSortDescriptorPrototypeKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -498,53 +457,15 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
|||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
[aCoder encodeObject:_identifier forKey:CPTableColumnIdentifierKey];
|
[aCoder encodeObject:_identifier forKey:CPTableColumnIdentifierKey];
|
||||||
|
|
||||||
|
[aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||||
|
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||||
|
|
||||||
[aCoder encodeObject:_width forKey:CPTableColumnWidthKey];
|
[aCoder encodeObject:_width forKey:CPTableColumnWidthKey];
|
||||||
[aCoder encodeObject:_minWidth forKey:CPTableColumnMinWidthKey];
|
[aCoder encodeObject:_minWidth forKey:CPTableColumnMinWidthKey];
|
||||||
[aCoder encodeObject:_maxWidth forKey:CPTableColumnMaxWidthKey];
|
[aCoder encodeObject:_maxWidth forKey:CPTableColumnMaxWidthKey];
|
||||||
|
|
||||||
[aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
|
||||||
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
[aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
||||||
[aCoder encodeBool:_isHidden forKey:CPTableColumnIsHiddenKey];
|
|
||||||
[aCoder encodeBool:_isEditable forKey:CPTableColumnIsEditableKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_sortDescriptorPrototype forKey:CPSortDescriptorPrototypeKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
@end
|
||||||
|
|||||||
@@ -1,685 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 <Foundation/CPIndexSet.j>
|
|
||||||
|
|
||||||
@import "CPTableColumn.j"
|
|
||||||
@import "CPTableView.j"
|
|
||||||
@import "CPView.j"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPTableColumnHeaderView : CPView
|
|
||||||
{
|
|
||||||
_CPImageAndTextView _textField;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return @"columnHeader";
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null], CGInsetMakeZero(), [CPNull null], [CPNull null], [CPNull null], CGSizeMakeZero()]
|
|
||||||
forKeys:[@"background-color", @"text-alignment", @"text-inset", @"text-color", @"text-font", @"text-shadow-color", @"text-shadow-offset"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)initWithFrame:(CGRect)frame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:frame];
|
|
||||||
if (self)
|
|
||||||
[self _init];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
_textField = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
|
||||||
|
|
||||||
[_textField setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
|
||||||
|
|
||||||
[_textField setLineBreakMode:CPLineBreakByTruncatingTail];
|
|
||||||
[_textField setAlignment:CPLeftTextAlignment];
|
|
||||||
[_textField setVerticalAlignment:CPCenterVerticalTextAlignment];
|
|
||||||
|
|
||||||
[self addSubview:_textField];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
|
||||||
|
|
||||||
var inset = [self currentValueForThemeAttribute:@"text-inset"],
|
|
||||||
bounds = [self bounds];
|
|
||||||
|
|
||||||
[_textField setFrame:_CGRectMake(inset.right, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
|
|
||||||
[_textField setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
|
||||||
[_textField setFont:[self currentValueForThemeAttribute:@"text-font"]];
|
|
||||||
[_textField setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
|
|
||||||
[_textField setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
|
|
||||||
[_textField setAlignment:[self currentValueForThemeAttribute:@"text-alignment"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setStringValue:(CPString)string
|
|
||||||
{
|
|
||||||
[_textField setText:string];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)stringValue
|
|
||||||
{
|
|
||||||
return [_textField text];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)textField
|
|
||||||
{
|
|
||||||
return _textField;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)sizeToFit
|
|
||||||
{
|
|
||||||
[_textField sizeToFit];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
[_textField setFont:aFont];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setIndicatorImage:(CPImage)anImage
|
|
||||||
{
|
|
||||||
if (anImage)
|
|
||||||
{
|
|
||||||
[_textField setImage:anImage];
|
|
||||||
[_textField setImagePosition:CPImageRight];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[_textField setImagePosition:CPNoImage];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringValueKey",
|
|
||||||
_CPTableColumnHeaderViewFontKey = @"_CPTableColumnHeaderViewFontKey",
|
|
||||||
_CPTableColumnHeaderViewImageKey = @"_CPTableColumnHeaderViewImageKey";
|
|
||||||
|
|
||||||
@implementation _CPTableColumnHeaderView (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if (self = [super initWithCoder:aCoder])
|
|
||||||
{
|
|
||||||
[self _init];
|
|
||||||
[self _setIndicatorImage:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewImageKey]];
|
|
||||||
[self setStringValue:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewStringValueKey]];
|
|
||||||
[self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeObject:[_textField text] forKey:_CPTableColumnHeaderViewStringValueKey];
|
|
||||||
[aCoder encodeObject:[_textField image] forKey:_CPTableColumnHeaderViewImageKey];
|
|
||||||
[aCoder encodeObject:[_textField font] forKey:_CPTableColumnHeaderViewFontKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPTableHeaderView : CPView
|
|
||||||
{
|
|
||||||
CGPoint _mouseDownLocation;
|
|
||||||
CGPoint _previousTrackingLocation;
|
|
||||||
int _activeColumn;
|
|
||||||
int _pressedColumn;
|
|
||||||
|
|
||||||
BOOL _isResizing;
|
|
||||||
BOOL _isDragging;
|
|
||||||
BOOL _isTrackingColumn;
|
|
||||||
BOOL _drawsColumnLines;
|
|
||||||
|
|
||||||
float _columnOldWidth;
|
|
||||||
|
|
||||||
CPTableView _tableView @accessors(property=tableView);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return @"tableHeaderRow";
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPColor grayColor]]
|
|
||||||
forKeys:[@"background-color", @"divider-color"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
_mouseDownLocation = _CGPointMakeZero();
|
|
||||||
_previousTrackingLocation = _CGPointMakeZero();
|
|
||||||
_activeColumn = -1;
|
|
||||||
_pressedColumn = -1;
|
|
||||||
|
|
||||||
_isResizing = NO;
|
|
||||||
_isDragging = NO;
|
|
||||||
_isTrackingColumn = NO;
|
|
||||||
_drawsColumnLines = YES;
|
|
||||||
|
|
||||||
_columnOldWidth = 0.0;
|
|
||||||
|
|
||||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
[self _init];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)columnAtPoint:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
return [_tableView columnAtPoint:CGPointMake(aPoint.x, aPoint.y)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)headerRectOfColumn:(int)aColumnIndex
|
|
||||||
{
|
|
||||||
var headerRect = [self bounds],
|
|
||||||
columnRect = [_tableView rectOfColumn:aColumnIndex];
|
|
||||||
|
|
||||||
headerRect.origin.x = _CGRectGetMinX(columnRect);
|
|
||||||
headerRect.size.width = _CGRectGetWidth(columnRect);
|
|
||||||
|
|
||||||
return headerRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDrawsColumnLines:(BOOL)aFlag
|
|
||||||
{
|
|
||||||
_drawsColumnLines = aFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)drawsColumnLines
|
|
||||||
{
|
|
||||||
return _drawsColumnLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)_cursorRectForColumn:(int)column
|
|
||||||
{
|
|
||||||
if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
|
|
||||||
return _CGRectMakeZero();
|
|
||||||
|
|
||||||
var rect = [self headerRectOfColumn:column];
|
|
||||||
|
|
||||||
rect.origin.x = _CGRectGetMaxX(rect) - 5;
|
|
||||||
rect.size.width = 20;
|
|
||||||
|
|
||||||
return rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setPressedColumn:(CPInteger)column
|
|
||||||
{
|
|
||||||
if (_pressedColumn != -1)
|
|
||||||
{
|
|
||||||
var headerView = [_tableView._tableColumns[_pressedColumn] headerView];
|
|
||||||
[headerView unsetThemeState:CPThemeStateHighlighted];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (column != -1)
|
|
||||||
{
|
|
||||||
var headerView = [_tableView._tableColumns[column] headerView];
|
|
||||||
[headerView setThemeState:CPThemeStateHighlighted];
|
|
||||||
}
|
|
||||||
|
|
||||||
_pressedColumn = column;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
[self trackMouse:theEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)trackMouse:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
var type = [theEvent type],
|
|
||||||
currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil];
|
|
||||||
|
|
||||||
// Take the right columns resize tracking area into account
|
|
||||||
currentLocation.x -= 5.0;
|
|
||||||
|
|
||||||
var columnIndex = [self columnAtPoint:currentLocation],
|
|
||||||
shouldResize = [self shouldResizeTableColumn:columnIndex at:_CGPointMake(currentLocation.x + 5.0, currentLocation.y)];
|
|
||||||
|
|
||||||
if (type === CPLeftMouseUp)
|
|
||||||
{
|
|
||||||
if (shouldResize)
|
|
||||||
[self stopResizingTableColumn:_activeColumn at:currentLocation];
|
|
||||||
else if ([self _shouldStopTrackingTableColumn:columnIndex at:currentLocation])
|
|
||||||
{
|
|
||||||
[_tableView _didClickTableColumn:columnIndex modifierFlags:[theEvent modifierFlags]];
|
|
||||||
[self stopTrackingTableColumn:columnIndex at:currentLocation];
|
|
||||||
|
|
||||||
_isTrackingColumn = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self _updateResizeCursor:[CPApp currentEvent]];
|
|
||||||
|
|
||||||
_activeColumn = CPNotFound;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === CPLeftMouseDown)
|
|
||||||
{
|
|
||||||
if (columnIndex === -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_mouseDownLocation = currentLocation;
|
|
||||||
_activeColumn = columnIndex;
|
|
||||||
|
|
||||||
[_tableView _sendDelegateDidMouseDownInHeader:columnIndex];
|
|
||||||
|
|
||||||
if (shouldResize)
|
|
||||||
[self startResizingTableColumn:columnIndex at:currentLocation];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[self startTrackingTableColumn:columnIndex at:currentLocation];
|
|
||||||
_isTrackingColumn = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type === CPLeftMouseDragged)
|
|
||||||
{
|
|
||||||
if (shouldResize)
|
|
||||||
[self continueResizingTableColumn:_activeColumn at:currentLocation];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_activeColumn === columnIndex && _CGRectContainsPoint([self headerRectOfColumn:columnIndex], currentLocation))
|
|
||||||
{
|
|
||||||
if (_isTrackingColumn && _pressedColumn !== -1)
|
|
||||||
{
|
|
||||||
if (![self continueTrackingTableColumn:columnIndex at:currentLocation])
|
|
||||||
return; // Stop tracking the column, because it's being dragged
|
|
||||||
} else
|
|
||||||
[self startTrackingTableColumn:columnIndex at:currentLocation];
|
|
||||||
|
|
||||||
} else if (_isTrackingColumn && _pressedColumn !== -1)
|
|
||||||
[self stopTrackingTableColumn:_activeColumn at:currentLocation];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_previousTrackingLocation = currentLocation;
|
|
||||||
[CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)startTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
[self _setPressedColumn:aColumnIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)continueTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
if ([self _shouldDragTableColumn:aColumnIndex at:aPoint])
|
|
||||||
{
|
|
||||||
var columnRect = [self headerRectOfColumn:aColumnIndex],
|
|
||||||
offset = _CGPointMakeZero(),
|
|
||||||
view = [_tableView _dragViewForColumn:aColumnIndex event:[CPApp currentEvent] offset:offset],
|
|
||||||
viewLocation = _CGPointMakeZero();
|
|
||||||
|
|
||||||
viewLocation.x = ( _CGRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
|
|
||||||
viewLocation.y = _CGRectGetMinY(columnRect) + offset.y;
|
|
||||||
|
|
||||||
[self dragView:view at:viewLocation offset:_CGSizeMakeZero() event:[CPApp currentEvent]
|
|
||||||
pasteboard:[CPPasteboard pasteboardWithName:CPDragPboard] source:self slideBack:YES];
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_shouldStopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
return _isTrackingColumn && _activeColumn === aColumnIndex &&
|
|
||||||
_CGRectContainsPoint([self headerRectOfColumn:aColumnIndex], aPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)stopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
[self _setPressedColumn:CPNotFound];
|
|
||||||
[self _updateResizeCursor:[CPApp currentEvent]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_shouldDragTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
return [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= 10.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)_headerRectOfLastVisibleColumn
|
|
||||||
{
|
|
||||||
var tableColumns = [_tableView tableColumns],
|
|
||||||
columnIndex = [tableColumns count];
|
|
||||||
|
|
||||||
while (columnIndex--)
|
|
||||||
{
|
|
||||||
var tableColumn = [tableColumns objectAtIndex:columnIndex];
|
|
||||||
|
|
||||||
if (![tableColumn isHidden])
|
|
||||||
return [self headerRectOfColumn:columnIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_constrainDragView:(CPView)theDragView at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var tableColumns = [_tableView tableColumns],
|
|
||||||
lastColumnRect = [self _headerRectOfLastVisibleColumn],
|
|
||||||
activeColumnRect = [self headerRectOfColumn:_activeColumn],
|
|
||||||
dragWindow = [theDragView window],
|
|
||||||
frame = [dragWindow frame];
|
|
||||||
|
|
||||||
// Convert the frame origin from the global coordinate system to the windows' coordinate system
|
|
||||||
frame.origin = [[self window] convertGlobalToBase:frame.origin];
|
|
||||||
// the from the window to the view
|
|
||||||
frame.origin = [self convertPoint:frame.origin fromView:nil];
|
|
||||||
|
|
||||||
// This effectively clamps the value between the minimum and maximum
|
|
||||||
frame.origin.x = MAX(0.0, MIN(_CGRectGetMinX(frame), _CGRectGetMaxX(lastColumnRect) - _CGRectGetWidth(activeColumnRect)));
|
|
||||||
|
|
||||||
// Make sure the column cannot move vertically
|
|
||||||
frame.origin.y = _CGRectGetMinY(lastColumnRect);
|
|
||||||
|
|
||||||
// Convert the calculated origin back to the window coordinate system
|
|
||||||
frame.origin = [self convertPoint:frame.origin toView:nil];
|
|
||||||
// Then back to the global coordinate system
|
|
||||||
frame.origin = [[self window] convertBaseToGlobal:frame.origin];
|
|
||||||
|
|
||||||
[dragWindow setFrame:frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_moveColumn:(int)aFromIndex toColumn:(int)aToIndex
|
|
||||||
{
|
|
||||||
[_tableView moveColumn:aFromIndex toColumn:aToIndex];
|
|
||||||
_activeColumn = aToIndex;
|
|
||||||
_pressedColumn = _activeColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)draggedView:(CPView)aView beganAt:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
_isDragging = YES;
|
|
||||||
|
|
||||||
var column = [[_tableView tableColumns] objectAtIndex:_activeColumn];
|
|
||||||
|
|
||||||
[[column headerView] setHidden:YES];
|
|
||||||
[_tableView _setDraggedColumn:column];
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)draggedView:(CPView)aView movedTo:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
[self _constrainDragView:aView at:aPoint];
|
|
||||||
|
|
||||||
var dragWindow = [aView window],
|
|
||||||
dragWindowFrame = [dragWindow frame];
|
|
||||||
|
|
||||||
var hoverPoint = CGPointCreateCopy(aPoint);
|
|
||||||
|
|
||||||
if (aPoint.x < _previousTrackingLocation.x)
|
|
||||||
hoverPoint = _CGPointMake(_CGRectGetMinX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
|
|
||||||
else if (aPoint.x > _previousTrackingLocation.x)
|
|
||||||
hoverPoint = _CGPointMake(_CGRectGetMaxX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
|
|
||||||
|
|
||||||
// Convert the hover point from the global coordinate system to windows' coordinate system
|
|
||||||
hoverPoint = [[self window] convertGlobalToBase:hoverPoint];
|
|
||||||
// then to the view
|
|
||||||
hoverPoint = [self convertPoint:hoverPoint fromView:nil];
|
|
||||||
|
|
||||||
var hoveredColumn = [self columnAtPoint:hoverPoint];
|
|
||||||
|
|
||||||
if (hoveredColumn !== -1)
|
|
||||||
{
|
|
||||||
var columnRect = [self headerRectOfColumn:hoveredColumn],
|
|
||||||
columnCenterPoint = [self convertPoint:CGPointMake(_CGRectGetMidX(columnRect), _CGRectGetMidY(columnRect)) fromView:self];
|
|
||||||
if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
|
|
||||||
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
|
||||||
else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
|
|
||||||
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
|
||||||
}
|
|
||||||
|
|
||||||
_previousTrackingLocation = aPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)draggedView:(CPImage)aView endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
|
|
||||||
{
|
|
||||||
_isDragging = NO;
|
|
||||||
_isTrackingColumn = NO; // We need to do this explicitly because the mouse up section of trackMouse is never reached
|
|
||||||
|
|
||||||
[_tableView _setDraggedColumn:nil];
|
|
||||||
[[[[_tableView tableColumns] objectAtIndex:_activeColumn] headerView] setHidden:NO];
|
|
||||||
[self stopTrackingTableColumn:_activeColumn at:aLocation];
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)shouldResizeTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
if (_isResizing)
|
|
||||||
return YES;
|
|
||||||
|
|
||||||
if (_isTrackingColumn)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return [_tableView allowsColumnResizing] && _CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)startResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
_isResizing = YES;
|
|
||||||
|
|
||||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
|
|
||||||
|
|
||||||
[tableColumn setDisableResizingPosting:YES];
|
|
||||||
[_tableView setDisableAutomaticResizing:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)continueResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
|
|
||||||
newWidth = [tableColumn width] + aPoint.x - _previousTrackingLocation.x;
|
|
||||||
|
|
||||||
if (newWidth < [tableColumn minWidth])
|
|
||||||
[[CPCursor resizeRightCursor] set];
|
|
||||||
else if (newWidth > [tableColumn maxWidth])
|
|
||||||
[[CPCursor resizeLeftCursor] set];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_tableView._lastColumnShouldSnap = NO;
|
|
||||||
[tableColumn setWidth:newWidth];
|
|
||||||
|
|
||||||
[[CPCursor resizeLeftRightCursor] set];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)stopResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
|
|
||||||
[tableColumn _postDidResizeNotificationWithOldWidth:_columnOldWidth];
|
|
||||||
[tableColumn setDisableResizingPosting:NO];
|
|
||||||
[_tableView setDisableAutomaticResizing:NO];
|
|
||||||
|
|
||||||
_isResizing = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateResizeCursor:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
// never get stuck in resize cursor mode (FIXME take out when we turn on tracking rects)
|
|
||||||
if (![_tableView allowsColumnResizing] || ([theEvent type] === CPLeftMouseUp && ![[self window] acceptsMouseMovedEvents]))
|
|
||||||
{
|
|
||||||
[[CPCursor arrowCursor] set];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
|
|
||||||
mouseOverLocation = CGPointMake(mouseLocation.x - 5, mouseLocation.y),
|
|
||||||
overColumn = [self columnAtPoint:mouseOverLocation];
|
|
||||||
|
|
||||||
if (overColumn >= 0 && _CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
|
|
||||||
{
|
|
||||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
|
|
||||||
width = [tableColumn width];
|
|
||||||
|
|
||||||
if (width == [tableColumn minWidth])
|
|
||||||
[[CPCursor resizeRightCursor] set];
|
|
||||||
else if (width == [tableColumn maxWidth])
|
|
||||||
[[CPCursor resizeLeftCursor] set];
|
|
||||||
else
|
|
||||||
[[CPCursor resizeLeftRightCursor] set];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[[CPCursor arrowCursor] set];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseEntered:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
[self _updateResizeCursor:theEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseMoved:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
[self _updateResizeCursor:theEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseExited:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
// FIXME: we should use CPCursor push/pop (if previous currentCursor != arrow).
|
|
||||||
[[CPCursor arrowCursor] set];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
var tableColumns = [_tableView tableColumns],
|
|
||||||
count = [tableColumns count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var column = [tableColumns objectAtIndex:i],
|
|
||||||
headerView = [column headerView];
|
|
||||||
|
|
||||||
var frame = [self headerRectOfColumn:i];
|
|
||||||
frame.size.height -= 0.5;
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
frame.origin.x += 0.5;
|
|
||||||
frame.size.width -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
[headerView setFrame:frame];
|
|
||||||
|
|
||||||
if([headerView superview] != self)
|
|
||||||
[self addSubview:headerView];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
if (!_tableView || ![self drawsColumnLines])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
exposedColumnIndexes = [_tableView columnIndexesInRect:aRect],
|
|
||||||
columnsArray = [],
|
|
||||||
tableColumns = [_tableView tableColumns],
|
|
||||||
exposedTableColumns = _tableView._exposedColumns,
|
|
||||||
firstIndex = [exposedTableColumns firstIndex],
|
|
||||||
exposedRange = CPMakeRange(firstIndex, [exposedTableColumns lastIndex] - firstIndex + 1);
|
|
||||||
|
|
||||||
CGContextSetLineWidth(context, 1);
|
|
||||||
CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"divider-color"]);
|
|
||||||
|
|
||||||
[exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:exposedRange];
|
|
||||||
|
|
||||||
var columnArrayIndex = 0,
|
|
||||||
columnArrayCount = columnsArray.length,
|
|
||||||
columnMaxX;
|
|
||||||
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
for(; columnArrayIndex < columnArrayCount; columnArrayIndex++)
|
|
||||||
{
|
|
||||||
// grab each column rect and add vertical lines
|
|
||||||
var columnIndex = columnsArray[columnArrayIndex],
|
|
||||||
columnToStroke = [self headerRectOfColumn:columnIndex];
|
|
||||||
|
|
||||||
columnMaxX = _CGRectGetMaxX(columnToStroke);
|
|
||||||
|
|
||||||
CGContextMoveToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMinY(columnToStroke)));
|
|
||||||
CGContextAddLineToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMaxY(columnToStroke)));
|
|
||||||
}
|
|
||||||
|
|
||||||
CGContextClosePath(context);
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
|
|
||||||
/*if (_isDragging)
|
|
||||||
{
|
|
||||||
CGContextSetFillColor(context, [CPColor grayColor]);
|
|
||||||
CGContextFillRect(context, [self headerRectOfColumn:_activeColumn])
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey",
|
|
||||||
CPTableHeaderViewDrawsColumnLines = @"CPTableHeaderViewDrawsColumnLines";
|
|
||||||
|
|
||||||
@implementation CPTableHeaderView (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if (self = [super initWithCoder:aCoder])
|
|
||||||
{
|
|
||||||
[self _init];
|
|
||||||
_tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
|
|
||||||
_drawsColumnLines = [aCoder decodeBoolForKey:CPTableHeaderViewDrawsColumnLines];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
[aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
|
|
||||||
[aCoder encodeBool:_drawsColumnLines forKey:CPTableHeaderViewDrawsColumnLines];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+1098
-3700
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPText.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Alexander Ljungberg.
|
|
||||||
* Copyright 2010, WireLoad, LLC.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
CPEnterCharacter = "\u0003";
|
|
||||||
CPBackspaceCharacter = "\u0008";
|
|
||||||
CPTabCharacter = "\u0009";
|
|
||||||
CPNewlineCharacter = "\u000a";
|
|
||||||
CPFormFeedCharacter = "\u000c";
|
|
||||||
CPCarriageReturnCharacter = "\u000d";
|
|
||||||
CPBackTabCharacter = "\u0019";
|
|
||||||
CPDeleteCharacter = "\u007f";
|
|
||||||
+240
-552
File diff suppressed because it is too large
Load Diff
+20
-219
@@ -22,12 +22,10 @@
|
|||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
@import <Foundation/CPKeyedUnarchiver.j>
|
|
||||||
|
|
||||||
|
|
||||||
var CPThemesByName = { },
|
var CPThemesByName = { },
|
||||||
CPThemeDefaultTheme = nil,
|
CPThemeDefaultTheme = nil;
|
||||||
CPThemeDefaultHudTheme = nil;
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -50,27 +48,6 @@ var CPThemesByName = { },
|
|||||||
return CPThemeDefaultTheme;
|
return CPThemeDefaultTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the default HUD theme. If set to nil, the default described in defaultHudTheme
|
|
||||||
will be used.
|
|
||||||
*/
|
|
||||||
+ (void)setDefaultHudTheme:(CPTheme)aTheme
|
|
||||||
{
|
|
||||||
CPThemeDefaultHudTheme = aTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
The default HUD theme is (sometimes) used for windows with the CPHUDBackgroundWindowMask
|
|
||||||
style mask. The default is theme with the name of the default theme with -HUD appended
|
|
||||||
at the end.
|
|
||||||
*/
|
|
||||||
+ (CPTheme)defaultHudTheme
|
|
||||||
{
|
|
||||||
if (!CPThemeDefaultHudTheme)
|
|
||||||
CPThemeDefaultHudTheme = [CPTheme themeNamed:[[self defaultTheme] name] + "-HUD"];
|
|
||||||
return CPThemeDefaultHudTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPTheme)themeNamed:(CPString)aName
|
+ (CPTheme)themeNamed:(CPString)aName
|
||||||
{
|
{
|
||||||
return CPThemesByName[aName];
|
return CPThemesByName[aName];
|
||||||
@@ -96,110 +73,9 @@ var CPThemesByName = { },
|
|||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (_CPThemeAttribute)_attributeWithName:(CPString)aName forClass:(CPString)aClass
|
||||||
Returns an array of names of themed classes defined in this theme, as found in its
|
|
||||||
ThemeDescriptors.j file.
|
|
||||||
|
|
||||||
NOTE: The names are not class names (such as "CPButton"), but the names returned
|
|
||||||
by the class' +defaultThemeClass method. For example, the name for CPCheckBox is "check-box",
|
|
||||||
as defined in CPCheckBox::themeClass.
|
|
||||||
*/
|
|
||||||
- (CPArray)classNames
|
|
||||||
{
|
{
|
||||||
return [_attributes allKeys];
|
var attributes = [_attributes objectForKey:aClass];
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a dictionary of all theme attributes defined for the given class, as found in the
|
|
||||||
theme's ThemeDescriptors.j file. The keys of the dictionary are attribute names, and the values
|
|
||||||
are instances of _CPThemeAttribute.
|
|
||||||
|
|
||||||
For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
|
|
||||||
|
|
||||||
@param aClass The themed class whose attributes you want to retrieve
|
|
||||||
@return A dictionary of attributes or nil
|
|
||||||
*/
|
|
||||||
- (CPDictionary)attributesForClass:(id)aClass
|
|
||||||
{
|
|
||||||
if (!aClass)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
var className = nil;
|
|
||||||
|
|
||||||
if ([aClass isKindOfClass:[CPString class]])
|
|
||||||
{
|
|
||||||
// See if it is a class name
|
|
||||||
var theClass = CPClassFromString(aClass);
|
|
||||||
|
|
||||||
if (theClass)
|
|
||||||
aClass = theClass;
|
|
||||||
else
|
|
||||||
className = aClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!className)
|
|
||||||
{
|
|
||||||
if ([aClass isKindOfClass:[CPView class]])
|
|
||||||
{
|
|
||||||
if ([aClass respondsToSelector:@selector(defaultThemeClass)])
|
|
||||||
className = [aClass defaultThemeClass];
|
|
||||||
else if ([aClass respondsToSelector:@selector(themeClass)])
|
|
||||||
{
|
|
||||||
CPLog.warn(@"%@ themeClass is deprecated in favor of defaultThemeClass",CPStringFromClass([anObject class]));
|
|
||||||
className = [aClass themeClass];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[CPException raise:CPInvalidArgumentException reason:@"aClass must be a class object or a string."];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [_attributes objectForKey:className];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns an array of names of all theme attributes defined for the given class, as found in the
|
|
||||||
theme's ThemeDescriptors.j file.
|
|
||||||
|
|
||||||
The \c aClass parameter can be one of the following:
|
|
||||||
|
|
||||||
- A class instance, for example the result of [CPCheckBox class]. The class must be a subclass
|
|
||||||
of CPView.
|
|
||||||
- A class name, for example "CPCheckBox".
|
|
||||||
- A themed class name, for example "check-box".
|
|
||||||
|
|
||||||
If \c aClass does not refer to a themed class in this theme, nil is returned.
|
|
||||||
|
|
||||||
@param aClass The themed class whose attributes you want to retrieve
|
|
||||||
@return An array of attribute names or nil
|
|
||||||
*/
|
|
||||||
- (CPDictionary)attributeNamesForClass:(id)aClass
|
|
||||||
{
|
|
||||||
var attributes = [self attributesForClass:aClass];
|
|
||||||
|
|
||||||
if (attributes)
|
|
||||||
return [attributes allKeys];
|
|
||||||
else
|
|
||||||
return [CPArray array];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a theme attribute defined for the given class, as found in the
|
|
||||||
theme's ThemeDescriptors.j file.
|
|
||||||
|
|
||||||
\c aName should be the attribute name as you would pass to the method
|
|
||||||
CPView::valueForThemeAttribute:.
|
|
||||||
|
|
||||||
For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
|
|
||||||
|
|
||||||
@param aName The name of the attribute you want to retrieve
|
|
||||||
@param aClass The themed class in which to look for the attribute
|
|
||||||
@return An instance of _CPThemeAttribute or nil
|
|
||||||
*/
|
|
||||||
- (_CPThemeAttribute)attributeWithName:(CPString)aName forClass:(id)aClass
|
|
||||||
{
|
|
||||||
var attributes = [self attributesForClass:aClass];
|
|
||||||
|
|
||||||
if (!attributes)
|
if (!attributes)
|
||||||
return nil;
|
return nil;
|
||||||
@@ -207,53 +83,12 @@ var CPThemesByName = { },
|
|||||||
return [attributes objectForKey:aName];
|
return [attributes objectForKey:aName];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the value for a theme attribute in its normal state, as defined for the given class
|
|
||||||
in the theme's ThemeDescriptors.j file.
|
|
||||||
|
|
||||||
\c aName should be the attribute name as you would pass to the method
|
|
||||||
CPView::valueForThemeAttribute:.
|
|
||||||
|
|
||||||
For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
|
|
||||||
|
|
||||||
@param aName The name of the attribute whose value you want to retrieve
|
|
||||||
@param aClass The themed class in which to look for the attribute
|
|
||||||
@return A value or nil
|
|
||||||
*/
|
|
||||||
- (id)valueForAttributeWithName:(CPString)aName forClass:(id)aClass
|
|
||||||
{
|
|
||||||
return [self valueForAttributeWithName:aName inState:CPThemeStateNormal forClass:aClass];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the value for a theme attribute in a given state, as defined for the given class
|
|
||||||
in the theme's ThemeDescriptors.j file. This is the equivalent of the method
|
|
||||||
CPView::valueForThemeAttribute:inState:, but retrieves the value from the theme definition as
|
|
||||||
opposed to a single view's current theme state.
|
|
||||||
|
|
||||||
For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
|
|
||||||
|
|
||||||
@param aName The name of the attribute whose value you want to retrieve
|
|
||||||
@param aState The state qualifier for the attribute
|
|
||||||
@param aClass The themed class in which to look for the attribute
|
|
||||||
@return A value or nil
|
|
||||||
*/
|
|
||||||
- (id)valueForAttributeWithName:(CPString)aName inState:(CPThemeState)aState forClass:(id)aClass
|
|
||||||
{
|
|
||||||
var attribute = [self attributeWithName:aName forClass:aClass];
|
|
||||||
|
|
||||||
if (!attribute)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return [attribute valueForState:aState];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)takeThemeFromObject:(id)anObject
|
- (void)takeThemeFromObject:(id)anObject
|
||||||
{
|
{
|
||||||
var attributes = [anObject _themeAttributeDictionary],
|
var attributes = [anObject _themeAttributeDictionary],
|
||||||
attributeName = nil,
|
attributeName = nil,
|
||||||
attributeNames = [attributes keyEnumerator],
|
attributeNames = [attributes keyEnumerator],
|
||||||
objectThemeClass = [anObject themeClass];
|
objectThemeClass = [[anObject class] themeClass];
|
||||||
|
|
||||||
while (attributeName = [attributeNames nextObject])
|
while (attributeName = [attributeNames nextObject])
|
||||||
[self _recordAttribute:[attributes objectForKey:attributeName] forClass:objectThemeClass];
|
[self _recordAttribute:[attributes objectForKey:attributeName] forClass:objectThemeClass];
|
||||||
@@ -407,9 +242,6 @@ CPThemeStateNormal = CPThemeStates["normal"] = 0;
|
|||||||
CPThemeStateDisabled = CPThemeState("disabled");
|
CPThemeStateDisabled = CPThemeState("disabled");
|
||||||
CPThemeStateHighlighted = CPThemeState("highlighted");
|
CPThemeStateHighlighted = CPThemeState("highlighted");
|
||||||
CPThemeStateSelected = CPThemeState("selected");
|
CPThemeStateSelected = CPThemeState("selected");
|
||||||
CPThemeStateTableDataView = CPThemeState("tableDataView");
|
|
||||||
CPThemeStateSelectedDataView = CPThemeStateSelectedTableDataView = CPThemeState("selectedTableDataView");
|
|
||||||
CPThemeStateGroupRow = CPThemeState("CPThemeStateGroupRow");
|
|
||||||
CPThemeStateBezeled = CPThemeState("bezeled");
|
CPThemeStateBezeled = CPThemeState("bezeled");
|
||||||
CPThemeStateBordered = CPThemeState("bordered");
|
CPThemeStateBordered = CPThemeState("bordered");
|
||||||
CPThemeStateEditable = CPThemeState("editable");
|
CPThemeStateEditable = CPThemeState("editable");
|
||||||
@@ -422,10 +254,10 @@ CPThemeStateCircular = CPThemeState("circular");
|
|||||||
{
|
{
|
||||||
CPString _name;
|
CPString _name;
|
||||||
id _defaultValue;
|
id _defaultValue;
|
||||||
CPDictionary _values @accessors(readonly, getter=values);
|
CPDictionary _values;
|
||||||
|
|
||||||
JSObject _cache;
|
JSObject _cache;
|
||||||
_CPThemeAttribute _parentAttribute;
|
CPThemeAttribute _parentAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue
|
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue
|
||||||
@@ -546,7 +378,7 @@ CPThemeStateCircular = CPThemeState("circular");
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setParentAttribute:(_CPThemeAttribute)anAttribute
|
- (void)setParentAttribute:(CPThemeAttribute)anAttribute
|
||||||
{
|
{
|
||||||
if (_parentAttribute === anAttribute)
|
if (_parentAttribute === anAttribute)
|
||||||
return;
|
return;
|
||||||
@@ -555,7 +387,7 @@ CPThemeStateCircular = CPThemeState("circular");
|
|||||||
_parentAttribute = anAttribute;
|
_parentAttribute = anAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
|
- (CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
|
||||||
{
|
{
|
||||||
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue];
|
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue];
|
||||||
|
|
||||||
@@ -640,15 +472,15 @@ CPThemeStateCircular = CPThemeState("circular");
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var cachedNumberOfOnes = [ 0 /*000000*/, 1 /*000001*/, 1 /*000010*/, 2 /*000011*/, 1 /*000100*/, 2 /*000101*/, 2 /*000110*/,
|
var cachedNumberOfOnes = [ 0 /*000000*/, 1 /*000001*/, 1 /*000010*/, 2 /*000011*/, 1 /*000100*/, 2 /*000101*/, 2 /*000110*/,
|
||||||
3 /*000111*/, 1 /*001000*/, 2 /*001001*/, 2 /*001010*/, 3 /*001011*/, 2 /*001100*/, 3 /*001101*/,
|
3 /*000111*/, 1 /*001000*/, 2 /*001001*/, 2 /*001010*/, 3 /*001011*/, 2 /*001100*/, 3 /*001101*/,
|
||||||
3 /*001110*/, 4 /*001111*/, 1 /*010000*/, 2 /*010001*/, 2 /*010010*/, 3 /*010011*/, 2 /*010100*/,
|
3 /*001110*/, 4 /*001111*/, 1 /*010000*/, 2 /*010001*/, 2 /*010010*/, 3 /*010011*/, 2 /*010100*/,
|
||||||
3 /*010101*/, 3 /*010110*/, 4 /*010111*/, 2 /*011000*/, 3 /*011001*/, 3 /*011010*/, 4 /*011011*/,
|
3 /*010101*/, 3 /*010110*/, 4 /*010111*/, 2 /*011000*/, 3 /*011001*/, 3 /*011010*/, 4 /*011011*/,
|
||||||
3 /*011100*/, 4 /*011101*/, 4 /*011110*/, 5 /*011111*/, 1 /*100000*/, 2 /*100001*/, 2 /*100010*/,
|
3 /*011100*/, 4 /*011101*/, 4 /*011110*/, 5 /*011111*/, 1 /*100000*/, 2 /*100001*/, 2 /*100010*/,
|
||||||
3 /*100011*/, 2 /*100100*/, 3 /*100101*/, 3 /*100110*/, 4 /*100111*/, 2 /*101000*/, 3 /*101001*/,
|
3 /*100011*/, 2 /*100100*/, 3 /*100101*/, 3 /*100110*/, 4 /*100111*/, 2 /*101000*/, 3 /*101001*/,
|
||||||
3 /*101010*/, 4 /*101011*/, 3 /*101100*/, 4 /*101101*/, 4 /*101110*/, 5 /*101111*/, 2 /*110000*/,
|
3 /*101010*/, 4 /*101011*/, 3 /*101100*/, 4 /*101101*/, 4 /*101110*/, 5 /*101111*/, 2 /*110000*/,
|
||||||
3 /*110001*/, 3 /*110010*/, 4 /*110011*/, 3 /*110100*/, 4 /*110101*/, 4 /*110110*/, 5 /*110111*/,
|
3 /*110001*/, 3 /*110010*/, 4 /*110011*/, 3 /*110100*/, 4 /*110101*/, 4 /*110110*/, 5 /*110111*/,
|
||||||
3 /*111000*/, 4 /*111001*/, 4 /*111010*/, 5 /*111011*/, 4 /*111100*/, 5 /*111101*/, 5 /*111110*/,
|
3 /*111000*/, 4 /*111001*/, 4 /*111010*/, 5 /*111011*/, 4 /*111100*/, 5 /*111101*/, 5 /*111110*/,
|
||||||
6 /*111111*/ ];
|
6 /*111111*/ ];
|
||||||
|
|
||||||
var numberOfOnes = function(aNumber)
|
var numberOfOnes = function(aNumber)
|
||||||
@@ -699,7 +531,7 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute)
|
|||||||
|
|
||||||
function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass)
|
function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass)
|
||||||
{
|
{
|
||||||
var key = "$a" + anAttributeName;
|
var key = "$a" + anAttributeName;
|
||||||
|
|
||||||
if (![aCoder containsValueForKey:key])
|
if (![aCoder containsValueForKey:key])
|
||||||
var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
|
var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
|
||||||
@@ -719,38 +551,7 @@ function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (aTheme && aClass)
|
if (aTheme && aClass)
|
||||||
[attribute setParentAttribute:[aTheme attributeWithName:anAttributeName forClass:aClass]];
|
[attribute setParentAttribute:[aTheme _attributeWithName:anAttributeName forClass:aClass]];
|
||||||
|
|
||||||
return attribute;
|
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+']');
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|||||||
+24
-42
@@ -22,10 +22,9 @@
|
|||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
|
||||||
@import "CPTheme.j"
|
@import <AppKit/CPTheme.j>
|
||||||
@import "_CPCibCustomResource.j"
|
@import <AppKit/_CPCibCustomResource.j>
|
||||||
@import "_CPCibKeyedUnarchiver.j"
|
@import <AppKit/_CPCibKeyedUnarchiver.j>
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -34,69 +33,52 @@
|
|||||||
@implementation CPThemeBlend : CPObject
|
@implementation CPThemeBlend : CPObject
|
||||||
{
|
{
|
||||||
CPBundle _bundle;
|
CPBundle _bundle;
|
||||||
CPArray _themes;
|
CPArray _themes @accessors(readonly, getter=themes);
|
||||||
id _loadDelegate;
|
id _loadDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithContentsOfURL:(CPURL)aURL
|
- (id)initWithContentsOfURL:(CPURL)aURL
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_bundle = [[CPBundle alloc] initWithPath:aURL];
|
_bundle = [[CPBundle alloc] initWithPath:aURL + "/Info.plist"];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns an array of names of the keyed theme archives that make up this blend.
|
|
||||||
Each item in the array will have the extension ".keyedtheme".
|
|
||||||
*/
|
|
||||||
- (CPArray)themes
|
|
||||||
{
|
|
||||||
return _themes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns an array of names of the themes that make up this blend.
|
|
||||||
*/
|
|
||||||
- (CPArray)themeNames
|
|
||||||
{
|
|
||||||
var names = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < _themes.length; ++i)
|
|
||||||
names.push(_themes[i].substring(0, _themes[i].indexOf(".keyedtheme")));
|
|
||||||
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)loadWithDelegate:(id)aDelegate
|
- (void)loadWithDelegate:(id)aDelegate
|
||||||
{
|
{
|
||||||
_loadDelegate = aDelegate;
|
_loadDelegate = aDelegate;
|
||||||
|
|
||||||
[_bundle loadWithDelegate:self];
|
[_bundle loadWithDelegate:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)bundleDidFinishLoading:(CPBundle)aBundle
|
- (void)bundleDidFinishLoading:(CPBundle)aBundle
|
||||||
{
|
{
|
||||||
_themes = [_bundle objectForInfoDictionaryKey:@"CPKeyedThemes"];
|
var paths = [_bundle objectForInfoDictionaryKey:@"CPBundleReplacedFiles"],
|
||||||
|
index = 0,
|
||||||
var count = _themes.length;
|
count = paths.length,
|
||||||
|
bundlePath = [_bundle bundlePath];
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
{
|
{
|
||||||
var path = [aBundle pathForResource:_themes[count]],
|
var path = paths[count];
|
||||||
unarchiver = [[_CPThemeKeyedUnarchiver alloc]
|
|
||||||
initForReadingWithData:[[CPURL URLWithString:path] staticResourceData]
|
if ([path pathExtension] === "keyedtheme")
|
||||||
bundle:_bundle];
|
{
|
||||||
|
var unarchiver = [[_CPThemeKeyedUnarchiver alloc]
|
||||||
|
initForReadingWithData:[CPData dataWithString:objj_files[bundlePath + '/' + path].contents]
|
||||||
|
bundle:_bundle];
|
||||||
|
|
||||||
[unarchiver decodeObjectForKey:@"root"];
|
[unarchiver decodeObjectForKey:@"root"];
|
||||||
|
|
||||||
[unarchiver finishDecoding];
|
[unarchiver finishDecoding];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[_loadDelegate blendDidFinishLoading:self];
|
[_loadDelegate blendDidFinishLoading:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+323
-611
File diff suppressed because it is too large
Load Diff
+86
-175
@@ -23,22 +23,38 @@
|
|||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
|
|
||||||
@import "CPImage.j"
|
@import <AppKit/CPImage.j>
|
||||||
@import "CPView.j"
|
@import <AppKit/CPView.j>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@class CPToolbarItem
|
||||||
|
*/
|
||||||
CPToolbarItemVisibilityPriorityStandard = 0;
|
CPToolbarItemVisibilityPriorityStandard = 0;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@class CPToolbarItem
|
||||||
|
*/
|
||||||
CPToolbarItemVisibilityPriorityLow = -1000;
|
CPToolbarItemVisibilityPriorityLow = -1000;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@class CPToolbarItem
|
||||||
|
*/
|
||||||
CPToolbarItemVisibilityPriorityHigh = 1000;
|
CPToolbarItemVisibilityPriorityHigh = 1000;
|
||||||
|
/*
|
||||||
|
@global
|
||||||
|
@class CPToolbarItem
|
||||||
|
*/
|
||||||
CPToolbarItemVisibilityPriorityUser = 2000;
|
CPToolbarItemVisibilityPriorityUser = 2000;
|
||||||
|
|
||||||
CPToolbarSeparatorItemIdentifier = @"CPToolbarSeparatorItem";
|
CPToolbarSeparatorItemIdentifier = @"CPToolbarSeparatorItemIdentifier";
|
||||||
CPToolbarSpaceItemIdentifier = @"CPToolbarSpaceItem";
|
CPToolbarSpaceItemIdentifier = @"CPToolbarSpaceItemIdentifier";
|
||||||
CPToolbarFlexibleSpaceItemIdentifier = @"CPToolbarFlexibleSpaceItem";
|
CPToolbarFlexibleSpaceItemIdentifier = @"CPToolbarFlexibleSpaceItemIdentifier";
|
||||||
CPToolbarShowColorsItemIdentifier = @"CPToolbarShowColorsItem";
|
CPToolbarShowColorsItemIdentifier = @"CPToolbarShowColorsItemIdentifier";
|
||||||
CPToolbarShowFontsItemIdentifier = @"CPToolbarShowFontsItem";
|
CPToolbarShowFontsItemIdentifier = @"CPToolbarShowFontsItemIdentifier";
|
||||||
CPToolbarCustomizeToolbarItemIdentifier = @"CPToolbarCustomizeToolbarItem";
|
CPToolbarCustomizeToolbarItemIdentifier = @"CPToolbarCustomizeToolbarItemIdentifier";
|
||||||
CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -68,13 +84,6 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
CGSize _maxSize;
|
CGSize _maxSize;
|
||||||
|
|
||||||
int _visibilityPriority;
|
int _visibilityPriority;
|
||||||
|
|
||||||
BOOL _autovalidates;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
return [self initWithItemIdentifier:@""];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creating a Toolbar Item
|
// Creating a Toolbar Item
|
||||||
@@ -98,7 +107,6 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
_maxSize = CGSizeMakeZero();
|
_maxSize = CGSizeMakeZero();
|
||||||
|
|
||||||
_visibilityPriority = CPToolbarItemVisibilityPriorityStandard;
|
_visibilityPriority = CPToolbarItemVisibilityPriorityStandard;
|
||||||
_autovalidates = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -121,12 +129,6 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
return _toolbar;
|
return _toolbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @ignore */
|
|
||||||
- (void)_setToolbar:(CPToolbar)aToolbar
|
|
||||||
{
|
|
||||||
_toolbar = aToolbar;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the item's label
|
Returns the item's label
|
||||||
*/
|
*/
|
||||||
@@ -181,7 +183,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
- (void)setToolTip:(CPString)aToolTip
|
- (void)setToolTip:(CPString)aToolTip
|
||||||
{
|
{
|
||||||
if ([_view respondsToSelector:@selector(setToolTip:)])
|
if ([_view respondsToSelector:@selector(setToolTip:)])
|
||||||
[_view setToolTip:aToolTip];
|
[view setToolTip:aToolTip];
|
||||||
|
|
||||||
_toolTip = aToolTip;
|
_toolTip = aToolTip;
|
||||||
}
|
}
|
||||||
@@ -221,7 +223,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the target of the action that is triggered when the user clicks this item. \c nil will cause
|
Sets the target of the action that is triggered when the user clicks this item. <code>nil</code> will cause
|
||||||
the action to be passed on to the first responder.
|
the action to be passed on to the first responder.
|
||||||
@param aTarget the new target
|
@param aTarget the new target
|
||||||
*/
|
*/
|
||||||
@@ -259,7 +261,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the item is enabled.
|
Returns <code>YES</code> if the item is enabled.
|
||||||
*/
|
*/
|
||||||
- (BOOL)isEnabled
|
- (BOOL)isEnabled
|
||||||
{
|
{
|
||||||
@@ -271,7 +273,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets whether the item is enabled.
|
Sets whether the item is enabled.
|
||||||
@param aFlag \c YES enables the item
|
@param aFlag <code>YES</code> enables the item
|
||||||
*/
|
*/
|
||||||
- (void)setEnabled:(BOOL)shouldBeEnabled
|
- (void)setEnabled:(BOOL)shouldBeEnabled
|
||||||
{
|
{
|
||||||
@@ -306,8 +308,8 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
if (!_image)
|
if (!_image)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_minSize.width === 0 && _minSize.height === 0 &&
|
if (_minSize.width == 0 && _minSize.height == 0 &&
|
||||||
_maxSize.width === 0 && _maxSize.height === 0)
|
_maxSize.width == 0 && _maxSize.height == 0)
|
||||||
{
|
{
|
||||||
var imageSize = [_image size];
|
var imageSize = [_image size];
|
||||||
|
|
||||||
@@ -386,9 +388,6 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
*/
|
*/
|
||||||
- (void)setMinSize:(CGSize)aMinSize
|
- (void)setMinSize:(CGSize)aMinSize
|
||||||
{
|
{
|
||||||
if(!aMinSize.height || !aMinSize.width)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_minSize = CGSizeMakeCopy(aMinSize);
|
_minSize = CGSizeMakeCopy(aMinSize);
|
||||||
|
|
||||||
// Try to provide some sanity: Make maxSize >= minSize
|
// Try to provide some sanity: Make maxSize >= minSize
|
||||||
@@ -409,9 +408,6 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
*/
|
*/
|
||||||
- (void)setMaxSize:(CGSize)aMaxSize
|
- (void)setMaxSize:(CGSize)aMaxSize
|
||||||
{
|
{
|
||||||
if(!aMaxSize.height || !aMaxSize.width)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_maxSize = CGSizeMakeCopy(aMaxSize);
|
_maxSize = CGSizeMakeCopy(aMaxSize);
|
||||||
|
|
||||||
// Try to provide some sanity: Make minSize <= maxSize
|
// Try to provide some sanity: Make minSize <= maxSize
|
||||||
@@ -448,127 +444,6 @@ CPToolbarItemVisibilityPriorityUser
|
|||||||
_visibilityPriority = aVisibilityPriority;
|
_visibilityPriority = aVisibilityPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)validate
|
|
||||||
{
|
|
||||||
var action = [self action],
|
|
||||||
target = [self target];
|
|
||||||
|
|
||||||
// View items do not do any target-action analysis.
|
|
||||||
if (_view)
|
|
||||||
{
|
|
||||||
if ([target respondsToSelector:@selector(validateToolbarItem:)])
|
|
||||||
[self setEnabled:[target validateToolbarItem:self]];
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!action)
|
|
||||||
return [self setEnabled:NO];
|
|
||||||
|
|
||||||
if (target && ![target respondsToSelector:action])
|
|
||||||
return [self setEnabled:NO];
|
|
||||||
|
|
||||||
target = [CPApp targetForAction:action to:target from:self];
|
|
||||||
|
|
||||||
if (!target)
|
|
||||||
return [self setEnabled:NO];
|
|
||||||
|
|
||||||
if ([target respondsToSelector:@selector(validateToolbarItem:)])
|
|
||||||
[self setEnabled:[target validateToolbarItem:self]];
|
|
||||||
else
|
|
||||||
[self setEnabled:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)autovalidates
|
|
||||||
{
|
|
||||||
return _autovalidates;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAutovalidates:(BOOL)shouldAutovalidate
|
|
||||||
{
|
|
||||||
_autovalidates = !!shouldAutovalidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPToolbarItemItemIdentifierKey = @"CPToolbarItemItemIdentifierKey",
|
|
||||||
CPToolbarItemLabelKey = @"CPToolbarItemLabelKey",
|
|
||||||
CPToolbarItemPaletteLabelKey = @"CPToolbarItemPaletteLabelKey",
|
|
||||||
CPToolbarItemToolTipKey = @"CPToolbarItemToolTipKey",
|
|
||||||
CPToolbarItemTagKey = @"CPToolbarItemTagKey",
|
|
||||||
CPToolbarItemTargetKey = @"CPToolbarItemTargetKey",
|
|
||||||
CPToolbarItemActionKey = @"CPToolbarItemActionKey",
|
|
||||||
CPToolbarItemEnabledKey = @"CPToolbarItemEnabledKey",
|
|
||||||
CPToolbarItemImageKey = @"CPToolbarItemImageKey",
|
|
||||||
CPToolbarItemAlternateImageKey = @"CPToolbarItemAlternateImageKey",
|
|
||||||
CPToolbarItemViewKey = @"CPToolbarItemViewKey",
|
|
||||||
CPToolbarItemMinSizeKey = @"CPToolbarItemMinSizeKey",
|
|
||||||
CPToolbarItemMaxSizeKey = @"CPToolbarItemMaxSizeKey",
|
|
||||||
CPToolbarItemVisibilityPriorityKey = @"CPToolbarItemVisibilityPriorityKey",
|
|
||||||
CPToolbarItemAutovalidatesKey = @"CPToolbarItemAutovalidatesKey";
|
|
||||||
|
|
||||||
@implementation CPToolbarItem (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_itemIdentifier = [aCoder decodeObjectForKey:CPToolbarItemItemIdentifierKey];
|
|
||||||
|
|
||||||
_minSize = [aCoder decodeSizeForKey:CPToolbarItemMinSizeKey];
|
|
||||||
_maxSize = [aCoder decodeSizeForKey:CPToolbarItemMaxSizeKey];
|
|
||||||
|
|
||||||
[self setLabel:[aCoder decodeObjectForKey:CPToolbarItemLabelKey]];
|
|
||||||
[self setPaletteLabel:[aCoder decodeObjectForKey:CPToolbarItemPaletteLabelKey]];
|
|
||||||
[self setToolTip:[aCoder decodeObjectForKey:CPToolbarItemToolTipKey]];
|
|
||||||
|
|
||||||
[self setTag:[aCoder decodeObjectForKey:CPToolbarItemTagKey]];
|
|
||||||
[self setTarget:[aCoder decodeObjectForKey:CPToolbarItemTargetKey]];
|
|
||||||
[self setAction:CPSelectorFromString([aCoder decodeObjectForKey:CPToolbarItemActionKey])];
|
|
||||||
|
|
||||||
[self setEnabled:[aCoder decodeBoolForKey:CPToolbarItemEnabledKey]];
|
|
||||||
|
|
||||||
[self setImage:[aCoder decodeObjectForKey:CPToolbarItemImageKey]];
|
|
||||||
[self setAlternateImage:[aCoder decodeObjectForKey:CPToolbarItemAlternateImageKey]];
|
|
||||||
|
|
||||||
[self setView:[aCoder decodeObjectForKey:CPToolbarItemViewKey]];
|
|
||||||
|
|
||||||
[self setVisibilityPriority:[aCoder decodeIntForKey:CPToolbarItemVisibilityPriorityKey]];
|
|
||||||
[self setAutovalidates:[aCoder decodeBoolForKey:CPToolbarItemAutovalidatesKey]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[aCoder encodeObject:_itemIdentifier forKey:CPToolbarItemItemIdentifierKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:[self label] forKey:CPToolbarItemLabelKey];
|
|
||||||
[aCoder encodeObject:[self paletteLabel] forKey:CPToolbarItemPaletteLabelKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:[self toolTip] forKey:CPToolbarItemToolTipKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:[self tag] forKey:CPToolbarItemTagKey];
|
|
||||||
[aCoder encodeObject:[self target] forKey:CPToolbarItemTargetKey];
|
|
||||||
[aCoder encodeObject:[self action] forKey:CPToolbarItemActionKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:[self isEnabled] forKey:CPToolbarItemEnabledKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:[self image] forKey:CPToolbarItemImageKey];
|
|
||||||
[aCoder encodeObject:[self alternateImage] forKey:CPToolbarItemAlternateImageKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:[self view] forKey:CPToolbarItemViewKey];
|
|
||||||
|
|
||||||
[aCoder encodeSize:[self minSize] forKey:CPToolbarItemMinSizeKey];
|
|
||||||
[aCoder encodeSize:[self maxSize] forKey:CPToolbarItemMaxSizeKey];
|
|
||||||
|
|
||||||
[aCoder encodeObject:[self visibilityPriority] forKey:CPToolbarItemVisibilityPriorityKey];
|
|
||||||
[aCoder encodeBool:[self autovalidates] forKey:CPToolbarItemAutovalidatesKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPToolbarItem (CPCopying)
|
@implementation CPToolbarItem (CPCopying)
|
||||||
@@ -576,12 +451,10 @@ var CPToolbarItemItemIdentifierKey = @"CPToolbarItemItemIdentifierKey",
|
|||||||
- (id)copy
|
- (id)copy
|
||||||
{
|
{
|
||||||
var copy = [[[self class] alloc] initWithItemIdentifier:_itemIdentifier];
|
var copy = [[[self class] alloc] initWithItemIdentifier:_itemIdentifier];
|
||||||
|
|
||||||
if (_view)
|
if (_view)
|
||||||
[copy setView:[CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:_view]]];
|
[copy setView:[CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:_view]]];
|
||||||
|
|
||||||
[copy _setToolbar:_toolbar];
|
|
||||||
|
|
||||||
[copy setLabel:_label];
|
[copy setLabel:_label];
|
||||||
[copy setPaletteLabel:_paletteLabel];
|
[copy setPaletteLabel:_paletteLabel];
|
||||||
[copy setToolTip:[self toolTip]];
|
[copy setToolTip:[self toolTip]];
|
||||||
@@ -589,7 +462,7 @@ var CPToolbarItemItemIdentifierKey = @"CPToolbarItemItemIdentifierKey",
|
|||||||
[copy setTag:[self tag]];
|
[copy setTag:[self tag]];
|
||||||
[copy setTarget:[self target]];
|
[copy setTarget:[self target]];
|
||||||
[copy setAction:[self action]];
|
[copy setAction:[self action]];
|
||||||
|
|
||||||
[copy setEnabled:[self isEnabled]];
|
[copy setEnabled:[self isEnabled]];
|
||||||
|
|
||||||
[copy setImage:[self image]];
|
[copy setImage:[self image]];
|
||||||
@@ -597,10 +470,9 @@ var CPToolbarItemItemIdentifierKey = @"CPToolbarItemItemIdentifierKey",
|
|||||||
|
|
||||||
[copy setMinSize:_minSize];
|
[copy setMinSize:_minSize];
|
||||||
[copy setMaxSize:_maxSize];
|
[copy setMaxSize:_maxSize];
|
||||||
|
|
||||||
[copy setVisibilityPriority:[self visibilityPriority]];
|
[copy setVisibilityPriority:_visibilityPriority];
|
||||||
[copy setAutovalidates:[self autovalidates]];
|
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,17 +480,62 @@ var CPToolbarItemItemIdentifierKey = @"CPToolbarItemItemIdentifierKey",
|
|||||||
|
|
||||||
// Standard toolbar identifiers
|
// Standard toolbar identifiers
|
||||||
|
|
||||||
|
var _CPToolbarSeparatorItemView = nil,
|
||||||
|
_CPToolbarSpaceItemView = nil;
|
||||||
|
|
||||||
@implementation CPToolbarItem (Standard)
|
@implementation CPToolbarItem (Standard)
|
||||||
|
|
||||||
|
+ (CPView)_separatorItemView
|
||||||
|
{
|
||||||
|
if (!_CPToolbarSeparatorItemView)
|
||||||
|
{
|
||||||
|
_CPToolbarSeparatorItemView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 2.0, 32.0)];
|
||||||
|
|
||||||
|
sizes = {};
|
||||||
|
sizes[@"CPToolbarItemSeparator"] = [CGSizeMake(2.0, 26.0), CGSizeMake(2.0, 1.0), CGSizeMake(2.0, 26.0)];
|
||||||
|
[_CPToolbarSeparatorItemView setBackgroundColor:_CPControlThreePartImagePattern(YES, sizes, @"CPToolbarItem", @"Separator")];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _CPToolbarSeparatorItemView;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (CPView)_spaceItemView
|
||||||
|
{
|
||||||
|
if (!_CPToolbarSpaceItemView)
|
||||||
|
_CPToolbarSpaceItemView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
|
return _CPToolbarSpaceItemView;
|
||||||
|
}
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
+ (CPToolbarItem)_standardItemWithItemIdentifier:(CPString)anItemIdentifier
|
+ (CPToolbarItem)_standardItemWithItemIdentifier:(CPString)anItemIdentifier
|
||||||
{
|
{
|
||||||
|
var item = [[CPToolbarItem alloc] initWithItemIdentifier:anItemIdentifier];
|
||||||
|
|
||||||
switch (anItemIdentifier)
|
switch (anItemIdentifier)
|
||||||
{
|
{
|
||||||
case CPToolbarSeparatorItemIdentifier: return [_CPToolbarSeparatorItem new];
|
case CPToolbarSeparatorItemIdentifier: [item setView:[self _separatorItemView]];
|
||||||
case CPToolbarSpaceItemIdentifier: return [_CPToolbarSpaceItem new];
|
|
||||||
case CPToolbarFlexibleSpaceItemIdentifier: return [_CPToolbarFlexibleSpaceItem new];
|
[item setMinSize:CGSizeMake(2.0, 0.0)];
|
||||||
case CPToolbarShowColorsItemIdentifier: return [_CPToolbarShowColorsItem new];
|
[item setMaxSize:CGSizeMake(2.0, 100000.0)];
|
||||||
|
|
||||||
|
return item;
|
||||||
|
|
||||||
|
case CPToolbarSpaceItemIdentifier: [item setView:[self _spaceItemView]];
|
||||||
|
|
||||||
|
[item setMinSize:CGSizeMake(32.0, 32.0)];
|
||||||
|
[item setMaxSize:CGSizeMake(32.0, 32.0)];
|
||||||
|
|
||||||
|
return item;
|
||||||
|
|
||||||
|
case CPToolbarFlexibleSpaceItemIdentifier: [item setView:[self _spaceItemView]];
|
||||||
|
|
||||||
|
[item setMinSize:CGSizeMake(32.0, 32.0)];
|
||||||
|
[item setMaxSize:CGSizeMake(10000.0, 32.0)];
|
||||||
|
|
||||||
|
return item;
|
||||||
|
|
||||||
|
case CPToolbarShowColorsItemIdentifier: return nil;
|
||||||
case CPToolbarShowFontsItemIdentifier: return nil;
|
case CPToolbarShowFontsItemIdentifier: return nil;
|
||||||
case CPToolbarCustomizeToolbarItemIdentifier: return nil;
|
case CPToolbarCustomizeToolbarItemIdentifier: return nil;
|
||||||
case CPToolbarPrintItemIdentifier: return nil;
|
case CPToolbarPrintItemIdentifier: return nil;
|
||||||
@@ -628,9 +545,3 @@ var CPToolbarItemItemIdentifierKey = @"CPToolbarItemItemIdentifierKey",
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@import "_CPToolbarFlexibleSpaceItem.j"
|
|
||||||
@import "_CPToolbarShowColorsItem.j"
|
|
||||||
@import "_CPToolbarSeparatorItem.j"
|
|
||||||
@import "_CPToolbarSpaceItem.j"
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,316 +0,0 @@
|
|||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPTreeNode : CPObject
|
|
||||||
{
|
|
||||||
id _representedObject @accessors(readonly, property=representedObject);
|
|
||||||
|
|
||||||
CPTreeNode _parentNode @accessors(readonly, property=parentNode);
|
|
||||||
CPMutableArray _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;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)childNodes
|
|
||||||
{
|
|
||||||
return [_childNodes copy];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPMutableArray)mutableChildNodes
|
|
||||||
{
|
|
||||||
return [self mutableArrayValueForKey:@"childNodes"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertObject:(id)aTreeNode inChildNodesAtIndex:(CPInteger)anIndex
|
|
||||||
{
|
|
||||||
[[aTreeNode._parentNode mutableChildNodes] removeObjectIdenticalTo:aTreeNode];
|
|
||||||
|
|
||||||
aTreeNode._parentNode = self;
|
|
||||||
|
|
||||||
[_childNodes insertObject:aTreeNode atIndex:anIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObjectFromChildNodesAtIndex:(CPInteger)anIndex
|
|
||||||
{
|
|
||||||
[_childNodes objectAtIndex:anIndex]._parentNode = nil;
|
|
||||||
|
|
||||||
[_childNodes removeObjectAtIndex:anIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)replaceObjectFromChildNodesAtIndex:(CPInteger)anIndex withObject:(id)aTreeNode
|
|
||||||
{
|
|
||||||
var oldTreeNode = [_childNodes objectAtIndex:anIndex];
|
|
||||||
|
|
||||||
oldTreeNode._parentNode = nil;
|
|
||||||
aTreeNode._parentNode = self;
|
|
||||||
|
|
||||||
[_childNodes replaceObjectAtIndex:anIndex withObject:aTreeNode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPEnumerator)preOrder
|
|
||||||
{
|
|
||||||
return [_CPTreeNodeEnumerator enumeratorWithTreeNode:self order:_CPTreeNodeEnumeratorPreOrder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPEnumerator)inOrder
|
|
||||||
{
|
|
||||||
return [_CPTreeNodeEnumerator enumeratorWithTreeNode:self order:_CPTreeNodeEnumeratorInOrder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPEnumerator)postOrder
|
|
||||||
{
|
|
||||||
return [_CPTreeNodeEnumerator enumeratorWithTreeNode:self order:_CPTreeNodeEnumeratorPostOrder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPEnumerator)levelOrder
|
|
||||||
{
|
|
||||||
return [_CPTreeNodeEnumerator enumeratorWithTreeNode:self order:_CPTreeNodeEnumeratorLevelOrder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)countByEnumeratingWithState:(id)aState objects:(id)objects count:(id)aCount
|
|
||||||
{
|
|
||||||
if (aState.state !== 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// FIXME: Change this to inorder when implemented?
|
|
||||||
var count = [[self preOrder] countByEnumeratingWithState:aState objects:objects count:aCount];
|
|
||||||
|
|
||||||
// Dangerous?
|
|
||||||
aState.state = YES;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPTreeNodeRepresentedObjectKey = @"CPTreeNodeRepresentedObjectKey",
|
|
||||||
CPTreeNodeParentNodeKey = @"CPTreeNodeParentNodeKey",
|
|
||||||
CPTreeNodeChildNodesKey = @"CPTreeNodeChildNodesKey";
|
|
||||||
|
|
||||||
@implementation CPTreeNode (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_representedObject = [aCoder decodeObjectForKey:CPTreeNodeRepresentedObjectKey];
|
|
||||||
_parentNode = [aCoder decodeObjectForKey:CPTreeNodeParentNodeKey];
|
|
||||||
_childNodes = [aCoder decodeObjectForKey:CPTreeNodeChildNodesKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[aCoder encodeObject:_representedObject forKey:CPTreeNodeRepresentedObjectKey];
|
|
||||||
[aCoder encodeConditionalObject:_parentNode forKey:CPTreeNodeParentNodeKey];
|
|
||||||
[aCoder encodeObject:_childNodes forKey:CPTreeNodeChildNodesKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var _CPTreeNodeEnumeratorPreOrder = 0,
|
|
||||||
_CPTreeNodeEnumeratorInOrder = 1,
|
|
||||||
_CPTreeNodeEnumeratorPostOrder = 2,
|
|
||||||
_CPTreeNodeEnumeratorLevelOrder = 3;
|
|
||||||
|
|
||||||
@implementation _CPTreeNodeEnumerator : CPEnumerator
|
|
||||||
{
|
|
||||||
int _order;
|
|
||||||
Object _state;
|
|
||||||
CPTreeNode _treeNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (_CPTreeNodeEnumerator)enumeratorWithTreeNode:(CPTreeNode)aTreeNode order:(int)anOrder
|
|
||||||
{
|
|
||||||
return [[self alloc] initWithTreeNode:aTreeNode order:anOrder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithTreeNode:(CPTreeNode)aTreeNode order:(int)anOrder
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_order = anOrder
|
|
||||||
_state = [self _newState];
|
|
||||||
_treeNode = aTreeNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_newState
|
|
||||||
{
|
|
||||||
return { stack:[_treeNode], visited:[CPSet set] };
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_preOrderFromState:(Object)state allObjects:(BOOL)shouldReturnAllObjects
|
|
||||||
{
|
|
||||||
var stack = state.stack;
|
|
||||||
|
|
||||||
if (stack.length <= 0)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
if (shouldReturnAllObjects)
|
|
||||||
var allObjects = [];
|
|
||||||
|
|
||||||
while (stack.length)
|
|
||||||
{
|
|
||||||
var currentNode = stack.pop(),
|
|
||||||
childNodes = currentNode._childNodes,
|
|
||||||
count = childNodes.length;
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
stack.push(childNodes[count]);
|
|
||||||
|
|
||||||
if (shouldReturnAllObjects)
|
|
||||||
allObjects.push(currentNode);
|
|
||||||
else
|
|
||||||
return currentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return allObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_inOrderFromState:(Object)state allObjects:(BOOL)shouldReturnAllObjects
|
|
||||||
{
|
|
||||||
// Implement.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_postOrderFromState:(Object)state allObjects:(BOOL)shouldReturnAllObjects
|
|
||||||
{
|
|
||||||
var stack = state.stack;
|
|
||||||
|
|
||||||
if (stack.length <= 0)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
var visited = state.visited;
|
|
||||||
|
|
||||||
if (shouldReturnAllObjects)
|
|
||||||
var allObjects = [];
|
|
||||||
|
|
||||||
while (stack.length)
|
|
||||||
{
|
|
||||||
var currentNode = [stack lastObject],
|
|
||||||
childNodes = currentNode._childNodes,
|
|
||||||
originalCount = stack.length,
|
|
||||||
count = childNodes.length;
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
var treeNode = childNodes[count];
|
|
||||||
|
|
||||||
if ([visited containsObject:treeNode])
|
|
||||||
continue;
|
|
||||||
else
|
|
||||||
stack.push(treeNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (originalCount === stack.length)
|
|
||||||
{
|
|
||||||
[visited addObject:currentNode];
|
|
||||||
|
|
||||||
if (shouldReturnAllObjects)
|
|
||||||
allObjects.push(stack.pop());
|
|
||||||
else
|
|
||||||
return stack.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return allObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_levelOrderFromState:(Object)state allObjects:(BOOL)shouldReturnAllObjects
|
|
||||||
{
|
|
||||||
// Implement.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)nextObject
|
|
||||||
{
|
|
||||||
if (_order === _CPTreeNodeEnumeratorPreOrder)
|
|
||||||
return [self _preOrderFromState:[self _newState] allObjects:NO];
|
|
||||||
|
|
||||||
else if (_order === _CPTreeNodeEnumeratorInOrder)
|
|
||||||
return [self _inOrderFromState:[self _newState] allObjects:NO];
|
|
||||||
|
|
||||||
else if (_order === _CPTreeNodeEnumeratorPostOrder)
|
|
||||||
return [self _postOrderFromState:[self _newState] allObjects:NO];
|
|
||||||
|
|
||||||
//else if (_order === _CPTreeNodeEnumeratorLevelOrder)
|
|
||||||
return [self _levelOrderFromState:[self _newState] allObjects:NO];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)allObjects
|
|
||||||
{
|
|
||||||
if (_order === _CPTreeNodeEnumeratorPreOrder)
|
|
||||||
return [self _preOrderFromState:[self _newState] allObjects:YES];
|
|
||||||
|
|
||||||
else if (_order === _CPTreeNodeEnumeratorInOrder)
|
|
||||||
return [self _inOrderFromState:[self _newState] allObjects:YES];
|
|
||||||
|
|
||||||
else if (_order === _CPTreeNodeEnumeratorPostOrder)
|
|
||||||
return [self _postOrderFromState:[self _newState] allObjects:YES];
|
|
||||||
|
|
||||||
//else if (_order === _CPTreeNodeEnumeratorLevelOrder)
|
|
||||||
return [self _levelOrderFromState:[self _newState] allObjects:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)countByEnumeratingWithState:(id)aState objects:(id)objects count:(id)aCount
|
|
||||||
{
|
|
||||||
if (aState.state !== 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var count = [[self allObjects] countByEnumeratingWithState:aState objects:objects count:aCount];
|
|
||||||
|
|
||||||
// Dangerous?
|
|
||||||
aState.state = YES;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+354
-691
File diff suppressed because it is too large
Load Diff
@@ -1,180 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPViewAnimation.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Klaas Pieter Annema on September 3, 2009.
|
|
||||||
* Copyright 2009, Sofa BV
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "CPAnimation.j"
|
|
||||||
|
|
||||||
|
|
||||||
CPViewAnimationTargetKey = @"CPViewAnimationTarget";
|
|
||||||
CPViewAnimationStartFrameKey = @"CPViewAnimationStartFrame";
|
|
||||||
CPViewAnimationEndFrameKey = @"CPViewAnimationEndFrame";
|
|
||||||
CPViewAnimationEffectKey = @"CPViewAnimationEffect";
|
|
||||||
|
|
||||||
CPViewAnimationFadeInEffect = @"CPViewAnimationFadeIn";
|
|
||||||
CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut";
|
|
||||||
|
|
||||||
@implementation CPViewAnimation : CPAnimation
|
|
||||||
{
|
|
||||||
CPArray _viewAnimations;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithViewAnimations:(CPArray)viewAnimations
|
|
||||||
{
|
|
||||||
if (self = [super initWithDuration:0.5 animationCurve:CPAnimationLinear])
|
|
||||||
{
|
|
||||||
[self setViewAnimations:viewAnimations];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)startAnimation
|
|
||||||
{
|
|
||||||
var animationIndex = [_viewAnimations count];
|
|
||||||
while (animationIndex--)
|
|
||||||
{
|
|
||||||
var dictionary = [_viewAnimations objectAtIndex:animationIndex],
|
|
||||||
view = [self _targetView:dictionary],
|
|
||||||
startFrame = [self _startFrame:dictionary];
|
|
||||||
|
|
||||||
[view setFrame:startFrame];
|
|
||||||
|
|
||||||
var effect = [self _effect:dictionary];
|
|
||||||
if (effect === CPViewAnimationFadeInEffect)
|
|
||||||
{
|
|
||||||
[view setAlphaValue:0.0];
|
|
||||||
[view setHidden:NO];
|
|
||||||
}
|
|
||||||
else if (effect === CPViewAnimationFadeOutEffect)
|
|
||||||
[view setAlphaValue:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
[super startAnimation];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setCurrentProgress:(CPAnimationProgress)progress
|
|
||||||
{
|
|
||||||
[super setCurrentProgress:progress];
|
|
||||||
|
|
||||||
var animationIndex = [_viewAnimations count];
|
|
||||||
while (animationIndex--)
|
|
||||||
{
|
|
||||||
var dictionary = [_viewAnimations objectAtIndex:animationIndex],
|
|
||||||
view = [self _targetView:dictionary],
|
|
||||||
startFrame = [self _startFrame:dictionary],
|
|
||||||
endFrame = [self _endFrame:dictionary],
|
|
||||||
differenceFrame = _CGRectMakeZero();
|
|
||||||
|
|
||||||
differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x;
|
|
||||||
differenceFrame.origin.y = endFrame.origin.y - startFrame.origin.y;
|
|
||||||
differenceFrame.size.width = endFrame.size.width - startFrame.size.width;
|
|
||||||
differenceFrame.size.height = endFrame.size.height - startFrame.size.height;
|
|
||||||
|
|
||||||
var intermediateFrame = _CGRectMakeZero();
|
|
||||||
intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * progress;
|
|
||||||
intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * progress;
|
|
||||||
intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * progress;
|
|
||||||
intermediateFrame.size.height = startFrame.size.height + differenceFrame.size.height * progress;
|
|
||||||
|
|
||||||
[view setFrame:intermediateFrame];
|
|
||||||
|
|
||||||
// Update the view's alpha value
|
|
||||||
var effect = [self _effect:dictionary];
|
|
||||||
if (effect === CPViewAnimationFadeInEffect)
|
|
||||||
[view setAlphaValue:1.0 * progress];
|
|
||||||
else if (effect === CPViewAnimationFadeOutEffect)
|
|
||||||
[view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * progress];
|
|
||||||
|
|
||||||
if (progress === 1.0)
|
|
||||||
[view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)stopAnimation
|
|
||||||
{
|
|
||||||
var animationIndex = [_viewAnimations count];
|
|
||||||
while (animationIndex--)
|
|
||||||
{
|
|
||||||
var dictionary = [_viewAnimations objectAtIndex:animationIndex],
|
|
||||||
view = [self _targetView:dictionary],
|
|
||||||
endFrame = [self _endFrame:dictionary];
|
|
||||||
|
|
||||||
[view setFrame:endFrame];
|
|
||||||
|
|
||||||
var effect = [self _effect:dictionary];
|
|
||||||
if (effect === CPViewAnimationFadeInEffect)
|
|
||||||
[view setAlphaValue:1.0];
|
|
||||||
else if (effect === CPViewAnimationFadeOutEffect)
|
|
||||||
[view setAlphaValue:0.0];
|
|
||||||
|
|
||||||
[view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
[super stopAnimation];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_targetView:(CPDictionary)dictionary
|
|
||||||
{
|
|
||||||
var targetView = [dictionary valueForKey:CPViewAnimationTargetKey];
|
|
||||||
if (!targetView)
|
|
||||||
[CPException raise:CPInternalInconsistencyException reason:[CPString stringWithFormat:@"view animation: %@ does not have a target view", [dictionary description]]];
|
|
||||||
|
|
||||||
return targetView;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)_startFrame:(CPDictionary)dictionary
|
|
||||||
{
|
|
||||||
var startFrame = [dictionary valueForKey:CPViewAnimationStartFrameKey];
|
|
||||||
if (!startFrame)
|
|
||||||
return [[self _targetView:dictionary] frame];
|
|
||||||
|
|
||||||
return startFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)_endFrame:(CPDictionary)dictionary
|
|
||||||
{
|
|
||||||
var endFrame = [dictionary valueForKey:CPViewAnimationEndFrameKey];
|
|
||||||
if (!endFrame)
|
|
||||||
return [[self _targetView:dictionary] frame];
|
|
||||||
|
|
||||||
return endFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)_effect:(CPDictionary)dictionary
|
|
||||||
{
|
|
||||||
return [dictionary valueForKey:CPViewAnimationEffectKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)viewAnimations
|
|
||||||
{
|
|
||||||
return _viewAnimations;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setViewAnimations:(CPArray)viewAnimations
|
|
||||||
{
|
|
||||||
if (viewAnimations != _viewAnimations)
|
|
||||||
{
|
|
||||||
[self stopAnimation];
|
|
||||||
_viewAnimations = [viewAnimations copy];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 <Foundation/CPArray.j>
|
|
||||||
|
|
||||||
@import "CPApplication.j"
|
|
||||||
@import "CPCib.j"
|
|
||||||
@import "CPResponder.j"
|
|
||||||
|
|
||||||
|
|
||||||
var CPViewControllerCachedCibs;
|
|
||||||
|
|
||||||
/*! @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 @accessors(property=view);
|
|
||||||
BOOL _isLoading;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self === CPViewController)
|
|
||||||
CPViewControllerCachedCibs = [CPDictionary dictionary];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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];
|
|
||||||
|
|
||||||
_isLoading = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, 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 synchronous.
|
|
||||||
*/
|
|
||||||
- (void)loadView
|
|
||||||
{
|
|
||||||
if (_view)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// check if a cib is already cached for the current _cibName
|
|
||||||
var cib = [CPViewControllerCachedCibs objectForKey:_cibName];
|
|
||||||
|
|
||||||
if (!cib)
|
|
||||||
{
|
|
||||||
// if the cib isn't cached yet : fetch it and cache it
|
|
||||||
cib = [[CPCib alloc] initWithContentsOfURL:[_cibBundle pathForResource:_cibName + @".cib"]];
|
|
||||||
[CPViewControllerCachedCibs setObject:cib forKey:_cibName];
|
|
||||||
}
|
|
||||||
|
|
||||||
[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.
|
|
||||||
*/
|
|
||||||
- (CPView)view
|
|
||||||
{
|
|
||||||
if (!_view)
|
|
||||||
{
|
|
||||||
_isLoading = YES;
|
|
||||||
|
|
||||||
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 (!_view)
|
|
||||||
{
|
|
||||||
var reason = [CPString stringWithFormat:@"View for %@ could not be loaded from Cib or no view specified. Override loadView to load the view manually.", self];
|
|
||||||
|
|
||||||
[CPException raise:CPInternalInconsistencyException reason:reason];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([cibOwner respondsToSelector:@selector(viewControllerDidLoadCib:)])
|
|
||||||
[cibOwner viewControllerDidLoadCib:self];
|
|
||||||
|
|
||||||
_isLoading = NO;
|
|
||||||
[self viewDidLoad];
|
|
||||||
}
|
|
||||||
|
|
||||||
return _view;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
This method is called after the view controller has loaded its associated views into memory.
|
|
||||||
This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method.
|
|
||||||
This method is most commonly used to perform additional initialization steps on views that are loaded from cib files.
|
|
||||||
*/
|
|
||||||
- (void)viewDidLoad
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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
|
|
||||||
{
|
|
||||||
var viewWasLoaded = !_view;
|
|
||||||
|
|
||||||
_view = aView;
|
|
||||||
|
|
||||||
// Make sure the viewDidLoad method is called if the view is set directly
|
|
||||||
if (!_isLoading && viewWasLoaded)
|
|
||||||
[self viewDidLoad];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
var CPViewControllerViewKey = @"CPViewControllerViewKey",
|
|
||||||
CPViewControllerTitleKey = @"CPViewControllerTitleKey",
|
|
||||||
CPViewControllerCibNameKey = @"CPViewControllerCibNameKey",
|
|
||||||
CPViewControllerBundleKey = @"CPViewControllerBundleKey";
|
|
||||||
|
|
||||||
@implementation CPViewController (CPCoding)
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes the view controller by unarchiving data from a coder.
|
|
||||||
@param aCoder the coder from which the data will be unarchived
|
|
||||||
@return the initialized view controller
|
|
||||||
*/
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_view = [aCoder decodeObjectForKey:CPViewControllerViewKey];
|
|
||||||
_title = [aCoder decodeObjectForKey:CPViewControllerTitleKey];
|
|
||||||
_cibName = [aCoder decodeObjectForKey:CPViewControllerCibNameKey];
|
|
||||||
|
|
||||||
var bundlePath = [aCoder decodeObjectForKey:CPViewControllerBundleKey];
|
|
||||||
_cibBundle = bundlePath ? [CPBundle bundleWithPath:bundlePath] : [CPBundle mainBundle];
|
|
||||||
|
|
||||||
_cibExternalNameTable = [CPDictionary dictionaryWithObject:self forKey:CPCibOwner];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Archives the view controller to the provided coder.
|
|
||||||
@param aCoder the coder to which the view controller should be archived
|
|
||||||
*/
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_view forKey:CPViewControllerViewKey];
|
|
||||||
[aCoder encodeObject:_title forKey:CPViewControllerTitleKey];
|
|
||||||
[aCoder encodeObject:_cibName forKey:CPViewControllerCibNameKey];
|
|
||||||
[aCoder encodeObject:[_cibBundle bundlePath] forKey:CPViewControllerBundleKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+57
-102
@@ -20,8 +20,9 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPView.j"
|
@import <AppKit/CPView.j>
|
||||||
@import "CPScrollView.j"
|
|
||||||
|
#include "Platform/Platform.h"
|
||||||
|
|
||||||
|
|
||||||
// FIXME: implement these where possible:
|
// FIXME: implement these where possible:
|
||||||
@@ -49,32 +50,30 @@ CPWebViewScrollNative = 2;
|
|||||||
{
|
{
|
||||||
CPScrollView _scrollView;
|
CPScrollView _scrollView;
|
||||||
CPView _frameView;
|
CPView _frameView;
|
||||||
|
|
||||||
IFrame _iframe;
|
IFrame _iframe;
|
||||||
CPString _mainFrameURL;
|
CPString _mainFrameURL;
|
||||||
CPArray _backwardStack;
|
CPArray _backwardStack;
|
||||||
CPArray _forwardStack;
|
CPArray _forwardStack;
|
||||||
|
|
||||||
BOOL _ignoreLoadStart;
|
BOOL _ignoreLoadStart;
|
||||||
BOOL _ignoreLoadEnd;
|
BOOL _ignoreLoadEnd;
|
||||||
|
|
||||||
id _downloadDelegate;
|
id _downloadDelegate;
|
||||||
id _frameLoadDelegate;
|
id _frameLoadDelegate;
|
||||||
id _policyDelegate;
|
id _policyDelegate;
|
||||||
id _resourceLoadDelegate;
|
id _resourceLoadDelegate;
|
||||||
id _UIDelegate;
|
id _UIDelegate;
|
||||||
|
|
||||||
CPWebScriptObject _wso;
|
CPWebScriptObject _wso;
|
||||||
|
|
||||||
CPString _url;
|
CPString _url;
|
||||||
CPString _html;
|
CPString _html;
|
||||||
|
|
||||||
Function _loadCallback;
|
Function _loadCallback;
|
||||||
|
|
||||||
int _scrollMode;
|
int _scrollMode;
|
||||||
CGSize _scrollSize;
|
CGSize _scrollSize;
|
||||||
|
|
||||||
int _loadHTMLStringTimer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CPRect)frameRect frameName:(CPString)frameName groupName:(CPString)groupName
|
- (id)initWithFrame:(CPRect)frameRect frameName:(CPString)frameName groupName:(CPString)groupName
|
||||||
@@ -94,10 +93,10 @@ CPWebViewScrollNative = 2;
|
|||||||
_backwardStack = [];
|
_backwardStack = [];
|
||||||
_forwardStack = [];
|
_forwardStack = [];
|
||||||
_scrollMode = CPWebViewScrollNative;
|
_scrollMode = CPWebViewScrollNative;
|
||||||
|
|
||||||
[self _initDOMWithFrame:aFrame];
|
[self _initDOMWithFrame:aFrame];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,62 +104,59 @@ CPWebViewScrollNative = 2;
|
|||||||
{
|
{
|
||||||
_ignoreLoadStart = YES;
|
_ignoreLoadStart = YES;
|
||||||
_ignoreLoadEnd = YES;
|
_ignoreLoadEnd = YES;
|
||||||
|
|
||||||
_iframe = document.createElement("iframe");
|
_iframe = document.createElement("iframe");
|
||||||
_iframe.name = "iframe_" + Math.floor(Math.random()*10000);
|
_iframe.name = "iframe_" + Math.floor(Math.random()*10000);
|
||||||
_iframe.style.width = "100%";
|
_iframe.style.width = "100%";
|
||||||
_iframe.style.height = "100%";
|
_iframe.style.height = "100%";
|
||||||
_iframe.style.borderWidth = "0px";
|
_iframe.style.borderWidth = "0px";
|
||||||
_iframe.frameBorder = "0";
|
|
||||||
|
|
||||||
[self setDrawsBackground:YES];
|
[self setDrawsBackground:YES];
|
||||||
|
|
||||||
_loadCallback = function() {
|
_loadCallback = function() {
|
||||||
// HACK: this block handles the case where we don't know about loads initiated by the user clicking a link
|
// HACK: this block handles the case where we don't know about loads initiated by the user clicking a link
|
||||||
if (!_ignoreLoadStart)
|
if (!_ignoreLoadStart)
|
||||||
{
|
{
|
||||||
// post the start load notification
|
// post the start load notification
|
||||||
[self _startedLoading];
|
[self _startedLoading];
|
||||||
|
|
||||||
if (_mainFrameURL)
|
if (_mainFrameURL)
|
||||||
[_backwardStack addObject:_mainFrameURL];
|
[_backwardStack addObject:_mainFrameURL];
|
||||||
|
|
||||||
// FIXME: this doesn't actually get the right URL for different domains. Not possible due to browser security restrictions.
|
// FIXME: this doesn't actually get the right URL for different domains. Not possible due to browser security restrictions.
|
||||||
_mainFrameURL = _iframe.src;
|
_mainFrameURL = _iframe.src;
|
||||||
_mainFrameURL = _iframe.src;
|
_mainFrameURL = _iframe.src;
|
||||||
|
|
||||||
// clear the forward
|
// clear the forward
|
||||||
[_forwardStack removeAllObjects];
|
[_forwardStack removeAllObjects];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_ignoreLoadStart = NO;
|
_ignoreLoadStart = NO;
|
||||||
|
|
||||||
if (!_ignoreLoadEnd)
|
if (!_ignoreLoadEnd)
|
||||||
{
|
{
|
||||||
[self _finishedLoading];
|
[self _finishedLoading];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_ignoreLoadEnd = NO;
|
_ignoreLoadEnd = NO;
|
||||||
|
|
||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_iframe.addEventListener)
|
if (_iframe.addEventListener)
|
||||||
_iframe.addEventListener("load", _loadCallback, false);
|
_iframe.addEventListener("load", _loadCallback, false);
|
||||||
else if (_iframe.attachEvent)
|
else if (_iframe.attachEvent)
|
||||||
_iframe.attachEvent("onload", _loadCallback);
|
_iframe.attachEvent("onload", _loadCallback);
|
||||||
|
|
||||||
|
|
||||||
_frameView = [[CPView alloc] initWithFrame:[self bounds]];
|
_frameView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||||
[_frameView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
|
||||||
|
|
||||||
_scrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
_scrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
||||||
[_scrollView setAutohidesScrollers:YES];
|
|
||||||
[_scrollView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
[_scrollView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||||
[_scrollView setDocumentView:_frameView];
|
[_scrollView setDocumentView:_frameView];
|
||||||
|
|
||||||
_frameView._DOMElement.appendChild(_iframe);
|
_frameView._DOMElement.appendChild(_iframe);
|
||||||
|
|
||||||
[self _setScrollMode:_scrollMode];
|
[self _setScrollMode:_scrollMode];
|
||||||
|
|
||||||
[self addSubview:_scrollView];
|
[self addSubview:_scrollView];
|
||||||
@@ -168,37 +164,13 @@ CPWebViewScrollNative = 2;
|
|||||||
|
|
||||||
|
|
||||||
- (void)setFrameSize:(CPSize)aSize
|
- (void)setFrameSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
[super setFrameSize:aSize];
|
[super setFrameSize:aSize];
|
||||||
|
|
||||||
[self _resizeWebFrame];
|
[self _resizeWebFrame];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_attachScrollEventIfNecessary
|
- (BOOL)_resizeWebFrame
|
||||||
{
|
|
||||||
if (_scrollMode !== CPWebViewScrollAppKit)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var win = null;
|
|
||||||
try { win = [self DOMWindow]; } catch (e) {}
|
|
||||||
|
|
||||||
if (win && win.addEventListener)
|
|
||||||
{
|
|
||||||
var scrollEventHandler = function(anEvent)
|
|
||||||
{
|
|
||||||
var frameBounds = [self bounds],
|
|
||||||
frameCenter = CGPointMake(CGRectGetMidX(frameBounds), CGRectGetMidY(frameBounds)),
|
|
||||||
windowOrigin = [self convertPoint:frameCenter toView:nil],
|
|
||||||
globalOrigin = [[self window] convertBaseToBridge:windowOrigin];
|
|
||||||
|
|
||||||
anEvent._overrideLocation = globalOrigin;
|
|
||||||
[[[self window] platformWindow] scrollEvent:anEvent];
|
|
||||||
};
|
|
||||||
|
|
||||||
win.addEventListener("DOMMouseScroll", scrollEventHandler, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_resizeWebFrame
|
|
||||||
{
|
{
|
||||||
if (_scrollMode === CPWebViewScrollAppKit)
|
if (_scrollMode === CPWebViewScrollAppKit)
|
||||||
{
|
{
|
||||||
@@ -208,14 +180,13 @@ CPWebViewScrollNative = 2;
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var visibleRect = [_frameView visibleRect];
|
[_frameView setFrameSize:[_scrollView bounds].size];
|
||||||
[_frameView setFrameSize:CGSizeMake(CGRectGetMaxX(visibleRect), CGRectGetMaxY(visibleRect))];
|
|
||||||
|
|
||||||
// try to get the document size so we can correctly set the frame
|
// try to get the document size so we can correctly set the frame
|
||||||
var win = null;
|
var win = null;
|
||||||
try { win = [self DOMWindow]; } catch (e) {}
|
try { win = [self DOMWindow]; } catch (e) {}
|
||||||
|
|
||||||
if (win && win.document && win.document.body)
|
if (win && win.document)
|
||||||
{
|
{
|
||||||
var width = win.document.body.scrollWidth,
|
var width = win.document.body.scrollWidth,
|
||||||
height = win.document.body.scrollHeight;
|
height = win.document.body.scrollHeight;
|
||||||
@@ -228,11 +199,9 @@ CPWebViewScrollNative = 2;
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
CPLog.warn("using default size 800*1600");
|
CPLog.warn("using default size 800*1600");
|
||||||
|
|
||||||
[_frameView setFrameSize:CGSizeMake(800, 1600)];
|
[_frameView setFrameSize:CGSizeMake(800, 1600)];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_frameView scrollRectToVisible:visibleRect];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,17 +210,14 @@ CPWebViewScrollNative = 2;
|
|||||||
{
|
{
|
||||||
if (_scrollMode == aScrollMode)
|
if (_scrollMode == aScrollMode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self _setScrollMode:aScrollMode];
|
[self _setScrollMode:aScrollMode];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_setScrollMode:(int)aScrollMode
|
- (void)_setScrollMode:(int)aScrollMode
|
||||||
{
|
{
|
||||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
_scrollMode = aScrollMode;
|
||||||
_scrollMode = CPWebViewScrollNative;
|
|
||||||
else
|
|
||||||
_scrollMode = aScrollMode;
|
|
||||||
|
|
||||||
_ignoreLoadStart = YES;
|
_ignoreLoadStart = YES;
|
||||||
_ignoreLoadEnd = YES;
|
_ignoreLoadEnd = YES;
|
||||||
|
|
||||||
@@ -262,16 +228,16 @@ CPWebViewScrollNative = 2;
|
|||||||
{
|
{
|
||||||
[_scrollView setHasHorizontalScroller:YES];
|
[_scrollView setHasHorizontalScroller:YES];
|
||||||
[_scrollView setHasVerticalScroller:YES];
|
[_scrollView setHasVerticalScroller:YES];
|
||||||
|
|
||||||
_iframe.setAttribute("scrolling", "no");
|
_iframe.setAttribute("scrolling", "no");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[_scrollView setHasHorizontalScroller:NO];
|
[_scrollView setHasHorizontalScroller:NO];
|
||||||
[_scrollView setHasVerticalScroller:NO];
|
[_scrollView setHasVerticalScroller:NO];
|
||||||
|
|
||||||
_iframe.setAttribute("scrolling", "auto");
|
_iframe.setAttribute("scrolling", "auto");
|
||||||
|
|
||||||
[_frameView setFrameSize:[_scrollView bounds].size];
|
[_frameView setFrameSize:[_scrollView bounds].size];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,16 +255,14 @@ CPWebViewScrollNative = 2;
|
|||||||
|
|
||||||
[self _setScrollMode:CPWebViewScrollAppKit];
|
[self _setScrollMode:CPWebViewScrollAppKit];
|
||||||
|
|
||||||
[_frameView setFrameSize:[_scrollView contentSize]];
|
|
||||||
|
|
||||||
[self _startedLoading];
|
[self _startedLoading];
|
||||||
|
|
||||||
_ignoreLoadStart = YES;
|
_ignoreLoadStart = YES;
|
||||||
_ignoreLoadEnd = NO;
|
_ignoreLoadEnd = NO;
|
||||||
|
|
||||||
_url = null;
|
_url = null;
|
||||||
_html = aString;
|
_html = aString;
|
||||||
|
|
||||||
[self _load];
|
[self _load];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,13 +271,13 @@ CPWebViewScrollNative = 2;
|
|||||||
[self _setScrollMode:CPWebViewScrollNative];
|
[self _setScrollMode:CPWebViewScrollNative];
|
||||||
|
|
||||||
[self _startedLoading];
|
[self _startedLoading];
|
||||||
|
|
||||||
_ignoreLoadStart = YES;
|
_ignoreLoadStart = YES;
|
||||||
_ignoreLoadEnd = NO;
|
_ignoreLoadEnd = NO;
|
||||||
|
|
||||||
_url = _mainFrameURL;
|
_url = _mainFrameURL;
|
||||||
_html = null;
|
_html = null;
|
||||||
|
|
||||||
[self _load];
|
[self _load];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,19 +292,11 @@ CPWebViewScrollNative = 2;
|
|||||||
// clear the iframe
|
// clear the iframe
|
||||||
_iframe.src = "";
|
_iframe.src = "";
|
||||||
|
|
||||||
if (_loadHTMLStringTimer !== nil)
|
// need to give the browser a chance to reset iframe, otherwise we'll be document.write()-ing the previous document
|
||||||
{
|
window.setTimeout(function() {
|
||||||
window.clearTimeout(_loadHTMLStringTimer);
|
|
||||||
_loadHTMLStringTimer = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to give the browser a chance to reset iframe, otherwise we'll be document.write()-ing the previous document
|
|
||||||
_loadHTMLStringTimer = window.setTimeout(function()
|
|
||||||
{
|
|
||||||
var win = [self DOMWindow];
|
var win = [self DOMWindow];
|
||||||
|
|
||||||
if (win)
|
win.document.write(_html);
|
||||||
win.document.write(_html);
|
|
||||||
|
|
||||||
window.setTimeout(_loadCallback, 1);
|
window.setTimeout(_loadCallback, 1);
|
||||||
}, 0);
|
}, 0);
|
||||||
@@ -358,8 +314,7 @@ CPWebViewScrollNative = 2;
|
|||||||
- (void)_finishedLoading
|
- (void)_finishedLoading
|
||||||
{
|
{
|
||||||
[self _resizeWebFrame];
|
[self _resizeWebFrame];
|
||||||
[self _attachScrollEventIfNecessary];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWebViewProgressFinishedNotification object:self];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPWebViewProgressFinishedNotification object:self];
|
||||||
|
|
||||||
if ([_frameLoadDelegate respondsToSelector:@selector(webView:didFinishLoadForFrame:)])
|
if ([_frameLoadDelegate respondsToSelector:@selector(webView:didFinishLoadForFrame:)])
|
||||||
@@ -372,7 +327,7 @@ CPWebViewScrollNative = 2;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)setMainFrameURL:(CPString)URLString
|
- (void)setMainFrameURL:(CPString)URLString
|
||||||
{
|
{
|
||||||
if (_mainFrameURL)
|
if (_mainFrameURL)
|
||||||
[_backwardStack addObject:_mainFrameURL];
|
[_backwardStack addObject:_mainFrameURL];
|
||||||
_mainFrameURL = URLString;
|
_mainFrameURL = URLString;
|
||||||
@@ -389,9 +344,9 @@ CPWebViewScrollNative = 2;
|
|||||||
[_forwardStack addObject:_mainFrameURL];
|
[_forwardStack addObject:_mainFrameURL];
|
||||||
_mainFrameURL = [_backwardStack lastObject];
|
_mainFrameURL = [_backwardStack lastObject];
|
||||||
[_backwardStack removeLastObject];
|
[_backwardStack removeLastObject];
|
||||||
|
|
||||||
[self _loadMainFrameURL];
|
[self _loadMainFrameURL];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
@@ -405,9 +360,9 @@ CPWebViewScrollNative = 2;
|
|||||||
[_backwardStack addObject:_mainFrameURL];
|
[_backwardStack addObject:_mainFrameURL];
|
||||||
_mainFrameURL = [_forwardStack lastObject];
|
_mainFrameURL = [_forwardStack lastObject];
|
||||||
[_forwardStack removeLastObject];
|
[_forwardStack removeLastObject];
|
||||||
|
|
||||||
[self _loadMainFrameURL];
|
[self _loadMainFrameURL];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
@@ -630,7 +585,7 @@ CPWebViewScrollNative = 2;
|
|||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super initWithCoder:aCoder];
|
self = [super initWithCoder:aCoder];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
// FIXME: encode/decode these?
|
// FIXME: encode/decode these?
|
||||||
@@ -638,14 +593,14 @@ CPWebViewScrollNative = 2;
|
|||||||
_backwardStack = [];
|
_backwardStack = [];
|
||||||
_forwardStack = [];
|
_forwardStack = [];
|
||||||
_scrollMode = CPWebViewScrollNative;
|
_scrollMode = CPWebViewScrollNative;
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
[self _initDOMWithFrame:[self frame]];
|
[self _initDOMWithFrame:[self frame]];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[self setBackgroundColor:[CPColor whiteColor]];
|
[self setBackgroundColor:[CPColor whiteColor]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+452
-1195
File diff suppressed because it is too large
Load Diff
@@ -38,11 +38,6 @@ var _CPToolbarViewBackgroundColor = nil;
|
|||||||
return _CPToolbarViewBackgroundColor;
|
return _CPToolbarViewBackgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator
|
|
||||||
{
|
|
||||||
// We don't ever want to show the resize indicator.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tile
|
- (void)tile
|
||||||
{
|
{
|
||||||
[super tile];
|
[super tile];
|
||||||
@@ -71,12 +66,11 @@ var _CPToolbarViewBackgroundColor = nil;
|
|||||||
[self addSubview:_toolbarBackgroundView positioned:CPWindowBelow relativeTo:nil];
|
[self addSubview:_toolbarBackgroundView positioned:CPWindowBelow relativeTo:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
var frame = CGRectMakeZero(),
|
var frame = CGRectMakeZero();
|
||||||
toolbarOffset = [self toolbarOffset];
|
|
||||||
|
frame.origin = CGPointMakeCopy([self toolbarOffset]);
|
||||||
frame.origin = CGPointMake(toolbarOffset.width, toolbarOffset.height);
|
|
||||||
frame.size = [_toolbarView frame].size;
|
frame.size = [_toolbarView frame].size;
|
||||||
|
|
||||||
[_toolbarBackgroundView setFrame:frame];
|
[_toolbarBackgroundView setFrame:frame];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
|
|
||||||
@import "_CPWindowView.j"
|
|
||||||
|
|
||||||
var _CPStandardWindowViewBodyBackgroundColor = nil;
|
|
||||||
|
|
||||||
@implementation _CPDocModalWindowView : _CPWindowView
|
|
||||||
{
|
|
||||||
CPView _bodyView;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPColor)bodyBackgroundColor
|
|
||||||
{
|
|
||||||
if (!_CPStandardWindowViewBodyBackgroundColor)
|
|
||||||
_CPStandardWindowViewBodyBackgroundColor = [CPColor colorWithWhite:0.96 alpha:0.9];
|
|
||||||
|
|
||||||
return _CPStandardWindowViewBodyBackgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame styleMask:aStyleMask];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
var theClass = [self class],
|
|
||||||
bounds = [self bounds];
|
|
||||||
|
|
||||||
_bodyView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), CGRectGetHeight(bounds))];
|
|
||||||
|
|
||||||
[_bodyView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
||||||
[_bodyView setBackgroundColor:[theClass bodyBackgroundColor]];
|
|
||||||
[_bodyView setHitTests:NO];
|
|
||||||
|
|
||||||
[self addSubview:_bodyView];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
|
||||||
{
|
|
||||||
return aFrameRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
|
||||||
{
|
|
||||||
return aContentRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -43,21 +43,21 @@ var HUD_TITLEBAR_HEIGHT = 26.0;
|
|||||||
|
|
||||||
_CPHUDWindowViewBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
_CPHUDWindowViewBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||||
[
|
[
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground0.png"] size:CPSizeMake(7.0, 37.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground0.png"] size:CPSizeMake(6.0, 78.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground1.png"] size:CPSizeMake(1.0, 37.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground1.png"] size:CPSizeMake(1.0, 78.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground2.png"] size:CPSizeMake(7.0, 37.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground2.png"] size:CPSizeMake(6.0, 78.0)],
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground3.png"] size:CPSizeMake(7.0, 1.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground3.png"] size:CPSizeMake(6.0, 1.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground4.png"] size:CPSizeMake(2.0, 2.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground4.png"] size:CPSizeMake(1.0, 1.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground5.png"] size:CPSizeMake(7.0, 1.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground5.png"] size:CPSizeMake(6.0, 1.0)],
|
||||||
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground6.png"] size:CPSizeMake(7.0, 3.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground6.png"] size:CPSizeMake(6.0, 6.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground7.png"] size:CPSizeMake(1.0, 3.0)],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground7.png"] size:CPSizeMake(6.0, 6.0)],
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground8.png"] size:CPSizeMake(7.0, 3.0)]
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground8.png"] size:CPSizeMake(6.0, 6.0)]
|
||||||
]]];
|
]]];
|
||||||
|
|
||||||
_CPHUDWindowViewCloseImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowClose.png"] size:CPSizeMake(18.0, 18.0)];
|
_CPHUDWindowViewCloseImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowClose.png"] size:CPSizeMake(20.0, 20.0)];
|
||||||
_CPHUDWindowViewCloseActiveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCloseActive.png"] size:CPSizeMake(18.0, 18.0)];
|
_CPHUDWindowViewCloseActiveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCloseActive.png"] size:CPSizeMake(20.0, 20.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||||
@@ -148,7 +148,7 @@ var HUD_TITLEBAR_HEIGHT = 26.0;
|
|||||||
{
|
{
|
||||||
var closeSize = [_CPHUDWindowViewCloseImage size];
|
var closeSize = [_CPHUDWindowViewCloseImage size];
|
||||||
|
|
||||||
_closeButton = [[CPButton alloc] initWithFrame:CGRectMake(8.0, 5.0, closeSize.width, closeSize.height)];
|
_closeButton = [[CPButton alloc] initWithFrame:CGRectMake(4.0, 4.0, closeSize.width, closeSize.height)];
|
||||||
|
|
||||||
[_closeButton setBordered:NO];
|
[_closeButton setBordered:NO];
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
@import "_CPWindowView.j"
|
@import "_CPWindowView.j"
|
||||||
|
|
||||||
|
|
||||||
var GRADIENT_HEIGHT = 41.0;
|
var GRADIENT_HEIGHT = 41.0;
|
||||||
|
|
||||||
var _CPTexturedWindowHeadGradientColor = nil,
|
var _CPTexturedWindowHeadGradientColor = nil,
|
||||||
@@ -57,7 +56,7 @@ var _CPTexturedWindowHeadGradientColor = nil,
|
|||||||
+ (CPColor)solidColor
|
+ (CPColor)solidColor
|
||||||
{
|
{
|
||||||
if (!_CPTexturedWindowHeadSolidColor)
|
if (!_CPTexturedWindowHeadSolidColor)
|
||||||
_CPTexturedWindowHeadSolidColor = [CPColor colorWithCalibratedRed:195.0 / 255.0 green:195.0 / 255.0 blue:195.0 / 255.0 alpha:1.0];
|
_CPTexturedWindowHeadSolidColor = [CPColor colorWithCalibratedRed:182.0 / 255.0 green:182.0 / 255.0 blue:182.0 / 255.0 alpha:1.0];
|
||||||
|
|
||||||
return _CPTexturedWindowHeadSolidColor;
|
return _CPTexturedWindowHeadSolidColor;
|
||||||
}
|
}
|
||||||
@@ -95,15 +94,11 @@ var _CPTexturedWindowHeadGradientColor = nil,
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var _CPStandardWindowViewBodyBackgroundColor = nil,
|
var _CPStandardWindowViewBodyBackgroundColor = nil,
|
||||||
_CPStandardWindowViewDividerBackgroundColor = nil,
|
_CPStandardWindowViewDividerBackgroundColor = nil,
|
||||||
_CPStandardWindowViewTitleBackgroundColor = nil,
|
_CPStandardWindowViewTitleBackgroundColor = nil,
|
||||||
_CPStandardWindowViewCloseButtonImage = nil,
|
_CPStandardWindowViewCloseButtonImage = nil,
|
||||||
_CPStandardWindowViewCloseButtonHighlightedImage = nil,
|
_CPStandardWindowViewCloseButtonHighlightedImage = nil;
|
||||||
_CPStandardWindowViewCloseButtonUnsavedImage = nil,
|
|
||||||
_CPStandardWindowViewCloseButtonUnsavedHighlightedImage = nil,
|
|
||||||
_CPStandardWindowViewMinimizeButtonImage = nil,
|
|
||||||
_CPStandardWindowViewMinimizeButtonHighlightedImage = nil;
|
|
||||||
|
|
||||||
var STANDARD_GRADIENT_HEIGHT = 41.0;
|
var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||||
STANDARD_TITLEBAR_HEIGHT = 25.0;
|
STANDARD_TITLEBAR_HEIGHT = 25.0;
|
||||||
@@ -117,9 +112,6 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
|||||||
|
|
||||||
CPTextField _titleField;
|
CPTextField _titleField;
|
||||||
CPButton _closeButton;
|
CPButton _closeButton;
|
||||||
CPButton _minimizeButton;
|
|
||||||
|
|
||||||
BOOL _isDocumentEdited;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPColor)bodyBackgroundColor
|
+ (CPColor)bodyBackgroundColor
|
||||||
@@ -238,7 +230,7 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
|||||||
[_bodyView setHitTests:NO];
|
[_bodyView setHitTests:NO];
|
||||||
|
|
||||||
[self addSubview:_bodyView];
|
[self addSubview:_bodyView];
|
||||||
|
|
||||||
[self setResizeIndicatorOffset:CGSizeMake(2.0, 2.0)];
|
[self setResizeIndicatorOffset:CGSizeMake(2.0, 2.0)];
|
||||||
|
|
||||||
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||||
@@ -264,44 +256,24 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
|||||||
if (!_CPStandardWindowViewCloseButtonImage)
|
if (!_CPStandardWindowViewCloseButtonImage)
|
||||||
{
|
{
|
||||||
var bundle = [CPBundle bundleForClass:[CPWindow class]];
|
var bundle = [CPBundle bundleForClass:[CPWindow class]];
|
||||||
|
|
||||||
_CPStandardWindowViewCloseButtonImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButton.png"] size:CGSizeMake(16.0, 16.0)];
|
_CPStandardWindowViewCloseButtonImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButton.png"] size:CGSizeMake(16.0, 16.0)];
|
||||||
_CPStandardWindowViewCloseButtonHighlightedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonHighlighted.png"] size:CGSizeMake(16.0, 16.0)];
|
_CPStandardWindowViewCloseButtonHighlightedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonHighlighted.png"] size:CGSizeMake(16.0, 16.0)];
|
||||||
_CPStandardWindowViewCloseButtonUnsavedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonUnsaved.png"] size:CGSizeMake(16.0, 16.0)];
|
|
||||||
_CPStandardWindowViewCloseButtonUnsavedHighlightedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonUnsavedHighlighted.png"] size:CGSizeMake(16.0, 16.0)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_closeButton = [[CPButton alloc] initWithFrame:CGRectMake(8.0, 7.0, 16.0, 16.0)];
|
_closeButton = [[CPButton alloc] initWithFrame:CGRectMake(8.0, 7.0, 16.0, 16.0)];
|
||||||
|
|
||||||
[_closeButton setBordered:NO];
|
[_closeButton setBordered:NO];
|
||||||
[self _updateCloseButton];
|
|
||||||
|
[_closeButton setImage:_CPStandardWindowViewCloseButtonImage];
|
||||||
|
[_closeButton setAlternateImage:_CPStandardWindowViewCloseButtonHighlightedImage];
|
||||||
|
|
||||||
[self addSubview:_closeButton];
|
[self addSubview:_closeButton];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_styleMask & CPMiniaturizableWindowMask && ![CPPlatform isBrowser])
|
|
||||||
{
|
|
||||||
if (!_CPStandardWindowViewMinimizeButtonImage)
|
|
||||||
{
|
|
||||||
var bundle = [CPBundle bundleForClass:[CPWindow class]];
|
|
||||||
|
|
||||||
_CPStandardWindowViewMinimizeButtonImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardMinimizeButton.png"] size:CGSizeMake(16.0, 16.0)];
|
|
||||||
_CPStandardWindowViewMinimizeButtonHighlightedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardMinimizeButtonHighlighted.png"] size:CGSizeMake(16.0, 16.0)];
|
|
||||||
}
|
|
||||||
|
|
||||||
_minimizeButton = [[CPButton alloc] initWithFrame:CGRectMake(27.0, 7.0, 16.0, 16.0)];
|
|
||||||
|
|
||||||
[_minimizeButton setBordered:NO];
|
|
||||||
|
|
||||||
[_minimizeButton setImage:_CPStandardWindowViewMinimizeButtonImage];
|
|
||||||
[_minimizeButton setAlternateImage:_CPStandardWindowViewMinimizeButtonHighlightedImage];
|
|
||||||
|
|
||||||
[self addSubview:_minimizeButton];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self tile];
|
[self tile];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,9 +281,6 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
|||||||
{
|
{
|
||||||
[_closeButton setTarget:[self window]];
|
[_closeButton setTarget:[self window]];
|
||||||
[_closeButton setAction:@selector(performClose:)];
|
[_closeButton setAction:@selector(performClose:)];
|
||||||
|
|
||||||
[_minimizeButton setTarget:[self window]];
|
|
||||||
[_minimizeButton setAction:@selector(performMiniaturize:)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGSize)toolbarOffset
|
- (CGSize)toolbarOffset
|
||||||
@@ -334,14 +303,7 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
|||||||
|
|
||||||
[_bodyView setFrame:CGRectMake(0.0, dividerMaxY, width, CGRectGetHeight(bounds) - dividerMaxY)];
|
[_bodyView setFrame:CGRectMake(0.0, dividerMaxY, width, CGRectGetHeight(bounds) - dividerMaxY)];
|
||||||
|
|
||||||
var leftOffset = 8;
|
[_titleField setFrame:CGRectMake(10.0, 3.0, width - 20.0, CGRectGetHeight([_titleField frame]))];
|
||||||
|
|
||||||
if (_closeButton)
|
|
||||||
leftOffset += 19.0;
|
|
||||||
if (_minimizeButton)
|
|
||||||
leftOffset += 19.0;
|
|
||||||
|
|
||||||
[_titleField setFrame:CGRectMake(leftOffset, 5.0, width - leftOffset*2.0, CGRectGetHeight([_titleField frame]))];
|
|
||||||
|
|
||||||
[[theWindow contentView] setFrameOrigin:CGPointMake(0.0, CGRectGetMaxY([_dividerView frame]))];
|
[[theWindow contentView] setFrameOrigin:CGPointMake(0.0, CGRectGetMaxY([_dividerView frame]))];
|
||||||
}
|
}
|
||||||
@@ -372,27 +334,6 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
- (void)_updateCloseButton
|
|
||||||
{
|
|
||||||
if (_isDocumentEdited)
|
|
||||||
{
|
|
||||||
[_closeButton setImage:_CPStandardWindowViewCloseButtonUnsavedImage];
|
|
||||||
[_closeButton setAlternateImage:_CPStandardWindowViewCloseButtonUnsavedHighlightedImage];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[_closeButton setImage:_CPStandardWindowViewCloseButtonImage];
|
|
||||||
[_closeButton setAlternateImage:_CPStandardWindowViewCloseButtonHighlightedImage];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDocumentEdited:(BOOL)isEdited
|
|
||||||
{
|
|
||||||
_isDocumentEdited = isEdited;
|
|
||||||
[self _updateCloseButton];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTitle:(CPString)aTitle
|
- (void)setTitle:(CPString)aTitle
|
||||||
{
|
{
|
||||||
[_titleField setStringValue:aTitle];
|
[_titleField setStringValue:aTitle];
|
||||||
|
|||||||
@@ -20,8 +20,10 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPImageView.j"
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
@import "CPImageView.j"
|
||||||
|
|
||||||
|
#include "../CoreGraphics/CGGeometry.h"
|
||||||
|
|
||||||
|
|
||||||
var _CPWindowViewResizeIndicatorImage = nil;
|
var _CPWindowViewResizeIndicatorImage = nil;
|
||||||
@@ -34,9 +36,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
CGSize _resizeIndicatorOffset;
|
CGSize _resizeIndicatorOffset;
|
||||||
|
|
||||||
CPView _toolbarView;
|
CPView _toolbarView;
|
||||||
CGSize _toolbarOffset;
|
|
||||||
// BOOL _isAnimatingToolbar;
|
// BOOL _isAnimatingToolbar;
|
||||||
|
|
||||||
|
|
||||||
CGRect _resizeFrame;
|
CGRect _resizeFrame;
|
||||||
CGPoint _mouseDraggedPoint;
|
CGPoint _mouseDraggedPoint;
|
||||||
@@ -79,17 +79,15 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_styleMask = aStyleMask;
|
_styleMask = aStyleMask;
|
||||||
_resizeIndicatorOffset = CGSizeMakeZero();
|
_resizeIndicatorOffset = CGSizeMake(0.0, 0.0);
|
||||||
_toolbarOffset = CGSizeMakeZero();
|
_toolbarOffset = CGSizeMake(0.0, 0.0);
|
||||||
|
|
||||||
|
[self setShowsResizeIndicator:!(_styleMask & CPBorderlessBridgeWindowMask) && (_styleMask & CPResizableWindowMask)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDocumentEdited:(BOOL)isEdited
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTitle:(CPString)aTitle
|
- (void)setTitle:(CPString)aTitle
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -102,7 +100,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var theWindow = [self window];
|
var theWindow = [self window];
|
||||||
|
|
||||||
if ((_styleMask & CPResizableWindowMask) && _resizeIndicator)
|
if ((_styleMask & CPResizableWindowMask) && _resizeIndicator)
|
||||||
{
|
{
|
||||||
// FIXME: This should be better
|
// FIXME: This should be better
|
||||||
@@ -137,38 +135,22 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
}
|
}
|
||||||
|
|
||||||
else if (type === CPLeftMouseDragged)
|
else if (type === CPLeftMouseDragged)
|
||||||
{
|
[theWindow setFrameSize:CGSizeMake(CGRectGetWidth(_resizeFrame) + location.x - CGRectGetMinX(_resizeFrame), CGRectGetHeight(_resizeFrame) + location.y - CGRectGetMinY(_resizeFrame))];
|
||||||
var newSize = CGSizeMake(CGRectGetWidth(_resizeFrame) + location.x - CGRectGetMinX(_resizeFrame), CGRectGetHeight(_resizeFrame) + location.y - CGRectGetMinY(_resizeFrame));
|
|
||||||
|
|
||||||
if (theWindow._isSheet && theWindow._parentView && (theWindow._frame.size.width !== newSize.width))
|
|
||||||
[theWindow._parentView _setAttachedSheetFrameOrigin];
|
|
||||||
|
|
||||||
[theWindow setFrameSize:newSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
[CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGPoint)_pointWithinScreenFrame:(CGPoint)aPoint
|
- (CGPoint)_pointWithinScreenFrame:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
// FIXME: this is WRONG, all of this is WRONG
|
|
||||||
if (![CPPlatform isBrowser])
|
|
||||||
return aPoint;
|
|
||||||
|
|
||||||
var visibleFrame = _cachedScreenFrame;
|
var visibleFrame = _cachedScreenFrame;
|
||||||
|
|
||||||
if (!visibleFrame)
|
if (!visibleFrame)
|
||||||
visibleFrame = [[CPPlatformWindow primaryPlatformWindow] visibleFrame];
|
visibleFrame = [[CPDOMWindowBridge sharedDOMWindowBridge] visibleFrame];
|
||||||
|
|
||||||
var minPointY = 0;
|
|
||||||
|
|
||||||
if([CPMenu menuBarVisible])
|
|
||||||
minPointY = [[CPApp mainMenu] menuBarHeight];
|
|
||||||
|
|
||||||
var restrictedPoint = CGPointMake(0, 0);
|
var restrictedPoint = CGPointMake(0, 0);
|
||||||
|
|
||||||
restrictedPoint.x = MIN(MAX(aPoint.x, -_frame.size.width + 4.0), CGRectGetMaxX(visibleFrame) - 4.0);
|
restrictedPoint.x = MIN(MAX(aPoint.x, -_frame.size.width + 4.0), CGRectGetMaxX(visibleFrame) - 4.0);
|
||||||
restrictedPoint.y = MIN(MAX(aPoint.y, minPointY), CGRectGetMaxY(visibleFrame) - 8.0);
|
restrictedPoint.y = MIN(MAX(aPoint.y, 0.0), CGRectGetMaxY(visibleFrame) - 8.0);
|
||||||
|
|
||||||
return restrictedPoint;
|
return restrictedPoint;
|
||||||
}
|
}
|
||||||
@@ -176,24 +158,22 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
- (void)trackMoveWithEvent:(CPEvent)anEvent
|
- (void)trackMoveWithEvent:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var type = [anEvent type];
|
var type = [anEvent type];
|
||||||
|
|
||||||
if (type === CPLeftMouseUp)
|
if (type === CPLeftMouseUp)
|
||||||
{
|
{
|
||||||
_cachedScreenFrame = nil;
|
_cachedScreenFrame = nil;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type === CPLeftMouseDown)
|
else if (type === CPLeftMouseDown)
|
||||||
{
|
{
|
||||||
_mouseDraggedPoint = [[self window] convertBaseToGlobal:[anEvent locationInWindow]];
|
_mouseDraggedPoint = [[self window] convertBaseToBridge:[anEvent locationInWindow]];
|
||||||
_cachedScreenFrame = [[CPPlatformWindow primaryPlatformWindow] visibleFrame];
|
_cachedScreenFrame = [[CPDOMWindowBridge sharedDOMWindowBridge] visibleFrame];
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type === CPLeftMouseDragged)
|
else if (type === CPLeftMouseDragged)
|
||||||
{
|
{
|
||||||
var theWindow = [self window],
|
var theWindow = [self window],
|
||||||
frame = [theWindow frame],
|
frame = [theWindow frame],
|
||||||
location = [theWindow convertBaseToGlobal:[anEvent locationInWindow]],
|
location = [theWindow convertBaseToBridge:[anEvent locationInWindow]],
|
||||||
origin = [self _pointWithinScreenFrame:CGPointMake(_CGRectGetMinX(frame) + (location.x - _mouseDraggedPoint.x),
|
origin = [self _pointWithinScreenFrame:CGPointMake(_CGRectGetMinX(frame) + (location.x - _mouseDraggedPoint.x),
|
||||||
_CGRectGetMinY(frame) + (location.y - _mouseDraggedPoint.y))];
|
_CGRectGetMinY(frame) + (location.y - _mouseDraggedPoint.y))];
|
||||||
|
|
||||||
@@ -216,18 +196,18 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
|
|
||||||
[_resizeIndicator setImage:_CPWindowViewResizeIndicatorImage];
|
[_resizeIndicator setImage:_CPWindowViewResizeIndicatorImage];
|
||||||
[_resizeIndicator setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin];
|
[_resizeIndicator setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin];
|
||||||
|
|
||||||
[self addSubview:_resizeIndicator];
|
[self addSubview:_resizeIndicator];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[_resizeIndicator removeFromSuperview];
|
[_resizeIndicator removeFromSuperview];
|
||||||
|
|
||||||
_resizeIndicator = nil;
|
_resizeIndicator = nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)showsResizeIndicator
|
- (CPImage)showsResizeIndicator
|
||||||
{
|
{
|
||||||
return _resizeIndicator !== nil;
|
return _resizeIndicator !== nil;
|
||||||
}
|
}
|
||||||
@@ -268,7 +248,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
|
|
||||||
- (CGSize)toolbarOffset
|
- (CGSize)toolbarOffset
|
||||||
{
|
{
|
||||||
return _toolbarOffset;
|
return CGSizeMakeZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPColor)toolbarLabelColor
|
- (CPColor)toolbarLabelColor
|
||||||
@@ -325,8 +305,11 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
if (toolbarView)
|
if (toolbarView)
|
||||||
{
|
{
|
||||||
[toolbarView removeFromSuperview];
|
[toolbarView removeFromSuperview];
|
||||||
[toolbarView FIXME_setIsHUD:_styleMask & CPHUDBackgroundWindowMask];
|
[toolbarView setLabelColor:[self toolbarLabelColor]];
|
||||||
|
|
||||||
|
if ([self respondsToSelector:@selector(toolbarLabelShadowColor)])
|
||||||
|
[toolbarView setLabelShadowColor:[self toolbarLabelShadowColor]];
|
||||||
|
|
||||||
[self addSubview:toolbarView];
|
[self addSubview:toolbarView];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +322,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
{
|
{
|
||||||
var contentRect = [self convertRect:[[theWindow contentView] frame] toView:nil];
|
var contentRect = [self convertRect:[[theWindow contentView] frame] toView:nil];
|
||||||
|
|
||||||
contentRect.origin = [theWindow convertBaseToGlobal:contentRect.origin];
|
contentRect.origin = [theWindow convertBaseToBridge:contentRect.origin];
|
||||||
|
|
||||||
[self setAutoresizesSubviews:NO];
|
[self setAutoresizesSubviews:NO];
|
||||||
[theWindow setFrame:[theWindow frameRectForContentRect:contentRect]];
|
[theWindow setFrame:[theWindow frameRectForContentRect:contentRect]];
|
||||||
|
|||||||
+42
-208
@@ -27,6 +27,8 @@
|
|||||||
@import "CPWindow.j"
|
@import "CPWindow.j"
|
||||||
@import "CPDocument.j"
|
@import "CPDocument.j"
|
||||||
|
|
||||||
|
#include "Platform/Platform.h"
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -39,24 +41,10 @@
|
|||||||
*/
|
*/
|
||||||
@implementation CPWindowController : CPResponder
|
@implementation CPWindowController : CPResponder
|
||||||
{
|
{
|
||||||
CPWindow _window;
|
id _owner;
|
||||||
|
CPWindow _window;
|
||||||
CPArray _documents;
|
CPDocument _document;
|
||||||
CPDocument _document;
|
CPString _windowCibName;
|
||||||
BOOL _shouldCloseDocument;
|
|
||||||
BOOL _supportsMultipleDocuments;
|
|
||||||
|
|
||||||
id _cibOwner;
|
|
||||||
CPString _windowCibName;
|
|
||||||
CPString _windowCibPath;
|
|
||||||
|
|
||||||
CPViewController _viewController;
|
|
||||||
CPView _viewControllerContainerView;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
return [self initWithWindow:nil];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -67,17 +55,14 @@
|
|||||||
- (id)initWithWindow:(CPWindow)aWindow
|
- (id)initWithWindow:(CPWindow)aWindow
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
[self setWindow:aWindow];
|
[self setWindow:aWindow];
|
||||||
[self setShouldCloseDocument:NO];
|
|
||||||
|
|
||||||
[self setNextResponder:CPApp];
|
[self setNextResponder:CPApp];
|
||||||
|
|
||||||
_documents = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,27 +84,16 @@
|
|||||||
*/
|
*/
|
||||||
- (id)initWithWindowCibName:(CPString)aWindowCibName owner:(id)anOwner
|
- (id)initWithWindowCibName:(CPString)aWindowCibName owner:(id)anOwner
|
||||||
{
|
{
|
||||||
self = [self initWithWindow:nil];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_cibOwner = anOwner;
|
_owner = anOwner;
|
||||||
_windowCibName = aWindowCibName;
|
_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;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,33 +102,34 @@
|
|||||||
*/
|
*/
|
||||||
- (void)loadWindow
|
- (void)loadWindow
|
||||||
{
|
{
|
||||||
if (_window)
|
[self windowWillLoad];
|
||||||
return;
|
//FIXME: ACTUALLY LOAD WINDOW!!!
|
||||||
|
[self setWindow:CPApp._keyWindow = [[CPWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:CPBorderlessBridgeWindowMask|CPTitledWindowMask|CPClosableWindowMask|CPResizableWindowMask]];
|
||||||
[[CPBundle mainBundle] loadCibFile:[self windowCibPath] externalNameTable:[CPDictionary dictionaryWithObject:_cibOwner forKey:CPCibOwner]];
|
|
||||||
|
[self windowDidLoad];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Shows the window.
|
Shows the window.
|
||||||
@param aSender the object requesting the show
|
@param aSender the object requesting the show
|
||||||
*/
|
*/
|
||||||
- (@action)showWindow:(id)aSender
|
- (CFAction)showWindow:(id)aSender
|
||||||
{
|
{
|
||||||
var theWindow = [self window];
|
var theWindow = [self window];
|
||||||
|
|
||||||
if ([theWindow respondsToSelector:@selector(becomesKeyOnlyIfNeeded)] && [theWindow becomesKeyOnlyIfNeeded])
|
if ([theWindow respondsToSelector:@selector(becomesKeyOnlyIfNeeded)] && [theWindow becomesKeyOnlyIfNeeded])
|
||||||
[theWindow orderFront:aSender];
|
[theWindow orderFront:aSender];
|
||||||
else
|
else
|
||||||
[theWindow makeKeyAndOrderFront:aSender];
|
[theWindow makeKeyAndOrderFront:aSender];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the window has been loaded. Specifically,
|
Returns <code>YES</code> if the window has been loaded. Specifically,
|
||||||
if loadWindow has been called.
|
if loadWindow has been called.
|
||||||
*/
|
*/
|
||||||
- (BOOL)isWindowLoaded
|
- (BOOL)isWindowLoaded
|
||||||
{
|
{
|
||||||
return _window !== nil;
|
return _window;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -163,28 +138,7 @@
|
|||||||
- (CPWindow)window
|
- (CPWindow)window
|
||||||
{
|
{
|
||||||
if (!_window)
|
if (!_window)
|
||||||
{
|
[self loadWindow];
|
||||||
[self windowWillLoad];
|
|
||||||
[_document windowControllerWillLoadCib:self];
|
|
||||||
|
|
||||||
[self loadWindow];
|
|
||||||
|
|
||||||
if (_window === nil && [_cibOwner isKindOfClass:[CPDocument class]])
|
|
||||||
[self setWindow:[_cibOwner valueForKey:@"window"]];
|
|
||||||
|
|
||||||
if (!_window)
|
|
||||||
{
|
|
||||||
var reason = [CPString stringWithFormat:@"Window for %@ could not be loaded from Cib or no window specified. \
|
|
||||||
Override loadWindow to load the window manually.", self];
|
|
||||||
|
|
||||||
[CPException raise:CPInternalInconsistencyException reason:reason];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self windowDidLoad];
|
|
||||||
[_document windowControllerDidLoadCib:self];
|
|
||||||
|
|
||||||
[self synchronizeWindowTitleWithDocumentName];
|
|
||||||
}
|
|
||||||
|
|
||||||
return _window;
|
return _window;
|
||||||
}
|
}
|
||||||
@@ -195,10 +149,8 @@
|
|||||||
*/
|
*/
|
||||||
- (void)setWindow:(CPWindow)aWindow
|
- (void)setWindow:(CPWindow)aWindow
|
||||||
{
|
{
|
||||||
[_window setWindowController:nil];
|
|
||||||
|
|
||||||
_window = aWindow;
|
_window = aWindow;
|
||||||
|
|
||||||
[_window setWindowController:self];
|
[_window setWindowController:self];
|
||||||
[_window setNextResponder:self];
|
[_window setNextResponder:self];
|
||||||
}
|
}
|
||||||
@@ -208,6 +160,9 @@
|
|||||||
*/
|
*/
|
||||||
- (void)windowDidLoad
|
- (void)windowDidLoad
|
||||||
{
|
{
|
||||||
|
[_document windowControllerDidLoadNib:self];
|
||||||
|
|
||||||
|
[self synchronizeWindowTitleWithDocumentName];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -215,6 +170,7 @@
|
|||||||
*/
|
*/
|
||||||
- (void)windowWillLoad
|
- (void)windowWillLoad
|
||||||
{
|
{
|
||||||
|
[_document windowControllerWillLoadNib:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -223,20 +179,17 @@
|
|||||||
*/
|
*/
|
||||||
- (void)setDocument:(CPDocument)aDocument
|
- (void)setDocument:(CPDocument)aDocument
|
||||||
{
|
{
|
||||||
if (_document === aDocument)
|
if (_document == aDocument)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||||
|
|
||||||
if (_document)
|
if (_document)
|
||||||
{
|
{
|
||||||
if (![self supportsMultipleDocuments])
|
|
||||||
[self removeDocument:_document];
|
|
||||||
|
|
||||||
[defaultCenter removeObserver:self
|
[defaultCenter removeObserver:self
|
||||||
name:CPDocumentWillSaveNotification
|
name:CPDocumentWillSaveNotification
|
||||||
object:_document];
|
object:_document];
|
||||||
|
|
||||||
[defaultCenter removeObserver:self
|
[defaultCenter removeObserver:self
|
||||||
name:CPDocumentDidSaveNotification
|
name:CPDocumentDidSaveNotification
|
||||||
object:_document];
|
object:_document];
|
||||||
@@ -245,18 +198,16 @@
|
|||||||
name:CPDocumentDidFailToSaveNotification
|
name:CPDocumentDidFailToSaveNotification
|
||||||
object:_document];
|
object:_document];
|
||||||
}
|
}
|
||||||
|
|
||||||
_document = aDocument;
|
_document = aDocument;
|
||||||
|
|
||||||
if (_document)
|
if (_document)
|
||||||
{
|
{
|
||||||
[self addDocument:_document];
|
|
||||||
|
|
||||||
[defaultCenter addObserver:self
|
[defaultCenter addObserver:self
|
||||||
selector:@selector(_documentWillSave:)
|
selector:@selector(_documentWillSave:)
|
||||||
name:CPDocumentWillSaveNotification
|
name:CPDocumentWillSaveNotification
|
||||||
object:_document];
|
object:_document];
|
||||||
|
|
||||||
[defaultCenter addObserver:self
|
[defaultCenter addObserver:self
|
||||||
selector:@selector(_documentDidSave:)
|
selector:@selector(_documentDidSave:)
|
||||||
name:CPDocumentDidSaveNotification
|
name:CPDocumentDidSaveNotification
|
||||||
@@ -266,94 +217,13 @@
|
|||||||
selector:@selector(_documentDidFailToSave:)
|
selector:@selector(_documentDidFailToSave:)
|
||||||
name:CPDocumentDidFailToSaveNotification
|
name:CPDocumentDidFailToSaveNotification
|
||||||
object:_document];
|
object:_document];
|
||||||
|
|
||||||
[self setDocumentEdited:[_document isDocumentEdited]];
|
[self setDocumentEdited:[_document isDocumentEdited]];
|
||||||
}
|
}
|
||||||
|
|
||||||
var viewController = [_document viewControllerForWindowController:self];
|
|
||||||
|
|
||||||
if (viewController)
|
|
||||||
[self setViewController:viewController];
|
|
||||||
|
|
||||||
[self synchronizeWindowTitleWithDocumentName];
|
[self synchronizeWindowTitleWithDocumentName];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSupportsMultipleDocuments:(BOOL)shouldSupportMultipleDocuments
|
|
||||||
{
|
|
||||||
_supportsMultipleDocuments = shouldSupportMultipleDocuments;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)supportsMultipleDocuments
|
|
||||||
{
|
|
||||||
return _supportsMultipleDocuments;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addDocument:(CPDocument)aDocument
|
|
||||||
{
|
|
||||||
if (aDocument && ![_documents containsObject:aDocument])
|
|
||||||
[_documents addObject:aDocument];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeDocument:(CPDocument)aDocument
|
|
||||||
{
|
|
||||||
var index = [_documents indexOfObjectIdenticalTo:aDocument];
|
|
||||||
|
|
||||||
if (index === CPNotFound)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[_documents removeObjectAtIndex:index];
|
|
||||||
|
|
||||||
if (_document === aDocument && [_documents count])
|
|
||||||
[self setDocument:[_documents objectAtIndex:MIN(index, [_documents count] - 1)]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeDocumentAndCloseIfNecessary:(CPDocument)aDocument
|
|
||||||
{
|
|
||||||
[self removeDocument:aDocument];
|
|
||||||
|
|
||||||
if (![_documents count])
|
|
||||||
[self close];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)documents
|
|
||||||
{
|
|
||||||
return _documents;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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 */
|
/* @ignore */
|
||||||
- (void)_documentWillSave:(CPNotification)aNotification
|
- (void)_documentWillSave:(CPNotification)aNotification
|
||||||
{
|
{
|
||||||
@@ -382,49 +252,13 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets whether the document has unsaved changes. The window can use this as a hint to
|
Sets whether the document has unsaved changes. The window can use this as a hint to
|
||||||
@param isEdited \c YES means the document has unsaved changes.
|
@param isEdited <code>YES</code> means the document has unsaved changes.
|
||||||
*/
|
*/
|
||||||
- (void)setDocumentEdited:(BOOL)isEdited
|
- (void)setDocumentEdited:(BOOL)isEdited
|
||||||
{
|
{
|
||||||
[[self window] setDocumentEdited: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 mainBundle] pathForResource:_windowCibName + @".cib"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting and Getting Window Attributes
|
// Setting and Getting Window Attributes
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -434,7 +268,7 @@
|
|||||||
{
|
{
|
||||||
if (!_document || !_window)
|
if (!_document || !_window)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// [_window setRepresentedFilename:];
|
// [_window setRepresentedFilename:];
|
||||||
[_window setTitle:[self windowTitleForDocumentDisplayName:[_document displayName]]];
|
[_window setTitle:[self windowTitleForDocumentDisplayName:[_document displayName]]];
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-29
@@ -30,14 +30,12 @@
|
|||||||
@import "_CPCibCustomView.j"
|
@import "_CPCibCustomView.j"
|
||||||
@import "_CPCibKeyedUnarchiver.j"
|
@import "_CPCibKeyedUnarchiver.j"
|
||||||
@import "_CPCibObjectData.j"
|
@import "_CPCibObjectData.j"
|
||||||
@import "_CPCibProxyObject.j"
|
|
||||||
@import "_CPCibWindowTemplate.j"
|
@import "_CPCibWindowTemplate.j"
|
||||||
|
|
||||||
|
|
||||||
CPCibOwner = @"CPCibOwner",
|
CPCibOwner = @"CPCibOwner",
|
||||||
CPCibTopLevelObjects = @"CPCibTopLevelObjects",
|
CPCibTopLevelObjects = @"CPCibTopLevelObjects",
|
||||||
CPCibReplacementClasses = @"CPCibReplacementClasses",
|
CPCibReplacementClasses = @"CPCibReplacementClasses";
|
||||||
CPCibExternalObjects = @"CPCibExternalObjects";
|
|
||||||
|
|
||||||
var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||||
|
|
||||||
@@ -60,7 +58,7 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil];
|
_data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil error:nil];
|
||||||
_awakenCustomResources = YES;
|
_awakenCustomResources = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,25 +81,11 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (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
|
- (id)initWithCibNamed:(CPString)aName bundle:(CPBundle)aBundle loadDelegate:(id)aLoadDelegate
|
||||||
{
|
{
|
||||||
if (![aName hasSuffix:@".cib"])
|
if (![aName hasSuffix:@".cib"])
|
||||||
aName = [aName stringByAppendingString:@".cib"];
|
aName = [aName stringByAppendingString:@".cib"];
|
||||||
|
|
||||||
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
|
// 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];
|
self = [self initWithContentsOfURL:[aBundle || [CPBundle mainBundle] pathForResource:aName] loadDelegate:aLoadDelegate];
|
||||||
|
|
||||||
@@ -141,8 +125,6 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
|||||||
[unarchiver setClass:[replacementClasses objectForKey:key] forClassName:key];
|
[unarchiver setClass:[replacementClasses objectForKey:key] forClassName:key];
|
||||||
}
|
}
|
||||||
|
|
||||||
[unarchiver setExternalObjectsForProxyIdentifiers:[anExternalNameTable objectForKey:CPCibExternalObjects]];
|
|
||||||
|
|
||||||
var objectData = [unarchiver decodeObjectForKey:CPCibObjectDataKey];
|
var objectData = [unarchiver decodeObjectForKey:CPCibObjectDataKey];
|
||||||
|
|
||||||
if (!objectData || ![objectData isKindOfClass:[_CPCibObjectData class]])
|
if (!objectData || ![objectData isKindOfClass:[_CPCibObjectData class]])
|
||||||
@@ -150,10 +132,18 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
|||||||
|
|
||||||
var topLevelObjects = [anExternalNameTable objectForKey:CPCibTopLevelObjects];
|
var topLevelObjects = [anExternalNameTable objectForKey:CPCibTopLevelObjects];
|
||||||
|
|
||||||
[objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects];
|
[objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects]
|
||||||
[objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects];
|
[objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects];
|
||||||
[objectData awakeWithOwner:owner topLevelObjects:topLevelObjects];
|
[objectData awakeWithOwner:owner topLevelObjects:topLevelObjects];
|
||||||
|
|
||||||
|
var menu;
|
||||||
|
|
||||||
|
if ((menu = [objectData mainMenu]) != nil)
|
||||||
|
{
|
||||||
|
[CPApp setMainMenu:menu];
|
||||||
|
[CPMenu setMenuBarVisible:YES];
|
||||||
|
}
|
||||||
|
|
||||||
// Display Visible Windows.
|
// Display Visible Windows.
|
||||||
[objectData displayVisibleWindows];
|
[objectData displayVisibleWindows];
|
||||||
|
|
||||||
@@ -171,17 +161,12 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
|||||||
|
|
||||||
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)data
|
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)data
|
||||||
{
|
{
|
||||||
// FIXME: Why aren't we getting connection:didFailWithError:
|
_data = [CPData dataWithString:data];
|
||||||
if (!data)
|
|
||||||
return [self connection:aConnection didFailWithError:nil];
|
|
||||||
|
|
||||||
_data = [CPData dataWithRawString:data];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
||||||
{
|
{
|
||||||
if ([_loadDelegate respondsToSelector:@selector(cibDidFailToLoad:)])
|
alert("cib: connection failed.");
|
||||||
[_loadDelegate cibDidFailToLoad:self];
|
|
||||||
|
|
||||||
_loadDelegate = nil;
|
_loadDelegate = nil;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.charAt(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
|
|
||||||
|
|
||||||
+24
-74
@@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
var CPCibOwner = @"CPCibOwner";
|
var CPCibOwner = @"CPCibOwner";
|
||||||
|
|
||||||
|
var LoadInfoForCib = {};
|
||||||
|
|
||||||
@implementation CPObject (CPCibLoading)
|
@implementation CPObject (CPCibLoading)
|
||||||
|
|
||||||
- (void)awakeFromCib
|
- (void)awakeFromCib
|
||||||
@@ -37,90 +39,38 @@ var CPCibOwner = @"CPCibOwner";
|
|||||||
|
|
||||||
@implementation CPBundle (CPCibLoading)
|
@implementation CPBundle (CPCibLoading)
|
||||||
|
|
||||||
+ (CPCib)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable
|
+ (void)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable loadDelegate:aDelegate
|
||||||
{
|
{
|
||||||
return [[[CPCib alloc] initWithContentsOfURL:anAbsolutePath] instantiateCibWithExternalNameTable:aNameTable];
|
var cib = [[CPCib alloc] initWithContentsOfURL:anAbsolutePath loadDelegate:self];
|
||||||
|
|
||||||
|
LoadInfoForCib[[cib hash]] = { loadDelegate:aDelegate, externalNameTable:aNameTable };
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner
|
+ (void)loadCibNamed:(CPString)aName owner:(id)anOwner loadDelegate:(id)aDelegate
|
||||||
{
|
{
|
||||||
if (![aName hasSuffix:@".cib"])
|
// Path is based solely on anOwner:
|
||||||
aName = [aName stringByAppendingString:@".cib"];
|
|
||||||
|
|
||||||
// Path is based solely on anOwner:
|
|
||||||
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
|
|
||||||
path = [bundle pathForResource:aName];
|
|
||||||
|
|
||||||
return [self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable
|
|
||||||
{
|
|
||||||
return [[[CPCib alloc] initWithContentsOfURL:aFileName] instantiateCibWithExternalNameTable:aNameTable];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCib)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable loadDelegate:aDelegate
|
|
||||||
{
|
|
||||||
return ([[CPCib alloc]
|
|
||||||
initWithContentsOfURL:anAbsolutePath
|
|
||||||
loadDelegate:[[_CPCibLoadDelegate alloc]
|
|
||||||
initWithLoadDelegate:aDelegate
|
|
||||||
externalNameTable:aNameTable]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner loadDelegate:(id)aDelegate
|
|
||||||
{
|
|
||||||
if (![aName hasSuffix:@".cib"])
|
|
||||||
aName = [aName stringByAppendingString:@".cib"];
|
|
||||||
|
|
||||||
// Path is based solely on anOwner:
|
|
||||||
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
|
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
|
||||||
path = [bundle pathForResource:aName];
|
path = [bundle pathForResource:aName];
|
||||||
|
|
||||||
return [self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner] loadDelegate:aDelegate];
|
[self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner] loadDelegate:aDelegate];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable loadDelegate:(id)aDelegate
|
- (void)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable loadDelegate:(id)aDelegate
|
||||||
{
|
{
|
||||||
return ([[CPCib alloc]
|
var cib = [[CPCib alloc] initWithCibNamed:aFileName bundle:self loadDelegate:[self class]];
|
||||||
initWithCibNamed:aFileName
|
|
||||||
bundle:self
|
LoadInfoForCib[[cib hash]] = { loadDelegate:aDelegate, externalNameTable:aNameTable };
|
||||||
loadDelegate:[[_CPCibLoadDelegate alloc]
|
}
|
||||||
initWithLoadDelegate:aDelegate
|
|
||||||
externalNameTable:aNameTable]]);
|
+ (void)cibDidFinishLoading:(CPCib)aCib
|
||||||
}
|
{
|
||||||
|
var loadInfo = LoadInfoForCib[[aCib hash]];
|
||||||
@end
|
|
||||||
|
delete LoadInfoForCib[[aCib hash]];
|
||||||
@implementation _CPCibLoadDelegate : CPObject
|
|
||||||
{
|
[aCib instantiateCibWithExternalNameTable:loadInfo.externalNameTable];
|
||||||
id _loadDelegate;
|
|
||||||
CPDictionary _externalNameTable;
|
[loadInfo.loadDelegate cibDidFinishLoading:aCib];
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithLoadDelegate:(id)aLoadDelegate externalNameTable:(id)anExternalNameTable
|
|
||||||
{
|
|
||||||
self = [self init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_loadDelegate = aLoadDelegate;
|
|
||||||
_externalNameTable = anExternalNameTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)cibDidFinishLoading:(CPCib)aCib
|
|
||||||
{
|
|
||||||
[aCib instantiateCibWithExternalNameTable:_externalNameTable];
|
|
||||||
|
|
||||||
[_loadDelegate cibDidFinishLoading:aCib];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)cibDidFailToLoad:(CPCib)aCib
|
|
||||||
{
|
|
||||||
[_loadDelegate cibDidFailToLoad:aCib];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user