mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-10 20:57:11 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80097ea2a2 | ||
|
|
ea7257c00d | ||
|
|
2f0de57c92 | ||
|
|
bb659e3c06 | ||
|
|
3c98ed78ee | ||
|
|
458481af4e |
@@ -5,12 +5,3 @@ Demos
|
|||||||
./Aristo
|
./Aristo
|
||||||
WebSite
|
WebSite
|
||||||
.push-package
|
.push-package
|
||||||
*.xcodeproj/*.pbxuser
|
|
||||||
*.xcodeproj/*.perspectivev3
|
|
||||||
xcuserdata/
|
|
||||||
!*.xcodeproj/project.pbxproj
|
|
||||||
*.xCodeSupport/
|
|
||||||
*.XcodeSupport/
|
|
||||||
*XcodeSupport/
|
|
||||||
*.sublime-project
|
|
||||||
*.sublime-workspace
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- 0.8
|
|
||||||
install: ./bootstrap.sh --quiet --noprompt --directory ./narwhal
|
|
||||||
script: jake test
|
|
||||||
env: PATH="$TRAVIS_BUILD_DIR/narwhal/bin:$PATH" CAPP_BUILD="$TRAVIS_BUILD_DIR/Build" NARWHAL_ENGINE=rhino
|
|
||||||
+3
-22
@@ -20,14 +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 "_CPToolTip.j"
|
|
||||||
@import "CALayer.j"
|
@import "CALayer.j"
|
||||||
@import "CGGeometry.j"
|
|
||||||
@import "CPAccordionView.j"
|
|
||||||
@import "CPAlert.j"
|
@import "CPAlert.j"
|
||||||
@import "CPAnimation.j"
|
@import "CPAnimation.j"
|
||||||
@import "CPApplication.j"
|
@import "CPApplication.j"
|
||||||
@import "CPArrayController.j"
|
|
||||||
@import "CPBezierPath.j"
|
@import "CPBezierPath.j"
|
||||||
@import "CPBox.j"
|
@import "CPBox.j"
|
||||||
@import "CPBrowser.j"
|
@import "CPBrowser.j"
|
||||||
@@ -35,27 +31,20 @@
|
|||||||
@import "CPButtonBar.j"
|
@import "CPButtonBar.j"
|
||||||
@import "CPCheckBox.j"
|
@import "CPCheckBox.j"
|
||||||
@import "CPCib.j"
|
@import "CPCib.j"
|
||||||
@import "CPCibBindingConnector.j"
|
|
||||||
@import "CPCibConnector.j"
|
@import "CPCibConnector.j"
|
||||||
@import "CPCibControlConnector.j"
|
@import "CPCibControlConnector.j"
|
||||||
@import "CPCibHelpConnector.j"
|
|
||||||
@import "CPCibLoading.j"
|
@import "CPCibLoading.j"
|
||||||
@import "CPCibOutletConnector.j"
|
@import "CPCibOutletConnector.j"
|
||||||
@import "CPCibRuntimeAttributesConnector.j"
|
|
||||||
@import "CPClipView.j"
|
@import "CPClipView.j"
|
||||||
@import "CPCollectionView.j"
|
@import "CPCollectionView.j"
|
||||||
@import "CPCollectionViewItem.j"
|
@import "CPCollectionViewItem.j"
|
||||||
@import "CPColor.j"
|
@import "CPColor.j"
|
||||||
@import "CPColorPanel.j"
|
@import "CPColorPanel.j"
|
||||||
@import "CPColorSpace.j"
|
|
||||||
@import "CPColorWell.j"
|
@import "CPColorWell.j"
|
||||||
@import "CPComboBox.j"
|
|
||||||
@import "CPCompatibility.j"
|
@import "CPCompatibility.j"
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPController.j"
|
|
||||||
@import "CPCookie.j"
|
@import "CPCookie.j"
|
||||||
@import "CPCursor.j"
|
@import "CPCursor.j"
|
||||||
@import "CPDatePicker.j"
|
|
||||||
@import "CPDocument.j"
|
@import "CPDocument.j"
|
||||||
@import "CPDocumentController.j"
|
@import "CPDocumentController.j"
|
||||||
@import "CPEvent.j"
|
@import "CPEvent.j"
|
||||||
@@ -63,26 +52,21 @@
|
|||||||
@import "CPFlashView.j"
|
@import "CPFlashView.j"
|
||||||
@import "CPFont.j"
|
@import "CPFont.j"
|
||||||
@import "CPFontManager.j"
|
@import "CPFontManager.j"
|
||||||
@import "CPGradient.j"
|
@import "CPGeometry.j"
|
||||||
@import "CPGraphics.j"
|
@import "CPGraphics.j"
|
||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
@import "CPImageView.j"
|
@import "CPImageView.j"
|
||||||
@import "CPKeyBinding.j"
|
@import "CPKeyBinding.j"
|
||||||
@import "CPLevelIndicator.j"
|
|
||||||
@import "CPMenu.j"
|
@import "CPMenu.j"
|
||||||
@import "CPMenuItem.j"
|
@import "CPMenuItem.j"
|
||||||
@import "CPOpenPanel.j"
|
@import "CPOpenPanel.j"
|
||||||
@import "CPOutlineView.j"
|
@import "CPOutlineView.j"
|
||||||
@import "CPPanel.j"
|
@import "CPPanel.j"
|
||||||
@import "CPPasteboard.j"
|
@import "CPPasteboard.j"
|
||||||
@import "CPPopover.j"
|
|
||||||
@import "CPPopUpButton.j"
|
@import "CPPopUpButton.j"
|
||||||
@import "CPPredicateEditor.j"
|
|
||||||
@import "CPPredicateEditorRowTemplate.j"
|
|
||||||
@import "CPProgressIndicator.j"
|
@import "CPProgressIndicator.j"
|
||||||
@import "CPRadio.j"
|
@import "CPRadio.j"
|
||||||
@import "CPResponder.j"
|
@import "CPResponder.j"
|
||||||
@import "CPRuleEditor.j"
|
|
||||||
@import "CPScroller.j"
|
@import "CPScroller.j"
|
||||||
@import "CPScrollView.j"
|
@import "CPScrollView.j"
|
||||||
@import "CPSearchField.j"
|
@import "CPSearchField.j"
|
||||||
@@ -90,23 +74,20 @@
|
|||||||
@import "CPSegmentedControl.j"
|
@import "CPSegmentedControl.j"
|
||||||
@import "CPShadow.j"
|
@import "CPShadow.j"
|
||||||
@import "CPSlider.j"
|
@import "CPSlider.j"
|
||||||
@import "CPSound.j"
|
|
||||||
@import "CPSplitView.j"
|
@import "CPSplitView.j"
|
||||||
@import "CPStepper.j"
|
|
||||||
@import "CPTableColumn.j"
|
@import "CPTableColumn.j"
|
||||||
@import "CPTableView.j"
|
@import "CPTableView.j"
|
||||||
@import "CPTabView.j"
|
@import "CPTabView.j"
|
||||||
@import "CPText.j"
|
@import "CPText.j"
|
||||||
@import "CPTextField.j"
|
@import "CPTextField.j"
|
||||||
@import "CPTokenField.j"
|
|
||||||
@import "CPToolbar.j"
|
@import "CPToolbar.j"
|
||||||
@import "CPToolbarItem.j"
|
@import "CPToolbarItem.j"
|
||||||
|
@import "CPTokenField.j"
|
||||||
@import "CPTreeNode.j"
|
@import "CPTreeNode.j"
|
||||||
@import "CPUserDefaultsController.j"
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
@import "CPViewAnimation.j"
|
@import "CPViewAnimation.j"
|
||||||
@import "CPViewController.j"
|
@import "CPViewController.j"
|
||||||
@import "CPWebView.j"
|
@import "CPWebView.j"
|
||||||
@import "CPWindow.j"
|
@import "CPWindow.j"
|
||||||
@import "CPWindowController.j"
|
@import "CPWindowController.j"
|
||||||
@import "CPWorkspace.j"
|
@import "CPArrayController.j"
|
||||||
|
|||||||
+16
-16
@@ -69,7 +69,7 @@
|
|||||||
@class CPAccordionView
|
@class CPAccordionView
|
||||||
|
|
||||||
<p>CPAccordionView provides a container for CPAccordionViewItem objects and manages layout state
|
<p>CPAccordionView provides a container for CPAccordionViewItem objects and manages layout state
|
||||||
for all sub-layout items.</p>
|
for all sublayout items.</p>
|
||||||
|
|
||||||
<strong>Example</strong><br />
|
<strong>Example</strong><br />
|
||||||
<pre>
|
<pre>
|
||||||
@@ -106,7 +106,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
|
|||||||
_itemViews = [];
|
_itemViews = [];
|
||||||
_expandedItemIndexes = [CPIndexSet indexSet];
|
_expandedItemIndexes = [CPIndexSet indexSet];
|
||||||
|
|
||||||
[self setItemHeaderPrototype:[[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 24.0)]];
|
[self setItemHeaderPrototype:[[CPButton alloc] initWithFrame:_CGRectMake(0.0, 0.0, 100.0, 24.0)]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -227,7 +227,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
|
|||||||
{
|
{
|
||||||
var indexSet = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, _items.length)];
|
var indexSet = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, _items.length)];
|
||||||
|
|
||||||
[indexSet removeIndexes:_expandedItemIndexes];
|
[indexSet removeIndexes:_expandedIndexes];
|
||||||
|
|
||||||
return indexSet;
|
return indexSet;
|
||||||
}
|
}
|
||||||
@@ -258,18 +258,18 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
|
|||||||
|
|
||||||
- (void)setFrameSize:(CGSize)aSize
|
- (void)setFrameSize:(CGSize)aSize
|
||||||
{
|
{
|
||||||
var width = CGRectGetWidth([self frame]);
|
var width = _CGRectGetWidth([self frame]);
|
||||||
|
|
||||||
[super setFrameSize:aSize];
|
[super setFrameSize:aSize];
|
||||||
|
|
||||||
if (width !== CGRectGetWidth([self frame]))
|
if (width !== _CGRectGetWidth([self frame]))
|
||||||
[self _invalidateItemsStartingAtIndex:0];
|
[self _invalidateItemsStartingAtIndex:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
{
|
{
|
||||||
if (_items.length <= 0)
|
if (_items.length <= 0)
|
||||||
return [self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), 0.0)];
|
return [self setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), 0.0)];
|
||||||
|
|
||||||
if (_dirtyItemIndex === CPNotFound)
|
if (_dirtyItemIndex === CPNotFound)
|
||||||
return;
|
return;
|
||||||
@@ -278,7 +278,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
|
|||||||
|
|
||||||
var index = _dirtyItemIndex,
|
var index = _dirtyItemIndex,
|
||||||
count = _itemViews.length,
|
count = _itemViews.length,
|
||||||
width = CGRectGetWidth([self bounds]),
|
width = _CGRectGetWidth([self bounds]),
|
||||||
y = index > 0 ? CGRectGetMaxY([_itemViews[index - 1] frame]) : 0.0;
|
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.
|
// 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.
|
||||||
@@ -293,7 +293,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
|
|||||||
y = CGRectGetMaxY([itemView frame]);
|
y = CGRectGetMaxY([itemView frame]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), y)];
|
[self setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), y)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -310,7 +310,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
|
|||||||
|
|
||||||
- (id)initWithAccordionView:(CPAccordionView)anAccordionView
|
- (id)initWithAccordionView:(CPAccordionView)anAccordionView
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:CGRectMakeZero()];
|
self = [super initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
@@ -376,21 +376,21 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
|
|||||||
|
|
||||||
- (void)setFrameY:(float)aY width:(float)aWidth
|
- (void)setFrameY:(float)aY width:(float)aWidth
|
||||||
{
|
{
|
||||||
var headerHeight = CGRectGetHeight([_headerView frame]);
|
var headerHeight = _CGRectGetHeight([_headerView frame]);
|
||||||
|
|
||||||
// Size to fit or something?
|
// Size to fit or something?
|
||||||
[_headerView setFrameSize:CGSizeMake(aWidth, headerHeight)];
|
[_headerView setFrameSize:_CGSizeMake(aWidth, headerHeight)];
|
||||||
[_contentView setFrameOrigin:CGPointMake(0.0, headerHeight)];
|
[_contentView setFrameOrigin:_CGPointMake(0.0, headerHeight)];
|
||||||
|
|
||||||
if ([self isCollapsed])
|
if ([self isCollapsed])
|
||||||
[self setFrame:CGRectMake(0.0, aY, aWidth, headerHeight)];
|
[self setFrame:_CGRectMake(0.0, aY, aWidth, headerHeight)];
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var contentHeight = CGRectGetHeight([_contentView frame]);
|
var contentHeight = _CGRectGetHeight([_contentView frame]);
|
||||||
|
|
||||||
[_contentView setFrameSize:CGSizeMake(aWidth, contentHeight)];
|
[_contentView setFrameSize:_CGSizeMake(aWidth, contentHeight)];
|
||||||
[self setFrame:CGRectMake(0.0, aY, aWidth, contentHeight + headerHeight)];
|
[self setFrame:_CGRectMake(0.0, aY, aWidth, contentHeight + headerHeight)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+250
-537
@@ -6,9 +6,7 @@
|
|||||||
* Copyright 2008, Jake MacMullin.
|
* Copyright 2008, Jake MacMullin.
|
||||||
*
|
*
|
||||||
* 11/10/2008 Ross Boucher
|
* 11/10/2008 Ross Boucher
|
||||||
* - Make it conform to style guidelines, general cleanup and enhancements
|
* - Make it conform to style guidelines, general cleanup and ehancements
|
||||||
* 11/10/2010 Antoine Mercadal
|
|
||||||
* - Enhancements, better compliance with Cocoa API
|
|
||||||
*
|
*
|
||||||
* 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
|
||||||
@@ -28,19 +26,15 @@
|
|||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
|
|
||||||
|
@import "CPApplication.j"
|
||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
@import "CPColor.j"
|
@import "CPColor.j"
|
||||||
@import "CPFont.j"
|
@import "CPFont.j"
|
||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
@import "CPImageView.j"
|
@import "CPImageView.j"
|
||||||
@import "CPPanel.j"
|
@import "CPPanel.j"
|
||||||
@import "CPText.j"
|
|
||||||
@import "CPTextField.j"
|
@import "CPTextField.j"
|
||||||
|
|
||||||
@class CPCheckBox
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@global
|
@global
|
||||||
@group CPAlertStyle
|
@group CPAlertStyle
|
||||||
@@ -57,8 +51,6 @@ CPInformationalAlertStyle = 1;
|
|||||||
*/
|
*/
|
||||||
CPCriticalAlertStyle = 2;
|
CPCriticalAlertStyle = 2;
|
||||||
|
|
||||||
var bottomHeight = 71;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
|
|
||||||
@@ -81,80 +73,47 @@ var bottomHeight = 71;
|
|||||||
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 : CPObject
|
@implementation CPAlert : CPView
|
||||||
{
|
{
|
||||||
BOOL _showHelp @accessors(property=showsHelp);
|
CPPanel _alertPanel;
|
||||||
BOOL _showSuppressionButton @accessors(property=showsSuppressionButton);
|
|
||||||
|
|
||||||
CPAlertStyle _alertStyle @accessors(property=alertStyle);
|
CPTextField _messageLabel;
|
||||||
CPString _title @accessors(property=title);
|
CPTextField _informativeLabel;
|
||||||
CPView _accessoryView @accessors(property=accessoryView);
|
CPImageView _alertImageView;
|
||||||
CPImage _icon @accessors(property=icon);
|
|
||||||
|
|
||||||
CPArray _buttons @accessors(property=buttons, readonly);
|
CPAlertStyle _alertStyle;
|
||||||
CPCheckBox _suppressionButton @accessors(property=suppressionButton, readonly);
|
CPString _windowTitle;
|
||||||
|
int _windowStyle;
|
||||||
|
CPArray _buttons;
|
||||||
|
|
||||||
id _delegate @accessors(property=delegate);
|
id _delegate;
|
||||||
id _modalDelegate;
|
SEL _didEndSelector;
|
||||||
SEL _didEndSelector;
|
id _modalDelegate;
|
||||||
|
|
||||||
_CPAlertThemeView _themeView @accessors(property=themeView, readonly);
|
|
||||||
CPWindow _window @accessors(property=window, readonly);
|
|
||||||
int _defaultWindowStyle;
|
|
||||||
|
|
||||||
CPImageView _alertImageView;
|
|
||||||
CPTextField _informativeLabel;
|
|
||||||
CPTextField _messageLabel;
|
|
||||||
CPButton _alertHelpButton;
|
|
||||||
|
|
||||||
BOOL _needsLayout;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Creating Alerts
|
+ (CPString)defaultThemeClass
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a CPAlert object with the provided info
|
|
||||||
|
|
||||||
@param aMessage the main body text of the alert
|
|
||||||
@param defaultButton the title of the default button
|
|
||||||
@param alternateButton if not nil, the title of a second button
|
|
||||||
@param otherButton if not nil, the title of the third button
|
|
||||||
@param informativeText if not nil the informative text of the alert
|
|
||||||
@return fully initialized CPAlert
|
|
||||||
*/
|
|
||||||
+ (CPAlert)alertWithMessageText:(CPString)aMessage defaultButton:(CPString)defaultButtonTitle alternateButton:(CPString)alternateButtonTitle otherButton:(CPString)otherButtonTitle informativeTextWithFormat:(CPString)informativeText
|
|
||||||
{
|
{
|
||||||
var newAlert = [[self alloc] init];
|
return @"alert";
|
||||||
|
|
||||||
[newAlert setMessageText:aMessage];
|
|
||||||
[newAlert addButtonWithTitle:defaultButtonTitle];
|
|
||||||
|
|
||||||
if (alternateButtonTitle)
|
|
||||||
[newAlert addButtonWithTitle:alternateButtonTitle];
|
|
||||||
|
|
||||||
if (otherButtonTitle)
|
|
||||||
[newAlert addButtonWithTitle:otherButtonTitle];
|
|
||||||
|
|
||||||
if (informativeText)
|
|
||||||
[newAlert setInformativeText:informativeText];
|
|
||||||
|
|
||||||
return newAlert;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
+ (id)themeAttributes
|
||||||
Return an CPAlert with type error
|
|
||||||
|
|
||||||
@param anErrorMessage the message of the alert
|
|
||||||
@return fully initialized CPAlert
|
|
||||||
*/
|
|
||||||
+ (CPAlert)alertWithError:(CPString)anErrorMessage
|
|
||||||
{
|
{
|
||||||
var newAlert = [[self alloc] init];
|
return [CPDictionary dictionaryWithObjects:[CGSizeMake(400.0, 110.0), CGInsetMake(15, 15, 15, 50), 6, 10,
|
||||||
|
CPJustifiedTextAlignment, [CPColor blackColor], [CPFont boldSystemFontOfSize:13.0], [CPNull null], CGSizeMakeZero(),
|
||||||
[newAlert setMessageText:anErrorMessage];
|
CPJustifiedTextAlignment, [CPColor blackColor], [CPFont systemFontOfSize:12.0], [CPNull null], CGSizeMakeZero(),
|
||||||
[newAlert setAlertStyle:CPCriticalAlertStyle];
|
CGPointMake(15, 12),
|
||||||
|
[CPNull null],
|
||||||
return newAlert;
|
[CPNull null],
|
||||||
|
[CPNull null]
|
||||||
|
]
|
||||||
|
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"
|
||||||
|
]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -162,99 +121,130 @@ var bottomHeight = 71;
|
|||||||
*/
|
*/
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super init];
|
if (self = [super init])
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
{
|
||||||
_buttons = [];
|
_buttons = [CPArray array];
|
||||||
_alertStyle = CPWarningAlertStyle;
|
_alertStyle = CPWarningAlertStyle;
|
||||||
_showHelp = NO;
|
_alertPanel = nil;
|
||||||
_needsLayout = YES;
|
_windowStyle = nil;
|
||||||
_defaultWindowStyle = _CPModalWindowMask;
|
_didEndSelector = nil;
|
||||||
_themeView = [_CPAlertThemeView new];
|
|
||||||
|
|
||||||
_messageLabel = [CPTextField labelWithTitle:@"Alert"];
|
_messageLabel = [CPTextField labelWithTitle:@"Alert"];
|
||||||
_alertImageView = [[CPImageView alloc] init];
|
_alertImageView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
_informativeLabel = [[CPTextField alloc] init];
|
_informativeLabel = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||||
_suppressionButton = [CPCheckBox checkBoxWithTitle:@"Do not show this message again"];
|
|
||||||
|
|
||||||
_alertHelpButton = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 16.0, 16.0)];
|
|
||||||
[_alertHelpButton setTarget:self];
|
|
||||||
[_alertHelpButton setAction:@selector(_showHelp:)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Accessors
|
/*!
|
||||||
|
Sets the window appearance. If CPHUDBackgroundWindowMask is set, the default HUD theme
|
||||||
|
will be activated.
|
||||||
|
|
||||||
- (CPTheme)theme
|
@param styleMask - Either CPHUDBackgroundWindowMask or nil for standard.
|
||||||
|
*/
|
||||||
|
- (void)setWindowStyle:(int)styleMask
|
||||||
{
|
{
|
||||||
return [_themeView theme];
|
_windowStyle = styleMask;
|
||||||
|
|
||||||
|
[self setTheme:(_windowStyle & CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
|
||||||
|
|
||||||
|
// We'll need to recreate the panel to get the new window style.
|
||||||
|
_alertPanel = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_createPanel
|
||||||
|
{
|
||||||
|
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--)
|
||||||
|
[contentView addSubview:_buttons[count]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
[self addButtonWithTitle:@"OK"];
|
||||||
|
|
||||||
|
[contentView addSubview:_messageLabel];
|
||||||
|
[contentView addSubview:_alertImageView];
|
||||||
|
[contentView addSubview:_informativeLabel];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
set the theme to use
|
Sets the window's title. If this is not defined, a default title based on your warning level will be used.
|
||||||
|
@param aTitle the title to use in place of the default. Set to nil to use default.
|
||||||
@param the theme to use
|
|
||||||
*/
|
*/
|
||||||
- (void)setTheme:(CPTheme)aTheme
|
- (void)setTitle:(CPString)aTitle
|
||||||
{
|
{
|
||||||
if (aTheme === [self theme])
|
_windowTitle = aTitle;
|
||||||
return;
|
|
||||||
|
|
||||||
if (aTheme === [CPTheme defaultHudTheme])
|
|
||||||
_defaultWindowStyle = CPTitledWindowMask | CPHUDBackgroundWindowMask;
|
|
||||||
else
|
|
||||||
_defaultWindowStyle = CPTitledWindowMask;
|
|
||||||
|
|
||||||
_window = nil; // will be regenerated at next layout
|
|
||||||
_needsLayout = YES;
|
|
||||||
[_themeView setTheme:aTheme];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
|
/*!
|
||||||
{
|
Gets the window's title.
|
||||||
[_themeView setValue:aValue forThemeAttribute:aName];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(CPThemeState)aState
|
|
||||||
{
|
|
||||||
[_themeView setValue:aValue forThemeAttribute:aName inState:aState];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! @deprecated
|
|
||||||
*/
|
*/
|
||||||
- (void)setWindowStyle:(int)aStyle
|
- (CPString)title
|
||||||
{
|
{
|
||||||
CPLog.warn("DEPRECATED: setWindowStyle: is deprecated. use setTheme: instead");
|
return _windowTitle;
|
||||||
[self setTheme:(aStyle === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! @deprecated
|
/*!
|
||||||
|
Gets the window's style.
|
||||||
*/
|
*/
|
||||||
- (int)windowStyle
|
- (int)windowStyle
|
||||||
{
|
{
|
||||||
CPLog.warn("DEPRECATED: windowStyle: is deprecated. use theme instead");
|
return _windowStyle;
|
||||||
return _defaultWindowStyle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
set the text of the alert's message
|
Sets the receiver’s delegate.
|
||||||
|
@param delegate - Delegate for the alert. nil removes the delegate.
|
||||||
@param aText CPString containing the text
|
|
||||||
*/
|
*/
|
||||||
- (void)setMessageText:(CPString)aText
|
- (void)setDelegate:(id)delegate
|
||||||
{
|
{
|
||||||
[_messageLabel setStringValue:aText];
|
_delegate = delegate;
|
||||||
_needsLayout = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
return the content of the message text
|
Gets the receiver's delegate.
|
||||||
@return CPString containing the message text
|
*/
|
||||||
|
- (void)delegate
|
||||||
|
{
|
||||||
|
return _delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the alert style of the receiver.
|
||||||
|
@param style - Alert style for the alert.
|
||||||
|
*/
|
||||||
|
- (void)setAlertStyle:(CPAlertStyle)style
|
||||||
|
{
|
||||||
|
_alertStyle = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets the alert style.
|
||||||
|
*/
|
||||||
|
- (CPAlertStyle)alertStyle
|
||||||
|
{
|
||||||
|
return _alertStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the receiver’s message text, or title, to a given text.
|
||||||
|
@param messageText - Message text for the alert.
|
||||||
|
*/
|
||||||
|
- (void)setMessageText:(CPString)messageText
|
||||||
|
{
|
||||||
|
[_messageLabel setStringValue:messageText];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the receiver's message text body.
|
||||||
*/
|
*/
|
||||||
- (CPString)messageText
|
- (CPString)messageText
|
||||||
{
|
{
|
||||||
@@ -262,61 +252,22 @@ var bottomHeight = 71;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
set the text of the alert's informative text
|
Sets the receiver's informative text, shown below the message text.
|
||||||
|
@param informativeText - The informative text.
|
||||||
@param aText CPString containing the informative text
|
|
||||||
*/
|
*/
|
||||||
- (void)setInformativeText:(CPString)aText
|
- (void)setInformativeText:(CPString)informativeText
|
||||||
{
|
{
|
||||||
[_informativeLabel setStringValue:aText];
|
[_informativeLabel setStringValue:informativeText];
|
||||||
_needsLayout = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
return the content of the message text
|
Returns the receiver's informative text.
|
||||||
|
|
||||||
@return CPString containing the message text
|
|
||||||
*/
|
*/
|
||||||
- (CPString)informativeText
|
- (CPString)informativeText
|
||||||
{
|
{
|
||||||
return [_informativeLabel stringValue];
|
return [_informativeLabel stringValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the title of the alert window.
|
|
||||||
This API is not present in Cocoa.
|
|
||||||
@param aTitle CPString containing the window title
|
|
||||||
*/
|
|
||||||
- (void)setTitle:(CPString)aTitle
|
|
||||||
{
|
|
||||||
_title = aTitle;
|
|
||||||
[_window setTitle:aTitle];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
set the accessory view
|
|
||||||
|
|
||||||
@param aView the accessory view
|
|
||||||
*/
|
|
||||||
- (void)setAccessoryView:(CPView)aView
|
|
||||||
{
|
|
||||||
_accessoryView = aView;
|
|
||||||
_needsLayout = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
set if alert shows the suppression button
|
|
||||||
|
|
||||||
@param shouldShowSuppressionButton YES or NO
|
|
||||||
*/
|
|
||||||
- (void)setShowsSuppressionButton:(BOOL)shouldShowSuppressionButton
|
|
||||||
{
|
|
||||||
_showSuppressionButton = shouldShowSuppressionButton;
|
|
||||||
_needsLayout = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Accessing Buttons
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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 \c CPAlert panel.
|
||||||
@@ -326,259 +277,125 @@ var bottomHeight = 71;
|
|||||||
and any button titled "Cancel" will be given a key equivalent of Escape.
|
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.
|
||||||
|
|
||||||
@param title the title of the button
|
|
||||||
*/
|
*/
|
||||||
- (void)addButtonWithTitle:(CPString)aTitle
|
- (void)addButtonWithTitle:(CPString)title
|
||||||
{
|
{
|
||||||
var bounds = [[_window contentView] bounds],
|
var bounds = [[_alertPanel contentView] bounds],
|
||||||
count = [_buttons count],
|
button = [[CPButton alloc] initWithFrame:CGRectMakeZero()],
|
||||||
|
_buttonCount = [_buttons count];
|
||||||
|
|
||||||
button = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
[button setTitle:title];
|
||||||
|
|
||||||
[button setTitle:aTitle];
|
|
||||||
[button setTag:count];
|
|
||||||
[button setTarget:self];
|
[button setTarget:self];
|
||||||
[button setAction:@selector(_takeReturnCodeFrom:)];
|
[button setTag:_buttonCount];
|
||||||
|
[button setAction:@selector(_dismissAlert:)];
|
||||||
|
|
||||||
[[_window contentView] addSubview:button];
|
[[_alertPanel contentView] addSubview:button];
|
||||||
|
|
||||||
if (count == 0)
|
if (_buttonCount == 0)
|
||||||
[button setKeyEquivalent:CPCarriageReturnCharacter];
|
[button setKeyEquivalent:CPCarriageReturnCharacter];
|
||||||
else if ([aTitle lowercaseString] === @"cancel")
|
else if ([title lowercaseString] === "cancel")
|
||||||
[button setKeyEquivalent:CPEscapeFunctionKey];
|
[button setKeyEquivalent:CPEscapeFunctionKey];
|
||||||
|
else
|
||||||
|
[button setKeyEquivalent:nil];
|
||||||
|
|
||||||
[_buttons insertObject:button atIndex:0];
|
[_buttons insertObject:button atIndex:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Layout
|
- (void)layoutPanel
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_layoutMessageView
|
|
||||||
{
|
{
|
||||||
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
|
if (!_alertPanel)
|
||||||
sizeWithFontCorrection = 6.0,
|
[self _createPanel];
|
||||||
messageLabelWidth,
|
|
||||||
messageLabelTextSize;
|
|
||||||
|
|
||||||
[_messageLabel setTextColor:[_themeView currentValueForThemeAttribute:@"message-text-color"]];
|
var inset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||||
[_messageLabel setFont:[_themeView currentValueForThemeAttribute:@"message-text-font"]];
|
iconOffset = [self currentValueForThemeAttribute:@"image-offset"],
|
||||||
[_messageLabel setTextShadowColor:[_themeView currentValueForThemeAttribute:@"message-text-shadow-color"]];
|
theTitle,
|
||||||
[_messageLabel setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"message-text-shadow-offset"]];
|
theImage;
|
||||||
[_messageLabel setAlignment:[_themeView currentValueForThemeAttribute:@"message-text-alignment"]];
|
|
||||||
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
|
||||||
|
|
||||||
messageLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right;
|
switch (_alertStyle)
|
||||||
messageLabelTextSize = [[_messageLabel stringValue] sizeWithFont:[_messageLabel font] inWidth:messageLabelWidth];
|
|
||||||
|
|
||||||
[_messageLabel setFrame:CGRectMake(inset.left, inset.top, messageLabelTextSize.width, messageLabelTextSize.height + sizeWithFontCorrection)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_layoutInformativeView
|
|
||||||
{
|
|
||||||
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
|
|
||||||
sizeWithFontCorrection = 6.0,
|
|
||||||
informativeLabelWidth,
|
|
||||||
informativeLabelOriginY,
|
|
||||||
informativeLabelTextSize;
|
|
||||||
|
|
||||||
[_informativeLabel setTextColor:[_themeView currentValueForThemeAttribute:@"informative-text-color"]];
|
|
||||||
[_informativeLabel setFont:[_themeView currentValueForThemeAttribute:@"informative-text-font"]];
|
|
||||||
[_informativeLabel setTextShadowColor:[_themeView currentValueForThemeAttribute:@"informative-text-shadow-color"]];
|
|
||||||
[_informativeLabel setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
|
|
||||||
[_informativeLabel setAlignment:[_themeView currentValueForThemeAttribute:@"informative-text-alignment"]];
|
|
||||||
[_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
|
||||||
|
|
||||||
informativeLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right;
|
|
||||||
informativeLabelOriginY = [_messageLabel frameOrigin].y + [_messageLabel frameSize].height + defaultElementsMargin;
|
|
||||||
informativeLabelTextSize = [[_informativeLabel stringValue] sizeWithFont:[_informativeLabel font] inWidth:informativeLabelWidth];
|
|
||||||
|
|
||||||
[_informativeLabel setFrame:CGRectMake(inset.left, informativeLabelOriginY, informativeLabelTextSize.width, informativeLabelTextSize.height + sizeWithFontCorrection)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_layoutAccessoryView
|
|
||||||
{
|
|
||||||
if (!_accessoryView)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
|
|
||||||
accessoryViewWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right,
|
|
||||||
accessoryViewOriginY = CGRectGetMaxY([_informativeLabel frame]) + defaultElementsMargin;
|
|
||||||
|
|
||||||
[_accessoryView setFrameOrigin:CGPointMake(inset.left, accessoryViewOriginY)];
|
|
||||||
[[_window contentView] addSubview:_accessoryView];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_layoutSuppressionButton
|
|
||||||
{
|
|
||||||
if (!_showSuppressionButton)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
suppressionViewXOffset = [_themeView currentValueForThemeAttribute:@"suppression-button-x-offset"],
|
|
||||||
suppressionViewYOffset = [_themeView currentValueForThemeAttribute:@"suppression-button-y-offset"],
|
|
||||||
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
|
|
||||||
suppressionButtonViewOriginY = CGRectGetMaxY([(_accessoryView || _informativeLabel) frame]) + defaultElementsMargin + suppressionViewYOffset;
|
|
||||||
|
|
||||||
[_suppressionButton setTextColor:[_themeView currentValueForThemeAttribute:@"suppression-button-text-color"]];
|
|
||||||
[_suppressionButton setFont:[_themeView currentValueForThemeAttribute:@"suppression-button-text-font"]];
|
|
||||||
[_suppressionButton setTextShadowColor:[_themeView currentValueForThemeAttribute:@"suppression-button-text-shadow-color"]];
|
|
||||||
[_suppressionButton setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"suppression-button-text-shadow-offset"]];
|
|
||||||
[_suppressionButton sizeToFit];
|
|
||||||
|
|
||||||
[_suppressionButton setFrameOrigin:CGPointMake(inset.left + suppressionViewXOffset, suppressionButtonViewOriginY)];
|
|
||||||
[[_window contentView] addSubview:_suppressionButton];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (CGSize)_layoutButtonsFromView:(CPView)lastView
|
|
||||||
{
|
|
||||||
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
minimumSize = [_themeView currentValueForThemeAttribute:@"size"],
|
|
||||||
buttonOffset = [_themeView currentValueForThemeAttribute:@"button-offset"],
|
|
||||||
helpLeftOffset = [_themeView currentValueForThemeAttribute:@"help-image-left-offset"],
|
|
||||||
aRepresentativeButton = [_buttons objectAtIndex:0],
|
|
||||||
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
|
|
||||||
panelSize = [[_window contentView] frame].size,
|
|
||||||
buttonsOriginY,
|
|
||||||
buttonMarginY,
|
|
||||||
buttonMarginX,
|
|
||||||
theme = [self theme],
|
|
||||||
offsetX;
|
|
||||||
|
|
||||||
[aRepresentativeButton setTheme:[self theme]];
|
|
||||||
[aRepresentativeButton sizeToFit];
|
|
||||||
|
|
||||||
panelSize.height = CGRectGetMaxY([lastView frame]) + defaultElementsMargin + [aRepresentativeButton frameSize].height;
|
|
||||||
if (panelSize.height < minimumSize.height)
|
|
||||||
panelSize.height = minimumSize.height;
|
|
||||||
|
|
||||||
buttonsOriginY = panelSize.height - [aRepresentativeButton frameSize].height + buttonOffset;
|
|
||||||
offsetX = panelSize.width - inset.right;
|
|
||||||
|
|
||||||
switch ([_window styleMask])
|
|
||||||
{
|
{
|
||||||
case _CPModalWindowMask:
|
case CPWarningAlertStyle: theImage = [self currentValueForThemeAttribute:@"warning-image"];
|
||||||
buttonMarginY = [_themeView currentValueForThemeAttribute:@"modal-window-button-margin-y"];
|
theTitle = @"Warning";
|
||||||
buttonMarginX = [_themeView currentValueForThemeAttribute:@"modal-window-button-margin-x"];
|
break;
|
||||||
break;
|
case CPInformationalAlertStyle: theImage = [self currentValueForThemeAttribute:@"information-image"];
|
||||||
|
theTitle = @"Information";
|
||||||
default:
|
break;
|
||||||
buttonMarginY = [_themeView currentValueForThemeAttribute:@"standard-window-button-margin-y"];
|
case CPCriticalAlertStyle: theImage = [self currentValueForThemeAttribute:@"error-image"];
|
||||||
buttonMarginX = [_themeView currentValueForThemeAttribute:@"standard-window-button-margin-x"];
|
theTitle = @"Error";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = [_buttons count] - 1; i >= 0 ; i--)
|
|
||||||
{
|
|
||||||
var button = _buttons[i];
|
|
||||||
[button setTheme:[self theme]];
|
|
||||||
[button sizeToFit];
|
|
||||||
|
|
||||||
var buttonFrame = [button frame],
|
|
||||||
width = MAX(80.0, CGRectGetWidth(buttonFrame)),
|
|
||||||
height = CGRectGetHeight(buttonFrame);
|
|
||||||
|
|
||||||
offsetX -= width;
|
|
||||||
[button setFrame:CGRectMake(offsetX + buttonMarginX, buttonsOriginY + buttonMarginY, width, height)];
|
|
||||||
offsetX -= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_showHelp)
|
|
||||||
{
|
|
||||||
var helpImage = [_themeView currentValueForThemeAttribute:@"help-image"],
|
|
||||||
helpImagePressed = [_themeView currentValueForThemeAttribute:@"help-image-pressed"],
|
|
||||||
helpImageSize = helpImage ? [helpImage size] : CGSizeMakeZero(),
|
|
||||||
helpFrame = CGRectMake(helpLeftOffset, buttonsOriginY, helpImageSize.width, helpImageSize.height);
|
|
||||||
|
|
||||||
[_alertHelpButton setImage:helpImage];
|
|
||||||
[_alertHelpButton setAlternateImage:helpImagePressed];
|
|
||||||
[_alertHelpButton setBordered:NO];
|
|
||||||
[_alertHelpButton setFrame:helpFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
panelSize.height += [aRepresentativeButton frameSize].height + inset.bottom + buttonOffset;
|
|
||||||
return panelSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)layout
|
|
||||||
{
|
|
||||||
if (!_needsLayout)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!_window)
|
|
||||||
[self _createWindowWithStyle:nil];
|
|
||||||
|
|
||||||
var iconOffset = [_themeView currentValueForThemeAttribute:@"image-offset"],
|
|
||||||
theImage = _icon,
|
|
||||||
finalSize;
|
|
||||||
|
|
||||||
if (!theImage)
|
|
||||||
switch (_alertStyle)
|
|
||||||
{
|
|
||||||
case CPWarningAlertStyle:
|
|
||||||
theImage = [_themeView currentValueForThemeAttribute:@"warning-image"];
|
|
||||||
break;
|
|
||||||
case CPInformationalAlertStyle:
|
|
||||||
theImage = [_themeView currentValueForThemeAttribute:@"information-image"];
|
|
||||||
break;
|
|
||||||
case CPCriticalAlertStyle:
|
|
||||||
theImage = [_themeView currentValueForThemeAttribute:@"error-image"];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[_alertImageView setImage:theImage];
|
[_alertImageView setImage:theImage];
|
||||||
|
|
||||||
var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
|
var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
|
||||||
[_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
|
[_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
|
||||||
|
|
||||||
[self _layoutMessageView];
|
[_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle];
|
||||||
[self _layoutInformativeView];
|
[_alertPanel setFloatingPanel:YES];
|
||||||
[self _layoutAccessoryView];
|
[_alertPanel center];
|
||||||
[self _layoutSuppressionButton];
|
|
||||||
|
|
||||||
var lastView = _informativeLabel;
|
[_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]];
|
||||||
if (_showSuppressionButton)
|
[_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]];
|
||||||
lastView = _suppressionButton;
|
[_messageLabel setTextShadowColor:[self currentValueForThemeAttribute:@"message-text-shadow-color"]];
|
||||||
else if (_accessoryView)
|
[_messageLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"message-text-shadow-offset"]];
|
||||||
lastView = _accessoryView;
|
[_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]];
|
||||||
|
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
||||||
|
|
||||||
finalSize = [self _layoutButtonsFromView:lastView];
|
[_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]];
|
||||||
if ([_window styleMask] & CPDocModalWindowMask)
|
[_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]];
|
||||||
finalSize.height -= 26; // adjust the absence of title bar
|
[_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]];
|
||||||
|
[_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
|
||||||
|
[_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
||||||
|
|
||||||
[_window setFrameSize:finalSize];
|
// FIXME sizeWithFontCorrection shouldn't be needed.
|
||||||
[_window center];
|
var bounds = [[_alertPanel contentView] bounds],
|
||||||
|
offsetX = CGRectGetWidth(bounds) - inset.right,
|
||||||
|
informativeOffset = [self currentValueForThemeAttribute:@"informative-offset"],
|
||||||
|
buttonOffset = [self currentValueForThemeAttribute:@"button-offset"],
|
||||||
|
|
||||||
if ([_window styleMask] & _CPModalWindowMask || [_window styleMask] & CPHUDBackgroundWindowMask)
|
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--)
|
||||||
{
|
{
|
||||||
[_window setMovable:YES];
|
var button = _buttons[count];
|
||||||
[_window setMovableByWindowBackground:YES];
|
[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;
|
||||||
}
|
}
|
||||||
|
|
||||||
_needsLayout = NO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Displaying Alerts
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Displays the \c CPAlert panel as a modal dialog. The user will not be
|
Displays the \c CPAlert panel as a modal dialog. The user will not be
|
||||||
able to interact with any other controls until s/he has dismissed the alert
|
able to interact with any other controls until s/he has dismissed the alert
|
||||||
@@ -586,14 +403,8 @@ var bottomHeight = 71;
|
|||||||
*/
|
*/
|
||||||
- (void)runModal
|
- (void)runModal
|
||||||
{
|
{
|
||||||
if (!([_window styleMask] & _defaultWindowStyle))
|
[self layoutPanel];
|
||||||
{
|
[CPApp runModalForWindow:_alertPanel];
|
||||||
_needsLayout = YES;
|
|
||||||
[self _createWindowWithStyle:_defaultWindowStyle];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self layout];
|
|
||||||
[CPApp runModalForWindow:_window];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -604,20 +415,16 @@ var bottomHeight = 71;
|
|||||||
@param alertDidEndSelector Message the alert sends to modalDelegate after the sheet is dismissed.
|
@param alertDidEndSelector Message the alert sends to modalDelegate after the sheet is dismissed.
|
||||||
@param contextInfo Contextual data passed to modalDelegate in didEndSelector message.
|
@param contextInfo Contextual data passed to modalDelegate in didEndSelector message.
|
||||||
*/
|
*/
|
||||||
- (void)beginSheetModalForWindow:(CPWindow)aWindow modalDelegate:(id)modalDelegate didEndSelector:(SEL)alertDidEndSelector contextInfo:(id)contextInfo
|
- (void)beginSheetModalForWindow:(CPWindow)window modalDelegate:(id)modalDelegate didEndSelector:(SEL)alertDidEndSelector contextInfo:(void)contextInfo
|
||||||
{
|
{
|
||||||
if (!([_window styleMask] & CPDocModalWindowMask))
|
if (!(_windowStyle & CPDocModalWindowMask))
|
||||||
{
|
[self setWindowStyle:CPDocModalWindowMask];
|
||||||
_needsLayout = YES;
|
[self layoutPanel];
|
||||||
[self _createWindowWithStyle:CPDocModalWindowMask];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self layout];
|
|
||||||
|
|
||||||
_modalDelegate = modalDelegate;
|
|
||||||
_didEndSelector = alertDidEndSelector;
|
_didEndSelector = alertDidEndSelector;
|
||||||
|
_modalDelegate = modalDelegate;
|
||||||
|
|
||||||
[CPApp beginSheet:_window modalForWindow:aWindow modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
|
[CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -625,133 +432,39 @@ var bottomHeight = 71;
|
|||||||
|
|
||||||
@param window The parent window for the sheet.
|
@param window The parent window for the sheet.
|
||||||
*/
|
*/
|
||||||
- (void)beginSheetModalForWindow:(CPWindow)aWindow
|
- (void)beginSheetModalForWindow:(CPWindow)window
|
||||||
{
|
{
|
||||||
[self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
|
if (!(_windowStyle & CPDocModalWindowMask))
|
||||||
|
[self setWindowStyle:CPDocModalWindowMask];
|
||||||
|
[self layoutPanel];
|
||||||
|
|
||||||
|
[CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Private
|
- (void)_alertDidEnd:(CPWindow)aSheet returnCode:(CPInteger)returnCode contextInfo:(id)contextInfo
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_createWindowWithStyle:(int)forceStyle
|
|
||||||
{
|
{
|
||||||
var frame = CGRectMakeZero();
|
if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
|
||||||
frame.size = [_themeView currentValueForThemeAttribute:@"size"];
|
[_delegate alertDidEnd:self returnCode:returnCode];
|
||||||
|
|
||||||
_window = [[CPPanel alloc] initWithContentRect:frame styleMask:forceStyle || _defaultWindowStyle];
|
|
||||||
[_window setLevel:CPStatusWindowLevel];
|
|
||||||
[_window setPlatformWindow:[[CPApp keyWindow] platformWindow]];
|
|
||||||
|
|
||||||
if (_title)
|
|
||||||
[_window setTitle:_title];
|
|
||||||
|
|
||||||
var contentView = [_window contentView],
|
|
||||||
count = [_buttons count];
|
|
||||||
|
|
||||||
if (count)
|
|
||||||
while (count--)
|
|
||||||
[contentView addSubview:_buttons[count]];
|
|
||||||
else
|
|
||||||
[self addButtonWithTitle:@"OK"];
|
|
||||||
|
|
||||||
[contentView addSubview:_messageLabel];
|
|
||||||
[contentView addSubview:_alertImageView];
|
|
||||||
[contentView addSubview:_informativeLabel];
|
|
||||||
|
|
||||||
if (_showHelp)
|
|
||||||
[contentView addSubview:_alertHelpButton];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (@action)_showHelp:(id)aSender
|
|
||||||
{
|
|
||||||
if ([_delegate respondsToSelector:@selector(alertShowHelp:)])
|
|
||||||
[_delegate alertShowHelp:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (@action)_takeReturnCodeFrom:(id)aSender
|
|
||||||
{
|
|
||||||
if ([_window isSheet])
|
|
||||||
{
|
|
||||||
[_window orderOut:nil];
|
|
||||||
[CPApp endSheet:_window returnCode:[aSender tag]];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[CPApp abortModal];
|
|
||||||
[_window close];
|
|
||||||
|
|
||||||
[self _alertDidEnd:_window returnCode:[aSender tag] contextInfo:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_alertDidEnd:(CPWindow)aWindow returnCode:(int)returnCode contextInfo:(id)contextInfo
|
|
||||||
{
|
|
||||||
if (_didEndSelector)
|
if (_didEndSelector)
|
||||||
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
||||||
|
|
||||||
_modalDelegate = nil;
|
|
||||||
_didEndSelector = nil;
|
_didEndSelector = nil;
|
||||||
|
_modalDelegate = nil;
|
||||||
|
}
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
|
/* @ignore */
|
||||||
[_delegate alertDidEnd:self returnCode:returnCode];
|
- (void)_dismissAlert:(CPButton)button
|
||||||
}
|
{
|
||||||
|
if ([_alertPanel isSheet])
|
||||||
@end
|
[CPApp endSheet:_alertPanel returnCode:[button tag]];
|
||||||
|
else
|
||||||
@implementation _CPAlertThemeView : CPView
|
{
|
||||||
|
[CPApp abortModal];
|
||||||
+ (CPString)defaultThemeClass
|
[_alertPanel close];
|
||||||
{
|
|
||||||
return @"alert";
|
[self _alertDidEnd:nil returnCode:[button tag] contextInfo:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return @{
|
|
||||||
@"size": CGSizeMake(400.0, 110.0),
|
|
||||||
@"content-inset": CGInsetMake(15, 15, 15, 50),
|
|
||||||
@"informative-offset": 6,
|
|
||||||
@"button-offset": 10,
|
|
||||||
@"message-text-alignment": CPJustifiedTextAlignment,
|
|
||||||
@"message-text-color": [CPColor blackColor],
|
|
||||||
@"message-text-font": [CPFont boldSystemFontOfSize:13.0],
|
|
||||||
@"message-text-shadow-color": [CPNull null],
|
|
||||||
@"message-text-shadow-offset": CGSizeMakeZero(),
|
|
||||||
@"informative-text-alignment": CPJustifiedTextAlignment,
|
|
||||||
@"informative-text-color": [CPColor blackColor],
|
|
||||||
@"informative-text-font": [CPFont systemFontOfSize:12.0],
|
|
||||||
@"informative-text-shadow-color": [CPNull null],
|
|
||||||
@"informative-text-shadow-offset": CGSizeMakeZero(),
|
|
||||||
@"image-offset": CGPointMake(15, 12),
|
|
||||||
@"information-image": [CPNull null],
|
|
||||||
@"warning-image": [CPNull null],
|
|
||||||
@"error-image": [CPNull null],
|
|
||||||
@"help-image": [CPNull null],
|
|
||||||
@"help-image-left-offset": 15,
|
|
||||||
@"help-image-pressed": [CPNull null],
|
|
||||||
@"suppression-button-y-offset": 0.0,
|
|
||||||
@"suppression-button-x-offset": 0.0,
|
|
||||||
@"default-elements-margin": 3.0,
|
|
||||||
@"suppression-button-text-color": [CPColor blackColor],
|
|
||||||
@"suppression-button-text-font": [CPFont systemFontOfSize:12.0],
|
|
||||||
@"suppression-button-text-shadow-color": [CPNull null],
|
|
||||||
@"suppression-button-text-shadow-offset": 0.0,
|
|
||||||
@"modal-window-button-margin-y": 0.0,
|
|
||||||
@"modal-window-button-margin-x": 0.0,
|
|
||||||
@"standard-window-button-margin-y": 0.0,
|
|
||||||
@"standard-window-button-margin-x": 0.0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+25
-112
@@ -21,7 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPTimer.j>
|
|
||||||
|
|
||||||
@import "CAMediaTimingFunction.j"
|
@import "CAMediaTimingFunction.j"
|
||||||
|
|
||||||
@@ -122,30 +121,23 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
*/
|
*/
|
||||||
- (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve
|
- (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve
|
||||||
{
|
{
|
||||||
var timingFunctionName;
|
|
||||||
|
|
||||||
switch (anAnimationCurve)
|
switch (anAnimationCurve)
|
||||||
{
|
{
|
||||||
case CPAnimationEaseInOut:
|
case CPAnimationEaseInOut: timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut;
|
||||||
timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case CPAnimationEaseIn:
|
case CPAnimationEaseIn: timingFunctionName = kCAMediaTimingFunctionEaseIn;
|
||||||
timingFunctionName = kCAMediaTimingFunctionEaseIn;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case CPAnimationEaseOut:
|
case CPAnimationEaseOut: timingFunctionName = kCAMediaTimingFunctionEaseOut;
|
||||||
timingFunctionName = kCAMediaTimingFunctionEaseOut;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case CPAnimationLinear:
|
case CPAnimationLinear: timingFunctionName = kCAMediaTimingFunctionLinear;
|
||||||
timingFunctionName = kCAMediaTimingFunctionLinear;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default: [CPException raise:CPInvalidArgumentException
|
||||||
[CPException raise:CPInvalidArgumentException
|
reason:"Invalid value provided for animation curve"];
|
||||||
reason:@"Invalid value provided for animation curve"];
|
break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_animationCurve = anAnimationCurve;
|
_animationCurve = anAnimationCurve;
|
||||||
@@ -314,16 +306,13 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
|
if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
|
||||||
return [_delegate animation:self valueForProgress:t];
|
return [_delegate animation:self valueForProgress:t];
|
||||||
|
|
||||||
if (_animationCurve == CPAnimationLinear)
|
|
||||||
return t;
|
|
||||||
|
|
||||||
var c1 = [],
|
var c1 = [],
|
||||||
c2 = [];
|
c2 = [];
|
||||||
|
|
||||||
[_timingFunction getControlPointAtIndex:1 values:c1];
|
[_timingFunction getControlPointAtIndex:1 values:c1];
|
||||||
[_timingFunction getControlPointAtIndex:2 values:c2];
|
[_timingFunction getControlPointAtIndex:2 values:c2];
|
||||||
|
|
||||||
return CubicBezierAtTime(t, c1[0], c1[1], c2[0], c2[1], _duration);
|
return CubicBezierAtTime(t,c1[0],c1[1],c2[0],c2[1],_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -331,104 +320,28 @@ ACTUAL_FRAME_RATE = 0;
|
|||||||
// currently used function to determine time
|
// currently used function to determine time
|
||||||
// 1:1 conversion to js from webkit source files
|
// 1:1 conversion to js from webkit source files
|
||||||
// UnitBezier.h, WebCore_animation_AnimationBase.cpp
|
// UnitBezier.h, WebCore_animation_AnimationBase.cpp
|
||||||
var CubicBezierAtTime = function(t, p1x, p1y, p2x, p2y, duration)
|
var CubicBezierAtTime = function CubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration)
|
||||||
{
|
{
|
||||||
var ax = 0,
|
var ax=0,bx=0,cx=0,ay=0,by=0,cy=0;
|
||||||
bx = 0,
|
|
||||||
cx = 0,
|
|
||||||
ay = 0,
|
|
||||||
by = 0,
|
|
||||||
cy = 0;
|
|
||||||
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
|
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
|
||||||
function sampleCurveX(t)
|
function sampleCurveX(t) {return ((ax*t+bx)*t+cx)*t;};
|
||||||
{
|
function sampleCurveY(t) {return ((ay*t+by)*t+cy)*t;};
|
||||||
return ((ax * t + bx) * t + cx) * t;
|
function sampleCurveDerivativeX(t) {return (3.0*ax*t+2.0*bx)*t+cx;};
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
// 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)
|
function solveEpsilon(duration) {return 1.0/(200.0*duration);};
|
||||||
{
|
function solve(x,epsilon) {return sampleCurveY(solveCurveX(x,epsilon));};
|
||||||
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.
|
// Given an x value, find a parametric value it came from.
|
||||||
function solveCurveX(x, epsilon)
|
function solveCurveX(x,epsilon) {var t0,t1,t2,x2,d2,i;
|
||||||
{
|
function fabs(n) {if(n>=0) {return n;}else {return 0-n;}};
|
||||||
var t0,
|
|
||||||
t1,
|
|
||||||
t2 = x,
|
|
||||||
x2,
|
|
||||||
d2,
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// First try a few iterations of Newton's method -- normally very fast.
|
// First try a few iterations of Newton's method -- normally very fast.
|
||||||
for (; i < 8; i++)
|
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;}
|
||||||
{
|
|
||||||
x2 = sampleCurveX(t2) - x;
|
|
||||||
|
|
||||||
if (ABS(x2) < epsilon)
|
|
||||||
return t2;
|
|
||||||
|
|
||||||
d2 = sampleCurveDerivativeX(t2);
|
|
||||||
|
|
||||||
if (ABS(d2) < 1e-6)
|
|
||||||
break;
|
|
||||||
|
|
||||||
t2 = t2 - x2 / d2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to the bisection method for reliability.
|
// Fall back to the bisection method for reliability.
|
||||||
t0 = 0.0;
|
t0=0.0; t1=1.0; t2=x; if(t2<t0) {return t0;} if(t2>t1) {return t1;}
|
||||||
t1 = 1.0;
|
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;}
|
||||||
t2 = x;
|
|
||||||
|
|
||||||
if (t2 < t0)
|
|
||||||
return t0;
|
|
||||||
|
|
||||||
if (t2 > t1)
|
|
||||||
return t1;
|
|
||||||
|
|
||||||
while (t0 < t1)
|
|
||||||
{
|
|
||||||
x2 = sampleCurveX(t2);
|
|
||||||
|
|
||||||
if (ABS(x2 - x) < epsilon)
|
|
||||||
return t2;
|
|
||||||
|
|
||||||
if (x > x2)
|
|
||||||
t0 = t2;
|
|
||||||
|
|
||||||
else
|
|
||||||
t1 = t2;
|
|
||||||
|
|
||||||
t2 = (t1 - t0) * 0.5 + t0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return t2; // Failure.
|
return t2; // Failure.
|
||||||
};
|
};
|
||||||
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
|
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
|
||||||
cx = 3.0 * p1x;
|
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;
|
||||||
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.
|
// Convert from input time to parametric value in curve, then from that to output time.
|
||||||
return solve(t, solveEpsilon(duration));
|
return solve(t, solveEpsilon(duration));
|
||||||
};
|
};
|
||||||
|
|||||||
+92
-340
@@ -23,22 +23,17 @@
|
|||||||
@import <Foundation/CPBundle.j>
|
@import <Foundation/CPBundle.j>
|
||||||
|
|
||||||
@import "CPCompatibility.j"
|
@import "CPCompatibility.j"
|
||||||
@import "CPColorPanel.j"
|
|
||||||
@import "CPCursor.j"
|
|
||||||
@import "CPDocumentController.j"
|
|
||||||
@import "CPEvent.j"
|
@import "CPEvent.j"
|
||||||
@import "CPMenu.j"
|
@import "CPMenu.j"
|
||||||
@import "CPResponder.j"
|
@import "CPResponder.j"
|
||||||
|
@import "CPDocumentController.j"
|
||||||
@import "CPThemeBlend.j"
|
@import "CPThemeBlend.j"
|
||||||
@import "CPCibLoading.j"
|
@import "CPCibLoading.j"
|
||||||
@import "CPPanel.j"
|
|
||||||
@import "CPPlatform.j"
|
@import "CPPlatform.j"
|
||||||
@import "CPWindowController.j"
|
|
||||||
@import "_CPPopoverWindow.j"
|
|
||||||
|
|
||||||
var CPMainCibFile = @"CPMainCibFile",
|
var CPMainCibFile = @"CPMainCibFile",
|
||||||
CPMainCibFileHumanFriendly = @"Main cib file base name",
|
CPMainCibFileHumanFriendly = @"Main cib file base name";
|
||||||
CPEventModifierFlags = 0;
|
|
||||||
|
|
||||||
CPApp = nil;
|
CPApp = nil;
|
||||||
|
|
||||||
@@ -62,7 +57,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPApplication
|
@class CPApplication
|
||||||
|
|
||||||
CPApplication is THE way to start up the Cappuccino framework for your application to use.
|
CPApplication is THE way to start up the Cappucino framework for your application to use.
|
||||||
Every GUI application has exactly one instance of CPApplication (or of a custom subclass of
|
Every GUI application has exactly one instance of CPApplication (or of a custom subclass of
|
||||||
CPApplication). Your program's main() function can create that instance by calling the
|
CPApplication). Your program's main() function can create that instance by calling the
|
||||||
\c CPApplicationMain function. A simple example looks like this:
|
\c CPApplicationMain function. A simple example looks like this:
|
||||||
@@ -86,10 +81,8 @@ CPRunContinuesResponse = -1002;
|
|||||||
@implementation CPApplication : CPResponder
|
@implementation CPApplication : CPResponder
|
||||||
{
|
{
|
||||||
CPArray _eventListeners;
|
CPArray _eventListeners;
|
||||||
int _eventListenerInsertionIndex;
|
|
||||||
|
|
||||||
CPEvent _currentEvent;
|
CPEvent _currentEvent;
|
||||||
CPWindow _lastMouseMoveWindow;
|
|
||||||
|
|
||||||
CPArray _windows;
|
CPArray _windows;
|
||||||
CPWindow _keyWindow;
|
CPWindow _keyWindow;
|
||||||
@@ -97,6 +90,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
CPWindow _previousKeyWindow;
|
CPWindow _previousKeyWindow;
|
||||||
CPWindow _previousMainWindow;
|
CPWindow _previousMainWindow;
|
||||||
|
|
||||||
|
CPMenu _mainMenu;
|
||||||
CPDocumentController _documentController;
|
CPDocumentController _documentController;
|
||||||
|
|
||||||
CPModalSession _currentSession;
|
CPModalSession _currentSession;
|
||||||
@@ -144,9 +138,10 @@ CPRunContinuesResponse = -1002;
|
|||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_eventListeners = [];
|
_eventListeners = [];
|
||||||
_eventListenerInsertionIndex = 0;
|
|
||||||
|
|
||||||
_windows = [[CPNull null]];
|
_windows = [];
|
||||||
|
|
||||||
|
[_windows addObject:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -225,22 +220,28 @@ CPRunContinuesResponse = -1002;
|
|||||||
// At this point we clear the window.status to eliminate Safari's "Cancelled" error message
|
// At this point we clear the window.status to eliminate Safari's "Cancelled" error message
|
||||||
// The message shouldn't be displayed, because only an XHR is cancelled, but it is a usability issue.
|
// The message shouldn't be displayed, because only an XHR is cancelled, but it is a usability issue.
|
||||||
// We do it here so that applications can change it in willFinish or didFinishLaunching
|
// We do it here so that applications can change it in willFinish or didFinishLaunching
|
||||||
#if PLATFORM(DOM)
|
|
||||||
window.status = " ";
|
window.status = " ";
|
||||||
#endif
|
|
||||||
|
|
||||||
// We also want to set the default cursor on the body, so that buttons and things don't have an iBeam
|
// We also want to set the default cursor on the body, so that buttons and things don't have an iBeam
|
||||||
[[CPCursor arrowCursor] set];
|
[[CPCursor arrowCursor] set];
|
||||||
|
|
||||||
var bundle = [CPBundle mainBundle],
|
var bundle = [CPBundle mainBundle],
|
||||||
delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"];
|
types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
|
||||||
|
|
||||||
|
if ([types count] > 0)
|
||||||
|
_documentController = [CPDocumentController sharedDocumentController];
|
||||||
|
|
||||||
|
var delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"];
|
||||||
|
|
||||||
if (delegateClassName)
|
if (delegateClassName)
|
||||||
{
|
{
|
||||||
var delegateClass = objj_getClass(delegateClassName);
|
var delegateClass = objj_getClass(delegateClassName);
|
||||||
|
|
||||||
if (delegateClass)
|
if (delegateClass)
|
||||||
[self setDelegate:[[delegateClass alloc] init]];
|
if ([_documentController class] == delegateClass)
|
||||||
|
[self setDelegate:_documentController];
|
||||||
|
else
|
||||||
|
[self setDelegate:[[delegateClass alloc] init]];
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||||
@@ -249,23 +250,13 @@ CPRunContinuesResponse = -1002;
|
|||||||
postNotificationName:CPApplicationWillFinishLaunchingNotification
|
postNotificationName:CPApplicationWillFinishLaunchingNotification
|
||||||
object:self];
|
object:self];
|
||||||
|
|
||||||
var types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
|
|
||||||
|
|
||||||
if ([types count] > 0)
|
|
||||||
_documentController = [CPDocumentController sharedDocumentController];
|
|
||||||
|
|
||||||
var needsUntitled = !!_documentController,
|
var needsUntitled = !!_documentController,
|
||||||
URLStrings = nil;
|
URLStrings = window.cpOpeningURLStrings && window.cpOpeningURLStrings(),
|
||||||
|
index = 0,
|
||||||
#if PLATFORM(DOM)
|
|
||||||
URLStrings = window.cpOpeningURLStrings && window.cpOpeningURLStrings();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var index = 0,
|
|
||||||
count = [URLStrings count];
|
count = [URLStrings count];
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
needsUntitled = ![self _openURL:[CPURL URLWithString:URLStrings[index]]] && needsUntitled;
|
needsUntitled = ![self _openURL:[CPURL URLWithString:URLStrings[index]]] || needsUntitled;
|
||||||
|
|
||||||
if (needsUntitled && [_delegate respondsToSelector:@selector(applicationShouldOpenUntitledFile:)])
|
if (needsUntitled && [_delegate respondsToSelector:@selector(applicationShouldOpenUntitledFile:)])
|
||||||
needsUntitled = [_delegate applicationShouldOpenUntitledFile:self];
|
needsUntitled = [_delegate applicationShouldOpenUntitledFile:self];
|
||||||
@@ -302,21 +293,11 @@ CPRunContinuesResponse = -1002;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the applications icon image. This image is used in the default "About" window.
|
|
||||||
By default this value is pulled from the CPApplicationIcon key in your info.plist file.
|
|
||||||
|
|
||||||
@param anImage - The image to set.
|
|
||||||
*/
|
|
||||||
- (void)setApplicationIconImage:(CPImage)anImage
|
- (void)setApplicationIconImage:(CPImage)anImage
|
||||||
{
|
{
|
||||||
_applicationIconImage = anImage;
|
_applicationIconImage = anImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the application icon image. By default this is pulled from the CPApplicationIcon key of info.plist.
|
|
||||||
@return CPImage - Your application icon image.
|
|
||||||
*/
|
|
||||||
- (CPImage)applicationIconImage
|
- (CPImage)applicationIconImage
|
||||||
{
|
{
|
||||||
if (_applicationIconImage)
|
if (_applicationIconImage)
|
||||||
@@ -329,38 +310,11 @@ CPRunContinuesResponse = -1002;
|
|||||||
return _applicationIconImage;
|
return _applicationIconImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Opens the standard about panel with no options.
|
|
||||||
*/
|
|
||||||
- (void)orderFrontStandardAboutPanel:(id)sender
|
- (void)orderFrontStandardAboutPanel:(id)sender
|
||||||
{
|
{
|
||||||
[self orderFrontStandardAboutPanelWithOptions:nil];
|
[self orderFrontStandardAboutPanelWithOptions:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Opens the standard about panel. This method takes a single argument \c options.
|
|
||||||
Options is a dictionary that can contain any of the following keys:
|
|
||||||
<pre>
|
|
||||||
ApplicationName - The name of your application.
|
|
||||||
ApplicationIcon - Application icon image.
|
|
||||||
Version - The full version of your application
|
|
||||||
ApplicationVersion - The shorter version number of your application.
|
|
||||||
Copyright - Human readable copyright information.
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
If you choose not the include any of the above keys, they will default
|
|
||||||
to the following respective keys in your info.plist file.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPBundleName
|
|
||||||
CPApplicationIcon (through a call to -applicationIconImage, see documentation for that method for more details)
|
|
||||||
CPBundleVersion
|
|
||||||
CPBundleShortVersionString
|
|
||||||
CPHumanReadableCopyright
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
@param options - A dictionary with the aboe listed keys. You can pass nil to default to your plist values.
|
|
||||||
*/
|
|
||||||
- (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)options
|
- (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)options
|
||||||
{
|
{
|
||||||
if (!_aboutPanel)
|
if (!_aboutPanel)
|
||||||
@@ -372,32 +326,22 @@ CPRunContinuesResponse = -1002;
|
|||||||
applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"],
|
applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"],
|
||||||
copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"];
|
copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"];
|
||||||
|
|
||||||
var windowWidth = 275,
|
var aboutPanelPath = [[CPBundle bundleForClass:[CPWindowController class]] pathForResource:@"AboutPanel.cib"],
|
||||||
windowHeight = 223,
|
aboutPanelController = [CPWindowController alloc],
|
||||||
imgWidth = 100,
|
aboutPanelController = [aboutPanelController initWithWindowCibPath:aboutPanelPath owner:aboutPanelController],
|
||||||
imgHeight = 100,
|
aboutPanel = [aboutPanelController window],
|
||||||
interField = 8,
|
contentView = [aboutPanel contentView],
|
||||||
aboutPanel = [[CPWindow alloc] initWithContentRect:CGRectMake(0, 0, windowWidth, windowHeight) styleMask:CPClosableWindowMask],
|
imageView = [contentView viewWithTag:1],
|
||||||
imageView = [[CPImageView alloc] initWithFrame:CGRectMake((windowWidth / 2) - (imgWidth / 2), interField, imgWidth, imgHeight)],
|
applicationLabel = [contentView viewWithTag:2],
|
||||||
applicationLabel = [[CPTextField alloc] initWithFrame:CGRectMake(17, imgHeight + 16, windowWidth - 34, 24)],
|
versionLabel = [contentView viewWithTag:3],
|
||||||
versionLabel = [[CPTextField alloc] initWithFrame:CGRectMake(17, imgHeight + 48, windowWidth - 34, 16)],
|
copyrightLabel = [contentView viewWithTag:4],
|
||||||
copyrightLabel = [[CPTextField alloc] initWithFrame:CGRectMake(17, imgHeight + 72, windowWidth - 34, 32)],
|
standardPath = [[CPBundle bundleForClass:[self class]] pathForResource:@"standardApplicationIcon.png"];
|
||||||
contentView = [aboutPanel contentView];
|
|
||||||
|
|
||||||
[applicationLabel setFont:[CPFont boldSystemFontOfSize:[CPFont systemFontSize] + 2]];
|
// FIXME move this into the CIB eventually
|
||||||
|
[applicationLabel setFont:[CPFont boldSystemFontOfSize:14.0]];
|
||||||
[applicationLabel setAlignment:CPCenterTextAlignment];
|
[applicationLabel setAlignment:CPCenterTextAlignment];
|
||||||
[versionLabel setFont:[CPFont systemFontOfSize:[CPFont systemFontSize] - 1]];
|
|
||||||
[versionLabel setAlignment:CPCenterTextAlignment];
|
[versionLabel setAlignment:CPCenterTextAlignment];
|
||||||
[copyrightLabel setFont:[CPFont systemFontOfSize:[CPFont systemFontSize] - 1]];
|
|
||||||
[copyrightLabel setAlignment:CPCenterTextAlignment];
|
[copyrightLabel setAlignment:CPCenterTextAlignment];
|
||||||
[copyrightLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
|
||||||
|
|
||||||
[contentView addSubview:imageView];
|
|
||||||
[contentView addSubview:applicationLabel];
|
|
||||||
[contentView addSubview:versionLabel];
|
|
||||||
[contentView addSubview:copyrightLabel];
|
|
||||||
|
|
||||||
var standardPath = [[CPBundle bundleForClass:[self class]] pathForResource:@"standardApplicationIcon.png"];
|
|
||||||
|
|
||||||
[imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath
|
[imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath
|
||||||
size:CGSizeMake(256, 256)]];
|
size:CGSizeMake(256, 256)]];
|
||||||
@@ -411,7 +355,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
else
|
else
|
||||||
[versionLabel setStringValue:@""];
|
[versionLabel setStringValue:@""];
|
||||||
|
|
||||||
[copyrightLabel setStringValue:copyright || @""];
|
[copyrightLabel setStringValue:copyright || ""];
|
||||||
[aboutPanel center];
|
[aboutPanel center];
|
||||||
|
|
||||||
_aboutPanel = aboutPanel;
|
_aboutPanel = aboutPanel;
|
||||||
@@ -421,7 +365,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)_documentController:(CPDocumentController)docController didCloseAll:(BOOL)didCloseAll context:(Object)info
|
- (void)_documentController:(NSDocumentController *)docController didCloseAll:(BOOL)didCloseAll context:(Object)info
|
||||||
{
|
{
|
||||||
// callback method for terminate:
|
// callback method for terminate:
|
||||||
if (didCloseAll)
|
if (didCloseAll)
|
||||||
@@ -520,8 +464,6 @@ CPRunContinuesResponse = -1002;
|
|||||||
if (_eventListeners[count]._callback === _CPRunModalLoop)
|
if (_eventListeners[count]._callback === _CPRunModalLoop)
|
||||||
{
|
{
|
||||||
_eventListeners.splice(count, 1);
|
_eventListeners.splice(count, 1);
|
||||||
if (count <= _eventListenerInsertionIndex)
|
|
||||||
_eventListenerInsertionIndex--;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -569,7 +511,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
|
|
||||||
// [theWindow._bridge _obscureWindowsBelowModalWindow];
|
// [theWindow._bridge _obscureWindowsBelowModalWindow];
|
||||||
|
|
||||||
[CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:YES];
|
[CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -588,7 +530,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
- (BOOL)_handleKeyEquivalent:(CPEvent)anEvent
|
- (BOOL)_handleKeyEquivalent:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
return [[self keyWindow] performKeyEquivalent:anEvent] ||
|
return [[self keyWindow] performKeyEquivalent:anEvent] ||
|
||||||
[[self mainMenu] performKeyEquivalent:anEvent];
|
[_mainMenu performKeyEquivalent:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -598,82 +540,40 @@ CPRunContinuesResponse = -1002;
|
|||||||
- (void)sendEvent:(CPEvent)anEvent
|
- (void)sendEvent:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
_currentEvent = anEvent;
|
_currentEvent = anEvent;
|
||||||
CPEventModifierFlags = [anEvent modifierFlags];
|
|
||||||
|
|
||||||
var theWindow = [anEvent window];
|
var willPropagate = [[[anEvent window] platformWindow] _willPropagateCurrentDOMEvent];
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var willPropagate = [[theWindow platformWindow] _willPropagateCurrentDOMEvent];
|
|
||||||
|
|
||||||
// temporarily pretend we won't propagate the event. we'll restore the saved value later
|
// temporarily pretend we won't propagate the event. we'll restore the saved value later
|
||||||
// we do this outside the if so that changes user code might make in _handleKeyEquiv. are preserved
|
// we do this outside the if so that changes user code might make in _handleKeyEquiv. are preserved
|
||||||
[[theWindow platformWindow] _propagateCurrentDOMEvent:NO];
|
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||||
#endif
|
|
||||||
|
|
||||||
// Check if this is a candidate for key equivalent...
|
// Check if this is a candidate for key equivalent...
|
||||||
if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent])
|
if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent])
|
||||||
{
|
{
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var characters = [anEvent characters],
|
var characters = [anEvent characters],
|
||||||
modifierFlags = [anEvent modifierFlags];
|
modifierFlags = [anEvent modifierFlags];
|
||||||
|
|
||||||
// Unconditionally propagate on these keys to solve browser copy paste bugs
|
// Unconditionally propagate on these keys to solve browser copy paste bugs
|
||||||
if ((characters == "c" || characters == "x" || characters == "v") && (modifierFlags & CPPlatformActionKeyMask))
|
if ((characters == "c" || characters == "x" || characters == "v") && (modifierFlags & CPPlatformActionKeyMask))
|
||||||
[[theWindow platformWindow] _propagateCurrentDOMEvent:YES];
|
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||||
#endif
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
// if we make it this far, then restore the original willPropagate value
|
// if we make it this far, then restore the original willPropagate value
|
||||||
[[theWindow platformWindow] _propagateCurrentDOMEvent:willPropagate];
|
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:willPropagate];
|
||||||
#endif
|
|
||||||
|
|
||||||
if ([anEvent type] == CPMouseMoved)
|
if (_eventListeners.length)
|
||||||
{
|
{
|
||||||
if (theWindow !== _lastMouseMoveWindow)
|
if (_eventListeners[_eventListeners.length - 1]._mask & (1 << [anEvent type]))
|
||||||
[_lastMouseMoveWindow _mouseExitedResizeRect];
|
_eventListeners.pop()._callback(anEvent);
|
||||||
|
|
||||||
_lastMouseMoveWindow = theWindow;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
[[anEvent window] sendEvent:anEvent];
|
||||||
Event listeners are processed from back to front so that newer event listeners normally take
|
|
||||||
precedence. If during the execution of a callback a new event listener is added, it should
|
|
||||||
be inserted after the current callback but before any higher priority callbacks. This makes
|
|
||||||
repeating event listeners (those that reinsert themselves) stable relative to each other.
|
|
||||||
*/
|
|
||||||
for (var i = _eventListeners.length - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
var listener = _eventListeners[i];
|
|
||||||
|
|
||||||
if (listener._mask & (1 << [anEvent type]))
|
|
||||||
{
|
|
||||||
_eventListeners.splice(i, 1);
|
|
||||||
// In case the callback wants to add more listeners.
|
|
||||||
_eventListenerInsertionIndex = i;
|
|
||||||
listener._callback(anEvent);
|
|
||||||
|
|
||||||
if (listener._dequeue)
|
|
||||||
{
|
|
||||||
// Don't process the event normally and don't send it to any other listener.
|
|
||||||
_eventListenerInsertionIndex = _eventListeners.length;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_eventListenerInsertionIndex = _eventListeners.length;
|
|
||||||
|
|
||||||
if (theWindow)
|
|
||||||
[theWindow sendEvent:anEvent];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
If the delegate responds to the given selector it will call the method on the delegate,
|
|
||||||
otherwise the method will be passed to CPResponder.
|
|
||||||
*/
|
|
||||||
- (void)doCommandBySelector:(SEL)aSelector
|
- (void)doCommandBySelector:(SEL)aSelector
|
||||||
{
|
{
|
||||||
if ([_delegate respondsToSelector:aSelector])
|
if ([_delegate respondsToSelector:aSelector])
|
||||||
@@ -703,10 +603,6 @@ CPRunContinuesResponse = -1002;
|
|||||||
*/
|
*/
|
||||||
- (CPWindow)windowWithWindowNumber:(int)aWindowNumber
|
- (CPWindow)windowWithWindowNumber:(int)aWindowNumber
|
||||||
{
|
{
|
||||||
// Never allow _windows[0] to be returned - it's an internal CPNull placeholder.
|
|
||||||
if (!aWindowNumber)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return _windows[aWindowNumber];
|
return _windows[aWindowNumber];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,8 +611,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
*/
|
*/
|
||||||
- (CPArray)windows
|
- (CPArray)windows
|
||||||
{
|
{
|
||||||
// Return all windows, but not the CPNull placeholder in _windows[0].
|
return _windows;
|
||||||
return [_windows subarrayWithRange:CPMakeRange(1, [_windows count] - 1)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -742,7 +637,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
*/
|
*/
|
||||||
- (CPMenu)mainMenu
|
- (CPMenu)mainMenu
|
||||||
{
|
{
|
||||||
return [self menu];
|
return _mainMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -750,30 +645,21 @@ CPRunContinuesResponse = -1002;
|
|||||||
@param aMenu the menu to set for the application
|
@param aMenu the menu to set for the application
|
||||||
*/
|
*/
|
||||||
- (void)setMainMenu:(CPMenu)aMenu
|
- (void)setMainMenu:(CPMenu)aMenu
|
||||||
{
|
|
||||||
[self setMenu:aMenu];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMenu:(CPMenu)aMenu
|
|
||||||
{
|
{
|
||||||
if ([aMenu _menuName] === "CPMainMenu")
|
if ([aMenu _menuName] === "CPMainMenu")
|
||||||
{
|
{
|
||||||
if ([self menu] === aMenu)
|
if (_mainMenu === aMenu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[super setMenu:aMenu];
|
_mainMenu = aMenu;
|
||||||
|
|
||||||
if ([CPPlatform supportsNativeMainMenu])
|
if ([CPPlatform supportsNativeMainMenu])
|
||||||
window.cpSetMainMenu([self menu]);
|
window.cpSetMainMenu(_mainMenu);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[aMenu _setMenuName:@"CPMainMenu"];
|
[aMenu _setMenuName:@"CPMainMenu"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Opens the shared color panel.
|
|
||||||
@param aSender
|
|
||||||
*/
|
|
||||||
- (void)orderFrontColorPanel:(id)aSender
|
- (void)orderFrontColorPanel:(id)aSender
|
||||||
{
|
{
|
||||||
[[CPColorPanel sharedColorPanel] orderFront:self];
|
[[CPColorPanel sharedColorPanel] orderFront:self];
|
||||||
@@ -942,51 +828,16 @@ CPRunContinuesResponse = -1002;
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Fires a callback function when an event matching a given mask occurs.
|
|
||||||
|
|
||||||
If multiple callbacks are set which match the same event, later callbacks
|
|
||||||
take priority over earlier callbacks, unless a new callback is set while
|
|
||||||
an existing callback is being processed in which case it's given the same
|
|
||||||
priority as the currently processing callback.
|
|
||||||
|
|
||||||
@param aCallback A js function to be fired.
|
|
||||||
@prarm aMask An event mask for the next event.
|
|
||||||
@param anExpiration The date for which this callback expires (not implemented).
|
|
||||||
@param inMode (not implemented).
|
|
||||||
@param shouldDequeue YES to remove the event from the queue after calling the callback,
|
|
||||||
NO to deliver it normally.
|
|
||||||
*/
|
|
||||||
- (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
|
- (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
|
||||||
{
|
{
|
||||||
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, aCallback, shouldDequeue));
|
_eventListeners.push(_CPEventListenerMake(aMask, aCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (CPEvent)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
|
||||||
Assigns a target and action for the next event matching a given event mask.
|
|
||||||
The callback method called will be passed the CPEvent when it fires.
|
|
||||||
|
|
||||||
If multiple callbacks are set which match the same event, later callbacks
|
|
||||||
take priority over earlier callbacks, unless a new callback is set while
|
|
||||||
an existing callback is being processed in which case it's given the same
|
|
||||||
priority as the currently processing callback.
|
|
||||||
|
|
||||||
@param aTarget The target object for the callback.
|
|
||||||
@param aSelector The selector which should be called on the target object.
|
|
||||||
@param aMask The mask for a given event which should trigger the callback.
|
|
||||||
@param anExpiration The date for which the callback expires (not implemented).
|
|
||||||
@param aMode (not implemented).
|
|
||||||
@param shouldDequeue YES to remove the event from the queue after calling the callback,
|
|
||||||
NO to deliver it normally.
|
|
||||||
*/
|
|
||||||
- (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
|
|
||||||
{
|
{
|
||||||
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }, shouldDequeue));
|
_eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the last event recieved by your application.
|
|
||||||
*/
|
|
||||||
- (CPEvent)currentEvent
|
- (CPEvent)currentEvent
|
||||||
{
|
{
|
||||||
return _currentEvent;
|
return _currentEvent;
|
||||||
@@ -999,34 +850,23 @@ CPRunContinuesResponse = -1002;
|
|||||||
@param aSheet the window to display as a sheet
|
@param aSheet the window to display as a sheet
|
||||||
@param aWindow the window that will hold the sheet as a child
|
@param aWindow the window that will hold the sheet as a child
|
||||||
@param aModalDelegate
|
@param aModalDelegate
|
||||||
@param didEndSelector
|
@param aDidEndSelector
|
||||||
@param contextInfo
|
@param aContextInfo
|
||||||
*/
|
*/
|
||||||
- (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)didEndSelector contextInfo:(id)contextInfo
|
- (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo
|
||||||
{
|
{
|
||||||
if ([aWindow isSheet])
|
var styleMask = [aSheet styleMask];
|
||||||
|
if (!(styleMask & CPDocModalWindowMask))
|
||||||
{
|
{
|
||||||
[CPException raise:CPInternalInconsistencyException reason:@"The target window of beginSheet: cannot be a sheet"];
|
[CPException raise:CPInternalInconsistencyException reason:@"Currently only CPDocModalWindowMask style mask is supported for attached sheets"];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[aSheet._windowView _enableSheet:YES inWindow:aWindow];
|
[aWindow orderFront:self];
|
||||||
[aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:didEndSelector contextInfo:contextInfo];
|
[aSheet setPlatformWindow:[aWindow platformWindow]];
|
||||||
|
[aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Ends a sheet modal.
|
|
||||||
The following are predefined return codes:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPRunStoppedResponse
|
|
||||||
CPRunAbortedResponse
|
|
||||||
CPRunContinuesResponse
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
@param sheet - The window object (sheet) to dismiss.
|
|
||||||
@param returnCode - The return code to send to the delegate. You can use one of the return codes above or a custom value that you define.
|
|
||||||
*/
|
|
||||||
- (void)endSheet:(CPWindow)sheet returnCode:(int)returnCode
|
- (void)endSheet:(CPWindow)sheet returnCode:(int)returnCode
|
||||||
{
|
{
|
||||||
var count = [_windows count];
|
var count = [_windows count];
|
||||||
@@ -1036,77 +876,39 @@ CPRunContinuesResponse = -1002;
|
|||||||
var aWindow = [_windows objectAtIndex:count],
|
var aWindow = [_windows objectAtIndex:count],
|
||||||
context = aWindow._sheetContext;
|
context = aWindow._sheetContext;
|
||||||
|
|
||||||
if (context && context["sheet"] === sheet)
|
if (context != nil && context["sheet"] === sheet)
|
||||||
{
|
{
|
||||||
context["returnCode"] = returnCode;
|
context["returnCode"] = returnCode;
|
||||||
[aWindow _endSheet];
|
[aWindow _detachSheetWindow];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Ends a sheet and sends the return code "0".
|
|
||||||
@param sheet - The CPWindow object (sheet) that should be dismissed.
|
|
||||||
*/
|
|
||||||
- (void)endSheet:(CPWindow)sheet
|
- (void)endSheet:(CPWindow)sheet
|
||||||
{
|
{
|
||||||
// FIX ME: this is wrong: by Cocoa this should be: CPRunStoppedResponse.
|
|
||||||
[self endSheet:sheet returnCode:0];
|
[self endSheet:sheet returnCode:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns and array of slash seperated arugments to your application.
|
|
||||||
These values are pulled from your window location hash.
|
|
||||||
|
|
||||||
For exampled if your application loaded:
|
|
||||||
<pre>
|
|
||||||
index.html#280north/cappuccino/issues
|
|
||||||
</pre>
|
|
||||||
The follow array would be returned:
|
|
||||||
<pre>
|
|
||||||
["280north", "cappuccino", "issues"]
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
@return CPArray - The array of arguments.
|
|
||||||
*/
|
|
||||||
- (CPArray)arguments
|
- (CPArray)arguments
|
||||||
{
|
{
|
||||||
// FIXME This should probably not access the window object #if !PLATFORM(DOM), but the unit tests rely on it.
|
if (_fullArgsString !== window.location.hash)
|
||||||
if (window && window.location && _fullArgsString !== window.location.hash)
|
|
||||||
[self _reloadArguments];
|
[self _reloadArguments];
|
||||||
|
|
||||||
return _args;
|
return _args;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the arguments of your application.
|
|
||||||
That is, set the slash seperated values of an array as the window location hash.
|
|
||||||
|
|
||||||
For example if you pass an array:
|
|
||||||
<pre>
|
|
||||||
["280north", "cappuccino", "issues"]
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
The new window location would be
|
|
||||||
<pre>
|
|
||||||
index.html#280north/cappuccino/issues
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
@param args - An array of arguments.
|
|
||||||
*/
|
|
||||||
- (void)setArguments:(CPArray)args
|
- (void)setArguments:(CPArray)args
|
||||||
{
|
{
|
||||||
if (!args || args.length == 0)
|
if (!args || args.length == 0)
|
||||||
{
|
{
|
||||||
_args = [];
|
_args = [];
|
||||||
// Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along.
|
window.location.hash = @"#";
|
||||||
if (window && window.location)
|
|
||||||
window.location.hash = @"#";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![args isKindOfClass:CPArray])
|
if ([args class] != CPArray)
|
||||||
args = [CPArray arrayWithObject:args];
|
args = [CPArray arrayWithObject:args];
|
||||||
|
|
||||||
_args = args;
|
_args = args;
|
||||||
@@ -1117,15 +919,12 @@ CPRunContinuesResponse = -1002;
|
|||||||
|
|
||||||
var hash = [toEncode componentsJoinedByString:@"/"];
|
var hash = [toEncode componentsJoinedByString:@"/"];
|
||||||
|
|
||||||
// Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along.
|
window.location.hash = @"#" + hash;
|
||||||
if (window && window.location)
|
|
||||||
window.location.hash = @"#" + hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_reloadArguments
|
- (void)_reloadArguments
|
||||||
{
|
{
|
||||||
// FIXME This should probably not access the window object #if !PLATFORM(DOM), but the unit tests rely on it.
|
_fullArgsString = window.location.hash;
|
||||||
_fullArgsString = (window && window.location) ? window.location.hash : "";
|
|
||||||
|
|
||||||
if (_fullArgsString.length)
|
if (_fullArgsString.length)
|
||||||
{
|
{
|
||||||
@@ -1140,23 +939,6 @@ CPRunContinuesResponse = -1002;
|
|||||||
_args = [];
|
_args = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a dictionary of the window location named arguments.
|
|
||||||
For example if your location was:
|
|
||||||
<pre>
|
|
||||||
index.html?owner=280north&repo=cappuccino&type=issues
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
a CPDictionary with the keys:
|
|
||||||
<pre>
|
|
||||||
owner, repo, type
|
|
||||||
</pre>
|
|
||||||
and respective values:
|
|
||||||
<pre>
|
|
||||||
280north, cappuccino, issues
|
|
||||||
</pre>
|
|
||||||
Will be returned.
|
|
||||||
*/
|
|
||||||
- (CPDictionary)namedArguments
|
- (CPDictionary)namedArguments
|
||||||
{
|
{
|
||||||
return _namedArgs;
|
return _namedArgs;
|
||||||
@@ -1239,7 +1021,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
|
|
||||||
+ (CPString)defaultThemeName
|
+ (CPString)defaultThemeName
|
||||||
{
|
{
|
||||||
return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo2");
|
return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -1247,32 +1029,24 @@ CPRunContinuesResponse = -1002;
|
|||||||
var _CPModalSessionMake = function(aWindow, aStopCode)
|
var _CPModalSessionMake = function(aWindow, aStopCode)
|
||||||
{
|
{
|
||||||
return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil };
|
return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil };
|
||||||
};
|
}
|
||||||
|
|
||||||
var _CPEventListenerMake = function(anEventMask, aCallback, shouldDequeue)
|
var _CPEventListenerMake = function(anEventMask, aCallback)
|
||||||
{
|
{
|
||||||
return { _mask:anEventMask, _callback:aCallback, _dequeue:shouldDequeue };
|
return { _mask:anEventMask, _callback:aCallback };
|
||||||
};
|
}
|
||||||
|
|
||||||
// Make this a global for use in CPPlatformWindow+DOM.j.
|
// Make this a global for use in CPPlatformWindow+DOM.j.
|
||||||
_CPRunModalLoop = function(anEvent)
|
_CPRunModalLoop = function(anEvent)
|
||||||
{
|
{
|
||||||
[CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:YES];
|
[CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
|
||||||
|
|
||||||
var theWindow = [anEvent window],
|
var theWindow = [anEvent window],
|
||||||
modalSession = CPApp._currentSession;
|
modalSession = CPApp._currentSession;
|
||||||
|
|
||||||
/*
|
if (theWindow == modalSession._window || [theWindow worksWhenModal])
|
||||||
The special case for popovers here is not clear. In Cocoa the popover window does not respond YES to worksWhenModal, yet it works when there is a modal window. Maybe it starts its own modal session, but interaction with the original modal window seems to continue working as well. Regardless of correctness, this solution beats popovers not working at all from sheets.
|
|
||||||
*/
|
|
||||||
if (theWindow == modalSession._window ||
|
|
||||||
[theWindow worksWhenModal] ||
|
|
||||||
[theWindow attachedSheet] == modalSession._window || // -dw- allow modal parent of sheet to be repositioned
|
|
||||||
([theWindow isKindOfClass:_CPPopoverWindow] && [[theWindow targetView] window] === modalSession._window))
|
|
||||||
{
|
|
||||||
[theWindow sendEvent:anEvent];
|
[theWindow sendEvent:anEvent];
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Starts the GUI and Cappuccino frameworks. This function should be
|
Starts the GUI and Cappuccino frameworks. This function should be
|
||||||
@@ -1287,17 +1061,7 @@ function CPApplicationMain(args, namedArgs)
|
|||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
// hook to allow recorder, etc to manipulate things before starting AppKit
|
// hook to allow recorder, etc to manipulate things before starting AppKit
|
||||||
if (window.parent !== window && typeof window.parent._childAppIsStarting === "function")
|
if (window.parent !== window && typeof window.parent._childAppIsStarting === "function")
|
||||||
{
|
window.parent._childAppIsStarting(window);
|
||||||
try
|
|
||||||
{
|
|
||||||
window.parent._childAppIsStarting(window);
|
|
||||||
}
|
|
||||||
catch(err)
|
|
||||||
{
|
|
||||||
// This could happen if we're in an iframe without access to the parent frame.
|
|
||||||
CPLog.warn("Failed to call parent frame's _childAppIsStarting().");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var mainBundle = [CPBundle mainBundle],
|
var mainBundle = [CPBundle mainBundle],
|
||||||
@@ -1354,7 +1118,7 @@ var _CPAppBootstrapperActions = nil;
|
|||||||
var defaultThemeName = [CPApplication defaultThemeName],
|
var defaultThemeName = [CPApplication defaultThemeName],
|
||||||
themeURL = nil;
|
themeURL = nil;
|
||||||
|
|
||||||
if (defaultThemeName === @"Aristo" || defaultThemeName === @"Aristo2")
|
if (defaultThemeName === @"Aristo")
|
||||||
themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"];
|
themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"];
|
||||||
else
|
else
|
||||||
themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"];
|
themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"];
|
||||||
@@ -1381,7 +1145,7 @@ var _CPAppBootstrapperActions = nil;
|
|||||||
if (mainCibFile)
|
if (mainCibFile)
|
||||||
{
|
{
|
||||||
[mainBundle loadCibFile:mainCibFile
|
[mainBundle loadCibFile:mainCibFile
|
||||||
externalNameTable:@{ CPCibOwner: CPApp }
|
externalNameTable:[CPDictionary dictionaryWithObject:CPApp forKey:CPCibOwner]
|
||||||
loadDelegate:self];
|
loadDelegate:self];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
@@ -1399,25 +1163,26 @@ var _CPAppBootstrapperActions = nil;
|
|||||||
// FIXME: We should implement autoenabling.
|
// FIXME: We should implement autoenabling.
|
||||||
[mainMenu setAutoenablesItems:NO];
|
[mainMenu setAutoenablesItems:NO];
|
||||||
|
|
||||||
var newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"n"];
|
var bundle = [CPBundle bundleForClass:[CPApplication class]],
|
||||||
|
newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"n"];
|
||||||
|
|
||||||
[newMenuItem setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-new" forClass:_CPMenuView]];
|
[newMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/New.png"] size:CGSizeMake(16.0, 16.0)]];
|
||||||
[newMenuItem setAlternateImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-new" inState:CPThemeStateHighlighted forClass:_CPMenuView]];
|
[newMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/NewHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
|
||||||
|
|
||||||
[mainMenu addItem:newMenuItem];
|
[mainMenu addItem:newMenuItem];
|
||||||
|
|
||||||
var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"o"];
|
var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"o"];
|
||||||
|
|
||||||
[openMenuItem setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-open" forClass:_CPMenuView]];
|
[openMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Open.png"] size:CGSizeMake(16.0, 16.0)]];
|
||||||
[openMenuItem setAlternateImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-open" inState:CPThemeStateHighlighted forClass:_CPMenuView]];
|
[openMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/OpenHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
|
||||||
|
|
||||||
[mainMenu addItem:openMenuItem];
|
[mainMenu addItem:openMenuItem];
|
||||||
|
|
||||||
var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"],
|
var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"],
|
||||||
saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil];
|
saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil];
|
||||||
|
|
||||||
[saveMenuItem setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-save" forClass:_CPMenuView]];
|
[saveMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Save.png"] size:CGSizeMake(16.0, 16.0)]];
|
||||||
[saveMenuItem setAlternateImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-save" inState:CPThemeStateHighlighted forClass:_CPMenuView]];
|
[saveMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/SaveHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
|
||||||
|
|
||||||
[saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]];
|
[saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]];
|
||||||
[saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]];
|
[saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]];
|
||||||
@@ -1438,8 +1203,8 @@ var _CPAppBootstrapperActions = nil;
|
|||||||
[editMenu addItem:undoMenuItem];
|
[editMenu addItem:undoMenuItem];
|
||||||
[editMenu addItem:redoMenuItem];
|
[editMenu addItem:redoMenuItem];
|
||||||
|
|
||||||
[editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]];
|
[editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]],
|
||||||
[editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]];
|
[editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]],
|
||||||
[editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]];
|
[editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]];
|
||||||
|
|
||||||
[editMenuItem setSubmenu:editMenu];
|
[editMenuItem setSubmenu:editMenu];
|
||||||
@@ -1468,16 +1233,3 @@ var _CPAppBootstrapperActions = nil;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@implementation CPEvent (CPApplicationModifierFlags)
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the currently pressed modifier flags.
|
|
||||||
*/
|
|
||||||
+ (unsigned)modifierFlags
|
|
||||||
{
|
|
||||||
return CPEventModifierFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|||||||
+85
-652
File diff suppressed because it is too large
Load Diff
+8
-94
@@ -20,6 +20,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
|
||||||
* Copyright 2009 280 North, Inc.
|
* Copyright 2009 280 North, Inc.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@@ -37,7 +38,7 @@ var DefaultLineWidth = 1.0;
|
|||||||
@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 polygons 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
|
||||||
@@ -48,8 +49,6 @@ var DefaultLineWidth = 1.0;
|
|||||||
{
|
{
|
||||||
CGPath _path;
|
CGPath _path;
|
||||||
float _lineWidth;
|
float _lineWidth;
|
||||||
CPArray _lineDashes;
|
|
||||||
float _lineDashesPhase;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -63,11 +62,11 @@ var DefaultLineWidth = 1.0;
|
|||||||
/*!
|
/*!
|
||||||
Create a new CPBezierPath object initialized with an oval path drawn within a rectangular path.
|
Create a new CPBezierPath object initialized with an oval path drawn within a rectangular path.
|
||||||
*/
|
*/
|
||||||
+ (CPBezierPath)bezierPathWithOvalInRect:(CGRect)aRect
|
+ (CPBezierPath)bezierPathWithOvalInRect:(CGRect)rect
|
||||||
{
|
{
|
||||||
var path = [self bezierPath];
|
var path = [self bezierPath];
|
||||||
|
|
||||||
[path appendBezierPathWithOvalInRect:aRect];
|
[path appendBezierPathWithOvalInRect:rect];
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
@@ -75,20 +74,11 @@ var DefaultLineWidth = 1.0;
|
|||||||
/*!
|
/*!
|
||||||
Create a new CPBezierPath object initialized with a rectangular path.
|
Create a new CPBezierPath object initialized with a rectangular path.
|
||||||
*/
|
*/
|
||||||
+ (CPBezierPath)bezierPathWithRect:(CGRect)aRect
|
+ (CPBezierPath)bezierPathWithRect:(CGRect)rect
|
||||||
{
|
{
|
||||||
var path = [self bezierPath];
|
var path = [self bezierPath];
|
||||||
|
|
||||||
[path appendBezierPathWithRect:aRect];
|
[path appendBezierPathWithRect:rect];
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPBezierPath)bezierPathWithRoundedRect:(CGRect)aRect xRadius:(float)xRadius yRadius:(float)yRadius
|
|
||||||
{
|
|
||||||
var path = [self bezierPath];
|
|
||||||
|
|
||||||
[path appendBezierPathWithRoundedRect:aRect xRadius:xRadius yRadius:yRadius];
|
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
@@ -176,19 +166,6 @@ var DefaultLineWidth = 1.0;
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)bounds
|
|
||||||
{
|
|
||||||
// TODO: this should return this. The controlPointBounds is not a tight fit.
|
|
||||||
// return CGPathGetBoundingBox(_path);
|
|
||||||
|
|
||||||
return [self controlPointBounds];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)controlPointBounds
|
|
||||||
{
|
|
||||||
return CGPathGetBoundingBox(_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Create a line segment between the first and last points in the subpath, closing it.
|
Create a line segment between the first and last points in the subpath, closing it.
|
||||||
*/
|
*/
|
||||||
@@ -207,7 +184,7 @@ var DefaultLineWidth = 1.0;
|
|||||||
CGContextBeginPath(ctx);
|
CGContextBeginPath(ctx);
|
||||||
CGContextAddPath(ctx, _path);
|
CGContextAddPath(ctx, _path);
|
||||||
CGContextSetLineWidth(ctx, [self lineWidth]);
|
CGContextSetLineWidth(ctx, [self lineWidth]);
|
||||||
CGContextSetLineDash(ctx, _lineDashesPhase, _lineDashes);
|
CGContextClosePath(ctx);
|
||||||
CGContextStrokePath(ctx);
|
CGContextStrokePath(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,50 +198,10 @@ var DefaultLineWidth = 1.0;
|
|||||||
CGContextBeginPath(ctx);
|
CGContextBeginPath(ctx);
|
||||||
CGContextAddPath(ctx, _path);
|
CGContextAddPath(ctx, _path);
|
||||||
CGContextSetLineWidth(ctx, [self lineWidth]);
|
CGContextSetLineWidth(ctx, [self lineWidth]);
|
||||||
CGContextSetLineDash(ctx, _lineDashesPhase, _lineDashes);
|
|
||||||
CGContextClosePath(ctx);
|
CGContextClosePath(ctx);
|
||||||
CGContextFillPath(ctx);
|
CGContextFillPath(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Cocoa compatibility.
|
|
||||||
*/
|
|
||||||
- (void)getLineDash:(CPArrayRef)patternRef count:(NSInteger)count phase:(CGFloatRef)phaseRef
|
|
||||||
{
|
|
||||||
return [self getLineDash:patternRef phase:phaseRef];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Retrieve the line dash pattern and phase and write them into the provided references.
|
|
||||||
*/
|
|
||||||
- (void)getLineDash:(CPArrayRef)patternRef phase:(CGFloatRef)phaseRef
|
|
||||||
{
|
|
||||||
if (patternRef)
|
|
||||||
@deref(patternRef) = [_lineDashes copy];
|
|
||||||
if (phaseRef)
|
|
||||||
@deref(phaseRef) = _lineDashesPhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Cocoa compatibility.
|
|
||||||
*/
|
|
||||||
- (void)setLineDash:(CPArray)aPattern count:(NSInteger)count phase:(CGFloat)aPhase
|
|
||||||
{
|
|
||||||
[self setLineDash:aPattern phase:aPhase];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set stroke line dash pattern.
|
|
||||||
|
|
||||||
@param aPattern an array of stroke-skip lengths such as [2, 2, 4, 4]
|
|
||||||
@param aPhase amount of shift for the starting position of the first stroke
|
|
||||||
*/
|
|
||||||
- (void)setLineDash:(CPArray)aPattern phase:(CGFloat)aPhase
|
|
||||||
{
|
|
||||||
_lineDashes = aPattern;
|
|
||||||
_lineDashesPhase = aPhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Get the line width.
|
Get the line width.
|
||||||
*/
|
*/
|
||||||
@@ -337,19 +274,13 @@ var DefaultLineWidth = 1.0;
|
|||||||
CGPathAddPath(_path, nil, CGPathWithRoundedRectangleInRect(rect, xRadius, yRadius, YES, YES, YES, YES));
|
CGPathAddPath(_path, nil, CGPathWithRoundedRectangleInRect(rect, xRadius, yRadius, YES, YES, YES, YES));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)appendBezierPathWithArcFromPoint:(CGPoint)fromPoint toPoint:(CGPoint)toPoint radius:(float)radius
|
|
||||||
{
|
|
||||||
CGPathAddArcToPoint(_path, null, fromPoint.x, fromPoint.y, toPoint.x, toPoint.y, radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Append the contents of a CPBezierPath object.
|
Append the contents of a CPBezierPath object.
|
||||||
*/
|
*/
|
||||||
- (void)appendBezierPath:(CPBezierPath)other
|
- (void)appendBezierPath:(NSBezierPath *)other
|
||||||
{
|
{
|
||||||
CGPathAddPath(_path, nil, other._path);
|
CGPathAddPath(_path, nil, other._path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Remove all path elements; clears path.
|
Remove all path elements; clears path.
|
||||||
*/
|
*/
|
||||||
@@ -358,21 +289,4 @@ var DefaultLineWidth = 1.0;
|
|||||||
_path = CGPathCreateMutable();
|
_path = CGPathCreateMutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addClip
|
|
||||||
{
|
|
||||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
CGContextAddPath(ctx, _path);
|
|
||||||
CGContextClip(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setClip
|
|
||||||
{
|
|
||||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
CGContextBeginPath(ctx);
|
|
||||||
CGContextAddPath(ctx, _path);
|
|
||||||
CGContextClip(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+102
-371
@@ -20,13 +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 "CPTextField.j"
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
// CPBoxType
|
// CPBoxType
|
||||||
CPBoxPrimary = 0;
|
CPBoxPrimary = 0;
|
||||||
CPBoxSecondary = 1;
|
CPBoxSecondary = 1;
|
||||||
CPBoxSeparator = 2;
|
CPBoxSeperator = 2;
|
||||||
CPBoxOldStyle = 3;
|
CPBoxOldStyle = 3;
|
||||||
CPBoxCustom = 4;
|
CPBoxCustom = 4;
|
||||||
|
|
||||||
@@ -36,58 +35,19 @@ CPLineBorder = 1;
|
|||||||
CPBezelBorder = 2;
|
CPBezelBorder = 2;
|
||||||
CPGrooveBorder = 3;
|
CPGrooveBorder = 3;
|
||||||
|
|
||||||
// CPTitlePosition
|
|
||||||
CPNoTitle = 0;
|
|
||||||
CPAboveTop = 1;
|
|
||||||
CPAtTop = 2;
|
|
||||||
CPBelowTop = 3;
|
|
||||||
CPAboveBottom = 4;
|
|
||||||
CPAtBottom = 5;
|
|
||||||
CPBelowBottom = 6;
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
@class CPBox
|
|
||||||
|
|
||||||
A CPBox is a simple view which can display a border.
|
|
||||||
*/
|
|
||||||
@implementation CPBox : CPView
|
@implementation CPBox : CPView
|
||||||
{
|
{
|
||||||
CPBoxType _boxType;
|
CPBoxType _boxType;
|
||||||
CPBorderType _borderType;
|
CPBorderType _borderType;
|
||||||
|
|
||||||
|
CPColor _borderColor;
|
||||||
|
CPColor _fillColor;
|
||||||
|
|
||||||
|
float _cornerRadius;
|
||||||
|
float _borderWidth;
|
||||||
|
|
||||||
|
CPSize _contentMargin;
|
||||||
CPView _contentView;
|
CPView _contentView;
|
||||||
|
|
||||||
CPString _title @accessors(getter=title);
|
|
||||||
int _titlePosition @accessors(getter=titlePosition);
|
|
||||||
CPTextField _titleView;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if ([aBinding hasPrefix:CPDisplayPatternTitleBinding])
|
|
||||||
return [CPTitleWithPatternBinding class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return @"box";
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return @{
|
|
||||||
@"background-color": [CPNull null],
|
|
||||||
@"border-color": [CPNull null],
|
|
||||||
@"border-width": 1.0,
|
|
||||||
@"corner-radius": 3.0,
|
|
||||||
@"inner-shadow-offset": CGSizeMakeZero(),
|
|
||||||
@"inner-shadow-size": 6.0,
|
|
||||||
@"inner-shadow-color": [CPNull null],
|
|
||||||
@"content-margin": CGSizeMakeZero(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)boxEnclosingView:(CPView)aView
|
+ (id)boxEnclosingView:(CPView)aView
|
||||||
@@ -95,7 +55,6 @@ CPBelowBottom = 6;
|
|||||||
var box = [[self alloc] initWithFrame:CGRectMakeZero()],
|
var box = [[self alloc] initWithFrame:CGRectMakeZero()],
|
||||||
enclosingView = [aView superview];
|
enclosingView = [aView superview];
|
||||||
|
|
||||||
[box setAutoresizingMask:[aView autoresizingMask]];
|
|
||||||
[box setFrameFromContentFrame:[aView frame]];
|
[box setFrameFromContentFrame:[aView frame]];
|
||||||
|
|
||||||
[enclosingView replaceSubview:aView with:box];
|
[enclosingView replaceSubview:aView with:box];
|
||||||
@@ -105,16 +64,18 @@ CPBelowBottom = 6;
|
|||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frameRect
|
- (id)initWithFrame:(CPRect)frameRect
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:frameRect];
|
self = [super initWithFrame:frameRect];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_borderType = CPBezelBorder;
|
_borderType = CPBezelBorder;
|
||||||
|
_fillColor = [CPColor clearColor];
|
||||||
|
_borderColor = [CPColor blackColor];
|
||||||
|
|
||||||
_titlePosition = CPNoTitle;
|
_borderWidth = 1.0;
|
||||||
_titleView = [CPTextField labelWithTitle:@""];
|
_contentMargin = CGSizeMake(0.0, 0.0);
|
||||||
|
|
||||||
_contentView = [[CPView alloc] initWithFrame:[self bounds]];
|
_contentView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
@@ -128,46 +89,16 @@ CPBelowBottom = 6;
|
|||||||
|
|
||||||
// Configuring Boxes
|
// Configuring Boxes
|
||||||
|
|
||||||
/*!
|
- (CPRect)borderRect
|
||||||
Returns the receiver's border rectangle.
|
|
||||||
|
|
||||||
@return the border rectangle of the box
|
|
||||||
*/
|
|
||||||
- (CGRect)borderRect
|
|
||||||
{
|
{
|
||||||
return [self bounds];
|
return [self bounds];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the receiver's border type. Possible values are:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPNoBorder
|
|
||||||
CPLineBorder
|
|
||||||
CPBezelBorder
|
|
||||||
CPGrooveBorder
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
@return the border type of the box
|
|
||||||
*/
|
|
||||||
- (CPBorderType)borderType
|
- (CPBorderType)borderType
|
||||||
{
|
{
|
||||||
return _borderType;
|
return _borderType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the receiver's border type. Valid values are:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPNoBorder
|
|
||||||
CPLineBorder
|
|
||||||
CPBezelBorder
|
|
||||||
CPGrooveBorder
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
@param borderType the border type to use
|
|
||||||
*/
|
|
||||||
- (void)setBorderType:(CPBorderType)aBorderType
|
- (void)setBorderType:(CPBorderType)aBorderType
|
||||||
{
|
{
|
||||||
if (_borderType === aBorderType)
|
if (_borderType === aBorderType)
|
||||||
@@ -177,41 +108,11 @@ CPBelowBottom = 6;
|
|||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the receiver's box type. Possible values are:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPBoxPrimary
|
|
||||||
CPBoxSecondary
|
|
||||||
CPBoxSeparator
|
|
||||||
CPBoxOldStyle
|
|
||||||
CPBoxCustom
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
(In the current implementation, all values act the same except CPBoxSeparator.)
|
|
||||||
|
|
||||||
@return the box type of the box.
|
|
||||||
*/
|
|
||||||
- (CPBoxType)boxType
|
- (CPBoxType)boxType
|
||||||
{
|
{
|
||||||
return _boxType;
|
return _boxType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the receiver's box type. Valid values are:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPBoxPrimary
|
|
||||||
CPBoxSecondary
|
|
||||||
CPBoxSeparator
|
|
||||||
CPBoxOldStyle
|
|
||||||
CPBoxCustom
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
(In the current implementation, all values act the same except CPBoxSeparator.)
|
|
||||||
|
|
||||||
@param aBoxType the box type of the box.
|
|
||||||
*/
|
|
||||||
- (void)setBoxType:(CPBoxType)aBoxType
|
- (void)setBoxType:(CPBoxType)aBoxType
|
||||||
{
|
{
|
||||||
if (_boxType === aBoxType)
|
if (_boxType === aBoxType)
|
||||||
@@ -223,54 +124,58 @@ CPBelowBottom = 6;
|
|||||||
|
|
||||||
- (CPColor)borderColor
|
- (CPColor)borderColor
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"border-color"];
|
return _borderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setBorderColor:(CPColor)color
|
- (void)setBorderColor:(CPColor)color
|
||||||
{
|
{
|
||||||
if ([color isEqual:[self borderColor]])
|
if ([color isEqual:_borderColor])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self setValue:color forThemeAttribute:@"border-color"];
|
_borderColor = color;
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)borderWidth
|
- (float)borderWidth
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"border-width"];
|
return _borderWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setBorderWidth:(float)width
|
- (void)setBorderWidth:(float)width
|
||||||
{
|
{
|
||||||
if (width === [self borderWidth])
|
if (width === _borderWidth)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self setValue:width forThemeAttribute:@"border-width"];
|
_borderWidth = width;
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)cornerRadius
|
- (float)cornerRadius
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"corner-radius"];
|
return _cornerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCornerRadius:(float)radius
|
- (void)setCornerRadius:(float)radius
|
||||||
{
|
{
|
||||||
if (radius === [self cornerRadius])
|
if (radius === _cornerRadius)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self setValue:radius forThemeAttribute:@"corner-radius"];
|
_cornerRadius = radius;
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPColor)fillColor
|
- (CPColor)fillColor
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"background-color"];
|
return _fillColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFillColor:(CPColor)color
|
- (void)setFillColor:(CPColor)color
|
||||||
{
|
{
|
||||||
if ([color isEqual:[self fillColor]])
|
if ([color isEqual:_fillColor])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self setValue:color forThemeAttribute:@"background-color"];
|
_fillColor = color;
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPView)contentView
|
- (CPView)contentView
|
||||||
@@ -283,319 +188,138 @@ CPBelowBottom = 6;
|
|||||||
if (aView === _contentView)
|
if (aView === _contentView)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var borderWidth = [self borderWidth],
|
[aView setFrame:CGRectInset([self bounds], _contentMargin.width + _borderWidth, _contentMargin.height + _borderWidth)];
|
||||||
contentMargin = [self valueForThemeAttribute:@"content-margin"];
|
|
||||||
|
|
||||||
[aView setFrame:CGRectInset([self bounds], contentMargin.width + borderWidth, contentMargin.height + borderWidth)];
|
|
||||||
[aView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[aView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
|
[self replaceSubview:_contentView with:aView];
|
||||||
// A nil contentView is allowed (tested in Cocoa 2013-02-22).
|
|
||||||
if (!aView)
|
|
||||||
[_contentView removeFromSuperview];
|
|
||||||
else if (_contentView)
|
|
||||||
[self replaceSubview:_contentView with:aView];
|
|
||||||
else
|
|
||||||
[self addSubview:aView];
|
|
||||||
|
|
||||||
_contentView = aView;
|
_contentView = aView;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGSize)contentViewMargins
|
- (CPSize)contentViewMargins
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"content-margin"];
|
return _contentMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setContentViewMargins:(CGSize)size
|
- (void)setContentViewMargins:(CPSize)size
|
||||||
{
|
{
|
||||||
if (size.width < 0 || size.height < 0)
|
if (size.width < 0 || size.height < 0)
|
||||||
[CPException raise:CPGenericException reason:@"Margins must be positive"];
|
[CPException raise:CPGenericException reason:@"Margins must be positive"];
|
||||||
|
|
||||||
[self setValue:CGSizeMakeCopy(size) forThemeAttribute:@"content-margin"];
|
_contentMargin = CGSizeMakeCopy(size);
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFrameFromContentFrame:(CGRect)aRect
|
- (void)setFrameFromContentFrame:(CPRect)aRect
|
||||||
{
|
{
|
||||||
var offset = [self _titleHeightOffset],
|
[self setFrame:CGRectInset(aRect, -(_contentMargin.width + _borderWidth), -(_contentMargin.height + _borderWidth))];
|
||||||
borderWidth = [self borderWidth],
|
|
||||||
contentMargin = [self valueForThemeAttribute:@"content-margin"];
|
|
||||||
|
|
||||||
[self setFrame:CGRectInset(aRect, -(contentMargin.width + borderWidth), -(contentMargin.height + offset[0] + borderWidth))];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTitle:(CPString)aTitle
|
|
||||||
{
|
|
||||||
if (aTitle == _title)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_title = aTitle;
|
|
||||||
|
|
||||||
[self _manageTitlePositioning];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTitlePosition:(int)aTitlePotisition
|
|
||||||
{
|
|
||||||
if (aTitlePotisition == _titlePosition)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_titlePosition = aTitlePotisition;
|
|
||||||
|
|
||||||
[self _manageTitlePositioning];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFont)titleFont
|
|
||||||
{
|
|
||||||
return [_titleView font];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTitleFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
[_titleView setFont:aFont];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Return the text field used to display the receiver's title.
|
|
||||||
|
|
||||||
This is the Cappuccino equivalent to the `titleCell` method.
|
|
||||||
*/
|
|
||||||
- (CPTextField)titleView
|
|
||||||
{
|
|
||||||
return _titleView;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_manageTitlePositioning
|
|
||||||
{
|
|
||||||
if (_titlePosition == CPNoTitle)
|
|
||||||
{
|
|
||||||
[_titleView removeFromSuperview];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[_titleView setStringValue:_title];
|
|
||||||
[_titleView sizeToFit];
|
|
||||||
[self addSubview:_titleView];
|
|
||||||
|
|
||||||
switch (_titlePosition)
|
|
||||||
{
|
|
||||||
case CPAtTop:
|
|
||||||
case CPAboveTop:
|
|
||||||
case CPBelowTop:
|
|
||||||
[_titleView setFrameOrigin:CGPointMake(5.0, 0.0)];
|
|
||||||
[_titleView setAutoresizingMask:CPViewNotSizable];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPAboveBottom:
|
|
||||||
case CPAtBottom:
|
|
||||||
case CPBelowBottom:
|
|
||||||
var h = [_titleView frameSize].height;
|
|
||||||
[_titleView setFrameOrigin:CGPointMake(5.0, [self frameSize].height - h)];
|
|
||||||
[_titleView setAutoresizingMask:CPViewMinYMargin];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self sizeToFit];
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)sizeToFit
|
- (void)sizeToFit
|
||||||
{
|
{
|
||||||
var contentFrame = [_contentView frame],
|
var contentFrame = [_contentView frame];
|
||||||
offset = [self _titleHeightOffset],
|
|
||||||
contentMargin = [self valueForThemeAttribute:@"content-margin"];
|
|
||||||
|
|
||||||
if (!contentFrame)
|
[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;
|
return;
|
||||||
|
|
||||||
[_contentView setFrameOrigin:CGPointMake(contentMargin.width, contentMargin.height + offset[1])];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)_titleHeightOffset
|
|
||||||
{
|
|
||||||
if (_titlePosition == CPNoTitle)
|
|
||||||
return [0.0, 0.0];
|
|
||||||
|
|
||||||
switch (_titlePosition)
|
|
||||||
{
|
|
||||||
case CPAtTop:
|
|
||||||
return [[_titleView frameSize].height, [_titleView frameSize].height];
|
|
||||||
|
|
||||||
case CPAtBottom:
|
|
||||||
return [[_titleView frameSize].height, 0.0];
|
|
||||||
|
|
||||||
default:
|
|
||||||
return [0.0, 0.0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forKey:(CPString)aKey
|
|
||||||
{
|
|
||||||
if (aKey === CPDisplayPatternTitleBinding)
|
|
||||||
[self setTitle:aValue || @""];
|
|
||||||
else
|
|
||||||
[super setValue:aValue forKey:aKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)rect
|
|
||||||
{
|
|
||||||
var bounds = [self bounds];
|
var bounds = [self bounds];
|
||||||
|
|
||||||
switch (_boxType)
|
switch (_boxType)
|
||||||
{
|
{
|
||||||
case CPBoxSeparator:
|
case CPBoxSeperator:
|
||||||
// NSBox does not include a horizontal flag for the separator type. We have to determine
|
// NSBox does not include a horitontal flag for the seperator type. We have to determine
|
||||||
// the type of separator to draw by the width and height of the frame.
|
// the type of seperator to draw by the width and height of the frame.
|
||||||
if (CGRectGetWidth(bounds) === 5.0)
|
if (CGRectGetWidth(bounds) === 5.0)
|
||||||
return [self _drawVerticalSeparatorInRect:bounds];
|
return [self _drawVerticalSeperatorInRect:bounds];
|
||||||
else if (CGRectGetHeight(bounds) === 5.0)
|
else if (CGRectGetHeight(bounds) === 5.0)
|
||||||
return [self _drawHorizontalSeparatorInRect:bounds];
|
return [self _drawHorizontalSeperatorInRect:bounds];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_titlePosition == CPAtTop)
|
|
||||||
{
|
|
||||||
bounds.origin.y += [_titleView frameSize].height;
|
|
||||||
bounds.size.height -= [_titleView frameSize].height;
|
|
||||||
}
|
|
||||||
if (_titlePosition == CPAtBottom)
|
|
||||||
{
|
|
||||||
bounds.size.height -= [_titleView frameSize].height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Primary or secondary type boxes always draw the same way, unless they are CPNoBorder.
|
|
||||||
if ((_boxType === CPBoxPrimary || _boxType === CPBoxSecondary) && _borderType !== CPNoBorder)
|
|
||||||
{
|
|
||||||
[self _drawPrimaryBorderInRect:bounds];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_borderType)
|
switch (_borderType)
|
||||||
{
|
{
|
||||||
case CPBezelBorder:
|
case CPBezelBorder:
|
||||||
[self _drawBezelBorderInRect:bounds];
|
[self _drawBezelBorderInRect:bounds];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPGrooveBorder:
|
default:
|
||||||
case CPLineBorder:
|
case CPLineBorder:
|
||||||
[self _drawLineBorderInRect:bounds];
|
[self _drawLineBorderInRect:bounds];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPNoBorder:
|
|
||||||
[self _drawNoBorderInRect:bounds];
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_drawHorizontalSeparatorInRect:(CGRect)aRect
|
- (void)_drawHorizontalSeperatorInRect:(CGRect)aRect
|
||||||
{
|
{
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, [self borderColor]);
|
CGContextSetStrokeColor(context, [self borderColor]);
|
||||||
CGContextSetLineWidth(context, 1.0);
|
CGContextSetLineWidth(context, 1.0);
|
||||||
|
|
||||||
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMidY(aRect));
|
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
|
||||||
CGContextAddLineToPoint(context, CGRectGetWidth(aRect), CGRectGetMidY(aRect));
|
CGContextAddLineToPoint(context, CGRectGetWidth(aRect), CGRectGetMinY(aRect));
|
||||||
CGContextStrokePath(context);
|
CGContextStrokePath(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_drawVerticalSeparatorInRect:(CGRect)aRect
|
- (void)_drawVerticalSeperatorInRect:(CGRect)aRect
|
||||||
{
|
{
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, [self borderColor]);
|
CGContextSetStrokeColor(context, [self borderColor]);
|
||||||
CGContextSetLineWidth(context, 1.0);
|
CGContextSetLineWidth(context, 1.0);
|
||||||
|
|
||||||
CGContextMoveToPoint(context, CGRectGetMidX(aRect), CGRectGetMinY(aRect));
|
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
|
||||||
CGContextAddLineToPoint(context, CGRectGetMidX(aRect), CGRectGetHeight(aRect));
|
CGContextAddLineToPoint(context, CGRectGetMinX(aRect), CGRectGetHeight(aRect));
|
||||||
CGContextStrokePath(context);
|
CGContextStrokePath(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_drawLineBorderInRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
cornerRadius = [self cornerRadius],
|
|
||||||
borderWidth = [self borderWidth];
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_drawBezelBorderInRect:(CGRect)aRect
|
- (void)_drawBezelBorderInRect:(CGRect)aRect
|
||||||
{
|
{
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||||
cornerRadius = [self cornerRadius],
|
sides = [CPMinYEdge, CPMaxXEdge, CPMaxYEdge, CPMinXEdge],
|
||||||
borderWidth = [self borderWidth],
|
sideGray = 190.0 / 255.0,
|
||||||
shadowOffset = [self valueForThemeAttribute:@"inner-shadow-offset"],
|
grays = [142.0 / 255.0, sideGray, sideGray, sideGray],
|
||||||
shadowSize = [self valueForThemeAttribute:@"inner-shadow-size"],
|
borderWidth = _borderWidth;
|
||||||
shadowColor = [self valueForThemeAttribute:@"inner-shadow-color"];
|
|
||||||
|
|
||||||
var baseRect = aRect;
|
while (borderWidth--)
|
||||||
aRect = CGRectInset(aRect, borderWidth / 2.0, borderWidth / 2.0);
|
aRect = CPDrawTiledRects(aRect, aRect, sides, grays);
|
||||||
|
|
||||||
CGContextSaveGState(context);
|
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, [self borderColor]);
|
|
||||||
CGContextSetLineWidth(context, borderWidth);
|
|
||||||
CGContextSetFillColor(context, [self fillColor]);
|
|
||||||
CGContextFillRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
|
|
||||||
CGContextStrokeRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
|
|
||||||
|
|
||||||
CGContextRestoreGState(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_drawPrimaryBorderInRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
// Draw the "primary" style CPBox.
|
|
||||||
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
cornerRadius = [self cornerRadius],
|
|
||||||
borderWidth = [self borderWidth],
|
|
||||||
shadowOffset = [self valueForThemeAttribute:@"inner-shadow-offset"],
|
|
||||||
shadowSize = [self valueForThemeAttribute:@"inner-shadow-size"],
|
|
||||||
shadowColor = [self valueForThemeAttribute:@"inner-shadow-color"],
|
|
||||||
baseRect = aRect;
|
|
||||||
|
|
||||||
aRect = CGRectInset(aRect, borderWidth / 2.0, borderWidth / 2.0);
|
|
||||||
|
|
||||||
CGContextSaveGState(context);
|
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, [self borderColor]);
|
|
||||||
CGContextSetLineWidth(context, borderWidth);
|
|
||||||
CGContextSetFillColor(context, [self fillColor]);
|
|
||||||
CGContextFillRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
|
|
||||||
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
// Note we can't use the 0.5 inset rectangle when setting up clipping. The clipping has to be
|
|
||||||
// on integer coordinates for this to look right in Chrome.
|
|
||||||
CGContextAddPath(context, CGPathWithRoundedRectangleInRect(baseRect, cornerRadius, cornerRadius, YES, YES, YES, YES));
|
|
||||||
CGContextClip(context);
|
|
||||||
CGContextSetShadowWithColor(context, shadowOffset, shadowSize, shadowColor);
|
|
||||||
CGContextStrokeRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
|
|
||||||
|
|
||||||
CGContextRestoreGState(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_drawNoBorderInRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
CGContextSetFillColor(context, [self fillColor]);
|
CGContextSetFillColor(context, [self fillColor]);
|
||||||
CGContextFillRect(context, aRect);
|
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
|
@end
|
||||||
|
|
||||||
var CPBoxTypeKey = @"CPBoxTypeKey",
|
var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||||
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
|
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
|
||||||
CPBoxTitle = @"CPBoxTitle",
|
CPBoxBorderColorKey = @"CPBoxBorderColorKey",
|
||||||
CPBoxTitlePosition = @"CPBoxTitlePosition",
|
CPBoxFillColorKey = @"CPBoxFillColorKey",
|
||||||
CPBoxTitleView = @"CPBoxTitleView";
|
CPBoxCornerRadiusKey = @"CPBoxCornerRadiusKey",
|
||||||
|
CPBoxBorderWidthKey = @"CPBoxBorderWidthKey",
|
||||||
|
CPBoxContentMarginKey = @"CPBoxContentMarginKey";
|
||||||
|
|
||||||
@implementation CPBox (CPCoding)
|
@implementation CPBox (CPCoding)
|
||||||
|
|
||||||
@@ -608,16 +332,18 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
|||||||
_boxType = [aCoder decodeIntForKey:CPBoxTypeKey];
|
_boxType = [aCoder decodeIntForKey:CPBoxTypeKey];
|
||||||
_borderType = [aCoder decodeIntForKey:CPBoxBorderTypeKey];
|
_borderType = [aCoder decodeIntForKey:CPBoxBorderTypeKey];
|
||||||
|
|
||||||
_title = [aCoder decodeObjectForKey:CPBoxTitle];
|
_borderColor = [aCoder decodeObjectForKey:CPBoxBorderColorKey];
|
||||||
_titlePosition = [aCoder decodeIntForKey:CPBoxTitlePosition];
|
_fillColor = [aCoder decodeObjectForKey:CPBoxFillColorKey];
|
||||||
_titleView = [aCoder decodeObjectForKey:CPBoxTitleView] || [CPTextField labelWithTitle:_title];
|
|
||||||
|
_cornerRadius = [aCoder decodeFloatForKey:CPBoxCornerRadiusKey];
|
||||||
|
_borderWidth = [aCoder decodeFloatForKey:CPBoxBorderWidthKey];
|
||||||
|
|
||||||
|
_contentMargin = [aCoder decodeSizeForKey:CPBoxContentMarginKey];
|
||||||
|
|
||||||
_contentView = [self subviews][0];
|
_contentView = [self subviews][0];
|
||||||
|
|
||||||
[self setAutoresizesSubviews:YES];
|
[self setAutoresizesSubviews:YES];
|
||||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
|
|
||||||
[self _manageTitlePositioning];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -629,9 +355,14 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
|||||||
|
|
||||||
[aCoder encodeInt:_boxType forKey:CPBoxTypeKey];
|
[aCoder encodeInt:_boxType forKey:CPBoxTypeKey];
|
||||||
[aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey];
|
[aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey];
|
||||||
[aCoder encodeObject:_title forKey:CPBoxTitle];
|
|
||||||
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePosition];
|
[aCoder encodeObject:_borderColor forKey:CPBoxBorderColorKey];
|
||||||
[aCoder encodeObject:_titleView forKey:CPBoxTitleView];
|
[aCoder encodeObject:_fillColor forKey:CPBoxFillColorKey];
|
||||||
|
|
||||||
|
[aCoder encodeFloat:_cornerRadius forKey:CPBoxCornerRadiusKey];
|
||||||
|
[aCoder encodeFloat:_borderWidth forKey:CPBoxBorderWidthKey];
|
||||||
|
|
||||||
|
[aCoder encodeSize:_contentMargin forKey:CPBoxContentMarginKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+91
-146
@@ -24,16 +24,15 @@
|
|||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
@import "CPScrollView.j"
|
|
||||||
@import "CPTableView.j"
|
@import "CPTableView.j"
|
||||||
@import "CPTextField.j"
|
@import "CPTextField.j"
|
||||||
|
@import "CPScrollView.j"
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPBrowser
|
@class CPBrowser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@implementation CPBrowser : CPControl
|
@implementation CPBrowser : CPControl
|
||||||
{
|
{
|
||||||
id _delegate;
|
id _delegate;
|
||||||
@@ -66,65 +65,60 @@
|
|||||||
CPArray _columnWidths;
|
CPArray _columnWidths;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPImage)branchImage
|
||||||
{
|
{
|
||||||
return "browser";
|
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]]
|
||||||
|
pathForResource:"browser-leaf.png"]
|
||||||
|
size:CGSizeMake(9,9)];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)themeAttributes
|
+ (CPImage)highlightedBranchImage
|
||||||
{
|
{
|
||||||
return @{
|
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]]
|
||||||
@"image-control-resize": [CPNull null],
|
pathForResource:"browser-leaf-highlighted.png"]
|
||||||
@"image-control-leaf": [CPNull null],
|
size:CGSizeMake(9,9)];
|
||||||
@"image-control-leaf-pressed": [CPNull null]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
if (self = [super initWithFrame:aFrame])
|
if (self = [super initWithFrame:aFrame])
|
||||||
{
|
{
|
||||||
[self _init];
|
_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;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
_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];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPrototypeView:(CPView)aPrototypeView
|
- (void)setPrototypeView:(CPView)aPrototypeView
|
||||||
{
|
{
|
||||||
_prototypeView = [CPKeyedUnarchiver unarchiveObjectWithData:
|
_prototypeView = [CPKeyedUnarchiver unarchiveObjectWithData:
|
||||||
@@ -179,11 +173,6 @@
|
|||||||
var oldValue = _tableViews.length - 1,
|
var oldValue = _tableViews.length - 1,
|
||||||
indexPlusOne = columnIndex + 1; // unloads all later columns.
|
indexPlusOne = columnIndex + 1; // unloads all later columns.
|
||||||
|
|
||||||
if (columnIndex > 0)
|
|
||||||
[_tableViews[columnIndex - 1] setNeedsDisplay:YES];
|
|
||||||
|
|
||||||
[_tableViews[columnIndex] setNeedsDisplay:YES];
|
|
||||||
|
|
||||||
[[_tableViews.slice(indexPlusOne) valueForKey:"enclosingScrollView"]
|
[[_tableViews.slice(indexPlusOne) valueForKey:"enclosingScrollView"]
|
||||||
makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||||
|
|
||||||
@@ -264,7 +253,7 @@
|
|||||||
var column = [[CPTableColumn alloc] initWithIdentifier:@"Image"],
|
var column = [[CPTableColumn alloc] initWithIdentifier:@"Image"],
|
||||||
view = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
view = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
[view setImageScaling:CPImageScaleProportionallyDown];
|
[view setImageScaling:CPScaleProportionally];
|
||||||
|
|
||||||
[column setDataView:view];
|
[column setDataView:view];
|
||||||
[column setResizingMask:CPTableColumnNoResizing];
|
[column setResizingMask:CPTableColumnNoResizing];
|
||||||
@@ -282,8 +271,8 @@
|
|||||||
var column = [[CPTableColumn alloc] initWithIdentifier:@"Leaf"],
|
var column = [[CPTableColumn alloc] initWithIdentifier:@"Leaf"],
|
||||||
view = [[_CPBrowserLeafView alloc] initWithFrame:CGRectMakeZero()];
|
view = [[_CPBrowserLeafView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
[view setBranchImage:[self valueForThemeAttribute:@"image-control-leaf"]];
|
[view setBranchImage:[[self class] branchImage]];
|
||||||
[view setHighlightedBranchImage:[self valueForThemeAttribute:@"image-control-leaf-pressed"]];
|
[view setHighlightedBranchImage:[[self class] highlightedBranchImage]];
|
||||||
|
|
||||||
[column setDataView:view];
|
[column setDataView:view];
|
||||||
[column setResizingMask:CPTableColumnNoResizing];
|
[column setResizingMask:CPTableColumnNoResizing];
|
||||||
@@ -310,7 +299,7 @@
|
|||||||
tableHeight = CGRectGetHeight([tableView bounds]);
|
tableHeight = CGRectGetHeight([tableView bounds]);
|
||||||
|
|
||||||
[[tableView tableColumnWithIdentifier:"Image"] setWidth:_imageWidth];
|
[[tableView tableColumnWithIdentifier:"Image"] setWidth:_imageWidth];
|
||||||
[[tableView tableColumnWithIdentifier:"Content"] setWidth:[self columnContentWidthForColumnWidth:width]];
|
[[tableView tableColumnWithIdentifier:"Content"] setWidth:width - (_leafWidth + _delegateSupportsImages ? _imageWidth : 0) - scrollerWidth - scrollerWidth];
|
||||||
[[tableView tableColumnWithIdentifier:"Leaf"] setWidth:_leafWidth];
|
[[tableView tableColumnWithIdentifier:"Leaf"] setWidth:_leafWidth];
|
||||||
|
|
||||||
[tableView setRowHeight:_rowHeight];
|
[tableView setRowHeight:_rowHeight];
|
||||||
@@ -431,40 +420,23 @@
|
|||||||
|
|
||||||
- (void)keyDown:(CPEvent)anEvent
|
- (void)keyDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var key = [anEvent charactersIgnoringModifiers],
|
var column = [self selectedColumn];
|
||||||
column = [self selectedColumn];
|
if (column === -1)
|
||||||
|
|
||||||
if (column === CPNotFound)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (key === CPLeftArrowFunctionKey || key === CPRightArrowFunctionKey)
|
[_tableViews[column] keyDown:anEvent];
|
||||||
{
|
|
||||||
if (key === CPLeftArrowFunctionKey)
|
|
||||||
{
|
|
||||||
var previousColumn = column - 1,
|
|
||||||
selectedRow = [self selectedRowInColumn:previousColumn];
|
|
||||||
|
|
||||||
[self selectRow:selectedRow inColumn:previousColumn];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[self selectRow:0 inColumn:column + 1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[_tableViews[column] keyDown:anEvent];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SIZING
|
// SIZING
|
||||||
|
|
||||||
- (float)columnContentWidthForColumnWidth:(float)aWidth
|
- (float)columnContentWidthForColumnWidth:(float)aWidth
|
||||||
{
|
{
|
||||||
var columnSpacing = [_tableViews[0] intercellSpacing].width;
|
return aWidth - (_leafWidth + _delegateSupportsImages ? _imageWidth : 0) - [CPScroller scrollerWidth];
|
||||||
return aWidth - (_leafWidth + columnSpacing + (_delegateSupportsImages ? _imageWidth + columnSpacing : 0)) - columnSpacing - [CPScroller scrollerWidth];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)columnWidthForColumnContentWidth:(float)aWidth
|
- (float)columnWidthForColumnContentWidth:(float)aWidth
|
||||||
{
|
{
|
||||||
var columnSpacing = [_tableViews[0] intercellSpacing].width;
|
return aWidth + (_leafWidth + _delegateSupportsImages ? _imageWidth : 0) + [CPScroller scrollerWidth];
|
||||||
return aWidth + (_leafWidth + columnSpacing + (_delegateSupportsImages ? _imageWidth + columnSpacing: 0)) + columnSpacing + [CPScroller scrollerWidth];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setImageWidth:(float)aWidth
|
- (void)setImageWidth:(float)aWidth
|
||||||
@@ -678,52 +650,7 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPBrowser (CPCoding)
|
var _CPBrowserResizeControlBackgroundImage = nil;
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
[self _init];
|
|
||||||
|
|
||||||
_allowsEmptySelection = [aCoder decodeBoolForKey:@"CPBrowserAllowsEmptySelectionKey"];
|
|
||||||
_allowsMultipleSelection = [aCoder decodeBoolForKey:@"CPBrowserAllowsMultipleSelectionKey"];
|
|
||||||
_prototypeView = [aCoder decodeObjectForKey:@"CPBrowserPrototypeViewKey"];
|
|
||||||
_rowHeight = [aCoder decodeFloatForKey:@"CPBrowserRowHeightKey"];
|
|
||||||
_imageWidth = [aCoder decodeFloatForKey:@"CPBrowserImageWidthKey"];
|
|
||||||
_minColumnWidth = [aCoder decodeFloatForKey:@"CPBrowserMinColumnWidthKey"];
|
|
||||||
_columnWidths = [aCoder decodeObjectForKey:@"CPBrowserColumnWidthsKey"];
|
|
||||||
|
|
||||||
[self setDelegate:[aCoder decodeObjectForKey:@"CPBrowserDelegateKey"]];
|
|
||||||
[self setAutohidesScroller:[aCoder decodeBoolForKey:@"CPBrowserAutohidesScrollerKey"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
// Don't encode the subviews, they're transient and will be recreated from data.
|
|
||||||
var actualSubviews = _subviews;
|
|
||||||
_subviews = [];
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
_subviews = actualSubviews;
|
|
||||||
|
|
||||||
[aCoder encodeBool:[self autohidesScroller] forKey:@"CPBrowserAutohidesScrollerKey"];
|
|
||||||
[aCoder encodeBool:_allowsEmptySelection forKey:@"CPBrowserAllowsEmptySelectionKey"];
|
|
||||||
[aCoder encodeBool:_allowsMultipleSelection forKey:@"CPBrowserAllowsMultipleSelectionKey"];
|
|
||||||
[aCoder encodeObject:_delegate forKey:@"CPBrowserDelegateKey"];
|
|
||||||
[aCoder encodeObject:_prototypeView forKey:@"CPBrowserPrototypeViewKey"];
|
|
||||||
[aCoder encodeFloat:_rowHeight forKey:@"CPBrowserRowHeightKey"];
|
|
||||||
[aCoder encodeFloat:_imageWidth forKey:@"CPBrowserImageWidthKey"];
|
|
||||||
[aCoder encodeFloat:_minColumnWidth forKey:@"CPBrowserMinColumnWidthKey"];
|
|
||||||
[aCoder encodeObject:_columnWidths forKey:@"CPBrowserColumnWidthsKey"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPBrowserResizeControl : CPView
|
@implementation _CPBrowserResizeControl : CPView
|
||||||
{
|
{
|
||||||
@@ -733,14 +660,22 @@
|
|||||||
unsigned _width;
|
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
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
if (self = [super initWithFrame:aFrame])
|
if (self = [super initWithFrame:aFrame])
|
||||||
{
|
[self setBackgroundColor:[CPColor colorWithPatternImage:[[self class] backgroundImage]]];
|
||||||
var browser = [[CPBrowser alloc] init];
|
|
||||||
[self setBackgroundColor:[CPColor colorWithPatternImage:[browser valueForThemeAttribute:@"image-control-resize"]]];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -825,26 +760,18 @@
|
|||||||
return _browser;
|
return _browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (BOOL)_isFocused
|
|
||||||
{
|
|
||||||
return ([super _isFocused] || [_browser tableViewInColumn:[_browser selectedColumn]] === self);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint
|
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint
|
||||||
{
|
{
|
||||||
return [_browser canDragRowsWithIndexes:rowIndexes inColumn:[_browser columnOfTableView:self] withEvent:[CPApp currentEvent]];
|
return [_browser canDragRowsWithIndexes:rowIndexes inColumn:[_browser columnOfTableView:self] withEvent:[CPApp currentEvent]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
|
- (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] ||
|
return [_browser draggingImageForRowsWithIndexes:dragRows inColumn:[_browser columnOfTableView:self] withEvent:dragEvent offset:dragImageOffset] ||
|
||||||
[super dragImageForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragImageOffset];
|
[super dragImageForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragImageOffset];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPView)dragViewForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CGPoint)dragViewOffset
|
- (CPView)dragViewForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPoint)dragViewOffset
|
||||||
{
|
{
|
||||||
var count = theTableColumns.length;
|
var count = theTableColumns.length;
|
||||||
while (count--)
|
while (count--)
|
||||||
@@ -857,6 +784,30 @@
|
|||||||
[super dragViewForRowsWithIndexes:dragRows tableColumns:theTableColumns event: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
|
@end
|
||||||
|
|
||||||
@@ -896,12 +847,6 @@
|
|||||||
[_browser _column:_index clickedRow:[selectedIndexes count] === 1 ? [selectedIndexes firstIndex] : -1];
|
[_browser _column:_index clickedRow:[selectedIndexes count] === 1 ? [selectedIndexes firstIndex] : -1];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)tableViewSelectionDidChange:(CPNotification)aNotification
|
|
||||||
{
|
|
||||||
var selectedIndexes = [[aNotification object] selectedRowIndexes];
|
|
||||||
[_browser selectRowIndexes:selectedIndexes inColumn:_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)childAtIndex:(unsigned)index
|
- (id)childAtIndex:(unsigned)index
|
||||||
{
|
{
|
||||||
return [_delegate browser:_browser child:index ofItem:_item];
|
return [_delegate browser:_browser child:index ofItem:_item];
|
||||||
@@ -983,7 +928,7 @@
|
|||||||
isHighlighted = [self themeState] & CPThemeStateSelectedDataView;
|
isHighlighted = [self themeState] & CPThemeStateSelectedDataView;
|
||||||
|
|
||||||
[imageView setImage: _isLeaf ? (isHighlighted ? _highlightedBranchImage : _branchImage) : nil];
|
[imageView setImage: _isLeaf ? (isHighlighted ? _highlightedBranchImage : _branchImage) : nil];
|
||||||
[imageView setImageScaling:CPImageScaleNone];
|
[imageView setImageScaling:CPScaleNone];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
|
|||||||
+94
-375
@@ -25,26 +25,24 @@
|
|||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPStringDrawing.j"
|
@import "CPStringDrawing.j"
|
||||||
@import "CPText.j"
|
|
||||||
@import "CPWindow_Constants.j"
|
|
||||||
|
|
||||||
/* @group CPBezelStyle */
|
/* @group CPBezelStyle */
|
||||||
|
|
||||||
// IB style
|
CPRoundedBezelStyle = 1;
|
||||||
CPRoundedBezelStyle = 1; // Push
|
CPRegularSquareBezelStyle = 2;
|
||||||
CPRegularSquareBezelStyle = 2; // Bevel
|
|
||||||
CPThickSquareBezelStyle = 3;
|
CPThickSquareBezelStyle = 3;
|
||||||
CPThickerSquareBezelStyle = 4;
|
CPThickerSquareBezelStyle = 4;
|
||||||
CPDisclosureBezelStyle = 5; // Disclosure triangle
|
CPDisclosureBezelStyle = 5;
|
||||||
CPShadowlessSquareBezelStyle = 6; // Square
|
CPShadowlessSquareBezelStyle = 6;
|
||||||
CPCircularBezelStyle = 7; // Round
|
CPCircularBezelStyle = 7;
|
||||||
CPTexturedSquareBezelStyle = 8; // Textured
|
CPTexturedSquareBezelStyle = 8;
|
||||||
CPHelpButtonBezelStyle = 9; // Help
|
CPHelpButtonBezelStyle = 9;
|
||||||
CPSmallSquareBezelStyle = 10; // Gradient
|
CPSmallSquareBezelStyle = 10;
|
||||||
CPTexturedRoundedBezelStyle = 11; // Round Textured
|
CPTexturedRoundedBezelStyle = 11;
|
||||||
CPRoundRectBezelStyle = 12; // Round Rect
|
CPRoundRectBezelStyle = 12;
|
||||||
CPRecessedBezelStyle = 13; // Recessed
|
CPRecessedBezelStyle = 13;
|
||||||
CPRoundedDisclosureBezelStyle = 14; // Disclosure
|
CPRoundedDisclosureBezelStyle = 14;
|
||||||
CPHUDBezelStyle = -1;
|
CPHUDBezelStyle = -1;
|
||||||
|
|
||||||
|
|
||||||
@@ -60,11 +58,11 @@ CPMomentaryPushInButton = 7;
|
|||||||
CPMomentaryPushButton = 0;
|
CPMomentaryPushButton = 0;
|
||||||
CPMomentaryLight = 7;
|
CPMomentaryLight = 7;
|
||||||
|
|
||||||
CPNoButtonMask = 0;
|
CPNoButtonMask = 0;
|
||||||
CPContentsButtonMask = 1;
|
CPContentsButtonMask = 1;
|
||||||
CPPushInButtonMask = 2;
|
CPPushInButtonMask = 2;
|
||||||
CPGrayButtonMask = 4;
|
CPGrayButtonMask = 4;
|
||||||
CPBackgroundButtonMask = 8;
|
CPBackgroundButtonMask = 8;
|
||||||
|
|
||||||
CPNoCellMask = CPNoButtonMask;
|
CPNoCellMask = CPNoButtonMask;
|
||||||
CPContentsCellMask = CPContentsButtonMask;
|
CPContentsCellMask = CPContentsButtonMask;
|
||||||
@@ -72,19 +70,10 @@ CPPushInCellMask = CPPushInButtonMask;
|
|||||||
CPChangeGrayCellMask = CPGrayButtonMask;
|
CPChangeGrayCellMask = CPGrayButtonMask;
|
||||||
CPChangeBackgroundCellMask = CPBackgroundButtonMask;
|
CPChangeBackgroundCellMask = CPBackgroundButtonMask;
|
||||||
|
|
||||||
CPButtonStateMixed = CPThemeState("mixed");
|
CPButtonStateMixed = CPThemeState("mixed");
|
||||||
CPButtonStateBezelStyleRounded = CPThemeState("rounded");
|
|
||||||
|
|
||||||
// add all future correspondance between bezel styles and theme state here.
|
CPButtonDefaultHeight = 24.0;
|
||||||
var CPButtonBezelStyleStateMap = @{
|
|
||||||
CPRoundedBezelStyle: CPButtonStateBezelStyleRounded,
|
|
||||||
CPRoundRectBezelStyle: [CPNull null],
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @cond IGNORE
|
|
||||||
CPButtonDefaultHeight = 25.0;
|
|
||||||
CPButtonImageOffset = 3.0;
|
CPButtonImageOffset = 3.0;
|
||||||
/// @endcond
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -111,21 +100,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
CPString _keyEquivalent;
|
CPString _keyEquivalent;
|
||||||
unsigned _keyEquivalentModifierMask;
|
unsigned _keyEquivalentModifierMask;
|
||||||
|
|
||||||
CPTimer _continuousDelayTimer;
|
|
||||||
CPTimer _continuousTimer;
|
|
||||||
float _periodicDelay;
|
|
||||||
float _periodicInterval;
|
|
||||||
|
|
||||||
BOOL _isTracking;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding === CPTargetBinding || [aBinding hasPrefix:CPArgumentBinding])
|
|
||||||
return [CPActionBinding class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)buttonWithTitle:(CPString)aTitle
|
+ (id)buttonWithTitle:(CPString)aTitle
|
||||||
@@ -151,20 +125,10 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return @{
|
return [CPDictionary dictionaryWithObjects:[[CPNull null], 0.0, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null]]
|
||||||
@"image": [CPNull null],
|
forKeys:[@"image", @"image-offset", @"bezel-inset", @"content-inset", @"bezel-color"]];
|
||||||
@"image-offset": 0.0,
|
|
||||||
@"bezel-inset": CGInsetMakeZero(),
|
|
||||||
@"content-inset": CGInsetMakeZero(),
|
|
||||||
@"bezel-color": [CPNull null],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes and returns a newly allocated CPButton object with a specified frame rectangle.
|
|
||||||
@param aFrame The frame rectangle for the created button object.
|
|
||||||
@return An initialized CPView object or nil if the object couldn't be created.
|
|
||||||
*/
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
@@ -175,35 +139,23 @@ CPButtonImageOffset = 3.0;
|
|||||||
[self setValue:CPCenterTextAlignment forThemeAttribute:@"alignment"];
|
[self setValue:CPCenterTextAlignment forThemeAttribute:@"alignment"];
|
||||||
[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:CPImageScaleNone forThemeAttribute:@"image-scaling"];
|
[self setValue:CPScaleNone forThemeAttribute:@"image-scaling"];
|
||||||
|
|
||||||
[self setBezelStyle:CPRoundRectBezelStyle];
|
_controlSize = CPRegularControlSize;
|
||||||
|
|
||||||
|
_keyEquivalent = @"";
|
||||||
|
_keyEquivalentModifierMask = 0;
|
||||||
|
|
||||||
|
// [self setBezelStyle:CPRoundRectBezelStyle];
|
||||||
[self setBordered:YES];
|
[self setBordered:YES];
|
||||||
|
|
||||||
[self _init];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
_controlSize = CPRegularControlSize;
|
|
||||||
|
|
||||||
_keyEquivalent = @"";
|
|
||||||
_keyEquivalentModifierMask = 0;
|
|
||||||
|
|
||||||
// Continuous button defaults.
|
|
||||||
_periodicInterval = 0.05;
|
|
||||||
_periodicDelay = 0.5;
|
|
||||||
|
|
||||||
[self setButtonType:CPMomentaryPushInButton];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting the state
|
// Setting the state
|
||||||
/*!
|
/*!
|
||||||
Returns a Boolean value indicating whether the button allows a mixed state.
|
Returns \c YES if the button has a 'mixed' state in addition to on and off.
|
||||||
@return \c YES if the button has a 'mixed' state in addition to on and off.
|
|
||||||
*/
|
*/
|
||||||
- (BOOL)allowsMixedState
|
- (BOOL)allowsMixedState
|
||||||
{
|
{
|
||||||
@@ -227,21 +179,21 @@ CPButtonImageOffset = 3.0;
|
|||||||
[self setState:CPOnState];
|
[self setState:CPOnState];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the value of the button using an Objective-J object.
|
|
||||||
@param anObjectValue The value of the button interpreted as an Objective-J object.
|
|
||||||
*/
|
|
||||||
- (void)setObjectValue:(id)anObjectValue
|
- (void)setObjectValue:(id)anObjectValue
|
||||||
{
|
{
|
||||||
if (!anObjectValue || anObjectValue === @"" || ([anObjectValue intValue] === 0))
|
if (!anObjectValue || anObjectValue === @"" || ([anObjectValue intValue] === 0))
|
||||||
anObjectValue = CPOffState;
|
anObjectValue = CPOffState;
|
||||||
|
|
||||||
else if (![anObjectValue isKindOfClass:[CPNumber class]])
|
else if (![anObjectValue isKindOfClass:[CPNumber class]])
|
||||||
anObjectValue = CPOnState;
|
anObjectValue = CPOnState;
|
||||||
else if (anObjectValue >= CPOnState)
|
|
||||||
anObjectValue = CPOnState;
|
else if (anObjectValue > CPOnState)
|
||||||
|
anObjectValue = CPOnState
|
||||||
|
|
||||||
else if (anObjectValue < CPOffState)
|
else if (anObjectValue < CPOffState)
|
||||||
if ([self allowsMixedState])
|
if ([self allowsMixedState])
|
||||||
anObjectValue = CPMixedState;
|
anObjectValue = CPMixedState;
|
||||||
|
|
||||||
else
|
else
|
||||||
anObjectValue = CPOnState;
|
anObjectValue = CPOnState;
|
||||||
|
|
||||||
@@ -249,36 +201,18 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
switch ([self objectValue])
|
switch ([self objectValue])
|
||||||
{
|
{
|
||||||
case CPMixedState:
|
case CPMixedState: [self unsetThemeState:CPThemeStateSelected];
|
||||||
[self unsetThemeState:CPThemeStateSelected];
|
[self setThemeState:CPButtonStateMixed];
|
||||||
[self setThemeState:CPButtonStateMixed];
|
break;
|
||||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
|
||||||
[self setThemeState:CPThemeStateHighlighted];
|
|
||||||
else
|
|
||||||
[self unsetThemeState:CPThemeStateHighlighted];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPOnState:
|
case CPOnState: [self unsetThemeState:CPButtonStateMixed];
|
||||||
[self unsetThemeState:CPButtonStateMixed];
|
[self setThemeState:CPThemeStateSelected];
|
||||||
[self setThemeState:CPThemeStateSelected];
|
break;
|
||||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
|
||||||
[self setThemeState:CPThemeStateHighlighted];
|
|
||||||
else
|
|
||||||
[self unsetThemeState:CPThemeStateHighlighted];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPOffState:
|
case CPOffState: [self unsetThemeState:CPThemeStateSelected | CPButtonStateMixed];
|
||||||
[self unsetThemeState:CPThemeStateSelected | CPButtonStateMixed | CPThemeStateHighlighted];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the button's next state.
|
|
||||||
@return The button's state. A button can have two or three states.
|
|
||||||
If it has two, this value is either \c CPOffState (the normal or unpressed state)
|
|
||||||
or \c CPOnState (the alternate or pressed state).
|
|
||||||
If it has three, this value can be \c CPOnState (the feature is in effect everywhere), \c CPOffState (the feature is in effect nowhere), or \c CPMixedState (the feature is in effect somewhere).
|
|
||||||
*/
|
|
||||||
- (CPInteger)nextState
|
- (CPInteger)nextState
|
||||||
{
|
{
|
||||||
if ([self allowsMixedState])
|
if ([self allowsMixedState])
|
||||||
@@ -291,16 +225,8 @@ CPButtonImageOffset = 3.0;
|
|||||||
return 1 - [self state];
|
return 1 - [self state];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the button's next state to \c aState.
|
|
||||||
@param aState Possible states are any of the CPButton globals:
|
|
||||||
\c CPOffState, \c CPOnState, \c CPMixedState
|
|
||||||
*/
|
|
||||||
- (void)setNextState
|
- (void)setNextState
|
||||||
{
|
{
|
||||||
if ([self infoForBinding:CPValueBinding])
|
|
||||||
[self setAllowsMixedState:NO];
|
|
||||||
|
|
||||||
[self setState:[self nextState]];
|
[self setState:[self nextState]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,11 +248,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
return [self intValue];
|
return [self intValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the title displayed by the button when in its normal state.
|
|
||||||
@param aTitle The string to set as the button's title. This title is always shown on buttons
|
|
||||||
that don’t use their alternate contents when highlighting or displaying their alternate state.
|
|
||||||
*/
|
|
||||||
- (void)setTitle:(CPString)aTitle
|
- (void)setTitle:(CPString)aTitle
|
||||||
{
|
{
|
||||||
if (_title === aTitle)
|
if (_title === aTitle)
|
||||||
@@ -338,11 +259,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the title displayed on the button when it’s in its normal state.
|
|
||||||
@return The title displayed on the receiver when it’s in its normal state
|
|
||||||
or the empty string if the button doesn’t display a title.
|
|
||||||
*/
|
|
||||||
- (CPString)title
|
- (CPString)title
|
||||||
{
|
{
|
||||||
return _title;
|
return _title;
|
||||||
@@ -403,19 +319,11 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
- (void)setShowsStateBy:(CPInteger)aMask
|
- (void)setShowsStateBy:(CPInteger)aMask
|
||||||
{
|
{
|
||||||
// CPPushInCellMask cannot be set for showsStateBy.
|
|
||||||
aMask &= ~CPPushInCellMask;
|
|
||||||
|
|
||||||
if (_showsStateBy === aMask)
|
if (_showsStateBy === aMask)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_showsStateBy = aMask;
|
_showsStateBy = aMask;
|
||||||
|
|
||||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask) && [self state] != CPOffState)
|
|
||||||
[self setThemeState:CPThemeStateHighlighted];
|
|
||||||
else
|
|
||||||
[self unsetThemeState:CPThemeStateHighlighted];
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
@@ -439,20 +347,15 @@ CPButtonImageOffset = 3.0;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPInteger)highlightsBy
|
|
||||||
{
|
|
||||||
return _highlightsBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setButtonType:(CPButtonType)aButtonType
|
- (void)setButtonType:(CPButtonType)aButtonType
|
||||||
{
|
{
|
||||||
switch (aButtonType)
|
switch (aButtonType)
|
||||||
{
|
{
|
||||||
case CPMomentaryLightButton: [self setHighlightsBy:CPChangeGrayCellMask | CPChangeBackgroundCellMask];
|
case CPMomentaryLightButton: [self setHighlightsBy:CPChangeBackgroundCellMask];
|
||||||
[self setShowsStateBy:CPNoCellMask];
|
[self setShowsStateBy:CPNoCellMask];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPMomentaryPushInButton: [self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask | CPChangeBackgroundCellMask];
|
case CPMomentaryPushInButton: [self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask];
|
||||||
[self setShowsStateBy:CPNoCellMask];
|
[self setShowsStateBy:CPNoCellMask];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -460,12 +363,12 @@ CPButtonImageOffset = 3.0;
|
|||||||
[self setShowsStateBy:CPNoCellMask];
|
[self setShowsStateBy:CPNoCellMask];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPPushOnPushOffButton: [self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask | CPChangeBackgroundCellMask];
|
case CPPushOnPushOffButton: [self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask];
|
||||||
[self setShowsStateBy:CPChangeBackgroundCellMask | CPChangeGrayCellMask];
|
[self setShowsStateBy:CPChangeBackgroundCellMask];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPOnOffButton: [self setHighlightsBy:CPChangeGrayCellMask | CPChangeBackgroundCellMask];
|
case CPOnOffButton: [self setHighlightsBy:CPChangeBackgroundCellMask];
|
||||||
[self setShowsStateBy:CPChangeGrayCellMask | CPChangeBackgroundCellMask];
|
[self setShowsStateBy:CPChangeBackgroundCellMask];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPToggleButton: [self setHighlightsBy:CPPushInCellMask | CPContentsCellMask];
|
case CPToggleButton: [self setHighlightsBy:CPPushInCellMask | CPContentsCellMask];
|
||||||
@@ -506,113 +409,66 @@ CPButtonImageOffset = 3.0;
|
|||||||
return _imageDimsWhenDisabled;
|
return _imageDimsWhenDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPeriodicDelay:(float)aDelay interval:(float)anInterval
|
|
||||||
{
|
|
||||||
_periodicDelay = aDelay;
|
|
||||||
_periodicInterval = anInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
if ([self isContinuous])
|
|
||||||
{
|
|
||||||
_continuousDelayTimer = [CPTimer scheduledTimerWithTimeInterval:_periodicDelay callback: function()
|
|
||||||
{
|
|
||||||
if (!_continuousTimer)
|
|
||||||
_continuousTimer = [CPTimer scheduledTimerWithTimeInterval:_periodicInterval target:self selector:@selector(onContinousEvent:) userInfo:anEvent repeats:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
repeats:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
[super mouseDown:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)onContinousEvent:(CPTimer)aTimer
|
|
||||||
{
|
|
||||||
if (_target && _action && [_target respondsToSelector:_action])
|
|
||||||
[_target performSelector:_action withObject:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
_isTracking = YES;
|
[self highlight:YES];
|
||||||
|
|
||||||
var startedTracking = [super startTrackingAt:aPoint];
|
return [super startTrackingAt:aPoint];
|
||||||
|
|
||||||
if (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))
|
|
||||||
{
|
|
||||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
|
||||||
[self highlight:[self state] == CPOffState];
|
|
||||||
else
|
|
||||||
[self highlight:YES];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
|
||||||
[self highlight:[self state] != CPOffState];
|
|
||||||
else
|
|
||||||
[self highlight:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
return startedTracking;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
||||||
{
|
{
|
||||||
_isTracking = NO;
|
[self highlight:NO];
|
||||||
|
|
||||||
|
[super stopTracking:lastPoint at:aPoint mouseIsUp:mouseIsUp];
|
||||||
|
|
||||||
if (mouseIsUp && CGRectContainsPoint([self bounds], aPoint))
|
if (mouseIsUp && CGRectContainsPoint([self bounds], aPoint))
|
||||||
[self setNextState];
|
[self setNextState];
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
|
||||||
[self highlight:[self state] != CPOffState];
|
|
||||||
else
|
|
||||||
[self highlight:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
[self invalidateTimers];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateTimers
|
|
||||||
{
|
|
||||||
if (_continuousTimer)
|
|
||||||
{
|
|
||||||
[_continuousTimer invalidate];
|
|
||||||
_continuousTimer = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_continuousDelayTimer)
|
|
||||||
{
|
|
||||||
[_continuousDelayTimer invalidate];
|
|
||||||
_continuousDelayTimer = nil;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)contentRectForBounds:(CGRect)bounds
|
- (CGRect)contentRectForBounds:(CGRect)bounds
|
||||||
{
|
{
|
||||||
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
||||||
|
|
||||||
return CGRectInsetByInset(bounds, contentInset);
|
if (_CGInsetIsEmpty(contentInset))
|
||||||
|
return bounds;
|
||||||
|
|
||||||
|
bounds = _CGRectMakeCopy(bounds);
|
||||||
|
bounds.origin.x += contentInset.left;
|
||||||
|
bounds.origin.y += contentInset.top;
|
||||||
|
bounds.size.width -= contentInset.left + contentInset.right;
|
||||||
|
bounds.size.height -= contentInset.top + contentInset.bottom;
|
||||||
|
|
||||||
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)bezelRectForBounds:(CGRect)bounds
|
- (CGRect)bezelRectForBounds:(CGRect)bounds
|
||||||
{
|
{
|
||||||
// Is this necessary? The theme itself can just change its inset to a zero inset when !CPThemeStateBordered.
|
|
||||||
if (![self isBordered])
|
if (![self isBordered])
|
||||||
return bounds;
|
return _CGRectMakeZero();
|
||||||
|
|
||||||
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
|
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
|
||||||
|
|
||||||
return CGRectInsetByInset(bounds, bezelInset);
|
if (_CGInsetIsEmpty(bezelInset))
|
||||||
|
return bounds;
|
||||||
|
|
||||||
|
bounds = _CGRectMakeCopy(bounds);
|
||||||
|
bounds.origin.x += bezelInset.left;
|
||||||
|
bounds.origin.y += bezelInset.top;
|
||||||
|
bounds.size.width -= bezelInset.left + bezelInset.right;
|
||||||
|
bounds.size.height -= bezelInset.top + bezelInset.bottom;
|
||||||
|
|
||||||
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGSize)_minimumFrameSize
|
/*!
|
||||||
|
Adjust the size of the button to fit the title and surrounding button image.
|
||||||
|
*/
|
||||||
|
- (void)sizeToFit
|
||||||
{
|
{
|
||||||
var size = CGSizeMakeZero(),
|
[self layoutSubviews];
|
||||||
|
|
||||||
|
var size,
|
||||||
contentView = [self ephemeralSubviewNamed:@"content-view"];
|
contentView = [self ephemeralSubviewNamed:@"content-view"];
|
||||||
|
|
||||||
if (contentView)
|
if (contentView)
|
||||||
@@ -636,20 +492,7 @@ CPButtonImageOffset = 3.0;
|
|||||||
if (maxSize.height >= 0.0)
|
if (maxSize.height >= 0.0)
|
||||||
size.height = MIN(size.height, maxSize.height);
|
size.height = MIN(size.height, maxSize.height);
|
||||||
|
|
||||||
return size;
|
[self setFrameSize:size];
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Adjust the size of the button to fit the title and surrounding button image.
|
|
||||||
*/
|
|
||||||
- (void)sizeToFit
|
|
||||||
{
|
|
||||||
[self layoutSubviews];
|
|
||||||
|
|
||||||
[self setFrameSize:[self _minimumFrameSize]];
|
|
||||||
|
|
||||||
if ([self ephemeralSubviewNamed:@"content-view"])
|
|
||||||
[self layoutSubviews];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||||
@@ -667,14 +510,14 @@ CPButtonImageOffset = 3.0;
|
|||||||
{
|
{
|
||||||
if (aName === "bezel-view")
|
if (aName === "bezel-view")
|
||||||
{
|
{
|
||||||
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()];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
@@ -691,51 +534,8 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
if (contentView)
|
if (contentView)
|
||||||
{
|
{
|
||||||
var title = nil,
|
[contentView setText:([self hasThemeState:CPThemeStateHighlighted] && _alternateTitle) ? _alternateTitle : _title];
|
||||||
image = nil;
|
[contentView setImage:[self currentValueForThemeAttribute:@"image"]];
|
||||||
|
|
||||||
if (_isTracking)
|
|
||||||
{
|
|
||||||
if (_highlightsBy & CPContentsCellMask)
|
|
||||||
{
|
|
||||||
if (_showsStateBy & CPContentsCellMask)
|
|
||||||
{
|
|
||||||
title = ([self state] == CPOffState && _alternateTitle) ? _alternateTitle : _title;
|
|
||||||
image = ([self state] == CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
title = [self alternateTitle];
|
|
||||||
image = [self alternateImage];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (_showsStateBy & CPContentsCellMask)
|
|
||||||
{
|
|
||||||
title = ([self state] != CPOffState && _alternateTitle) ? _alternateTitle : _title;
|
|
||||||
image = ([self state] != CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
title = _title;
|
|
||||||
image = [self image];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_showsStateBy & CPContentsCellMask)
|
|
||||||
{
|
|
||||||
title = ([self state] != CPOffState && _alternateTitle) ? _alternateTitle : _title;
|
|
||||||
image = ([self state] != CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
title = _title;
|
|
||||||
image = [self image];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[contentView setText:title];
|
|
||||||
[contentView setImage:image];
|
|
||||||
[contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]];
|
[contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]];
|
||||||
|
|
||||||
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
|
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
|
||||||
@@ -834,75 +634,19 @@ CPButtonImageOffset = 3.0;
|
|||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[self performClick:nil];
|
[self performClick:nil];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Perform a click on the receiver.
|
|
||||||
|
|
||||||
@param sender - The sender object
|
|
||||||
*/
|
|
||||||
- (void)performClick:(id)sender
|
|
||||||
{
|
|
||||||
// This is slightly different from [super performClick:] in that the highlight behaviour is dependent on
|
|
||||||
// highlightsBy and showsStateBy.
|
|
||||||
if (![self isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self setState:[self nextState]];
|
|
||||||
|
|
||||||
var shouldHighlight = NO;
|
|
||||||
|
|
||||||
if (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))
|
|
||||||
{
|
|
||||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
|
||||||
shouldHighlight = [self state] == CPOffState;
|
|
||||||
else
|
|
||||||
shouldHighlight = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self highlight:shouldHighlight];
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (shouldHighlight)
|
|
||||||
[CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unhighlightButtonTimerDidFinish:) userInfo:nil repeats:NO];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPButton (NS)
|
@implementation CPButton (NS)
|
||||||
|
|
||||||
- (void)setBezelStyle:(unsigned)aBezelStyle
|
- (void)setBezelStyle:(unsigned)aBezelStyle
|
||||||
{
|
{
|
||||||
if (aBezelStyle === _bezelStyle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var currentState = [CPButtonBezelStyleStateMap objectForKey:_bezelStyle],
|
|
||||||
newState = [CPButtonBezelStyleStateMap objectForKey:aBezelStyle];
|
|
||||||
|
|
||||||
if (currentState)
|
|
||||||
[self unsetThemeState:currentState];
|
|
||||||
|
|
||||||
if (newState)
|
|
||||||
[self setThemeState:newState];
|
|
||||||
|
|
||||||
_bezelStyle = aBezelStyle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned)bezelStyle
|
- (unsigned)bezelStyle
|
||||||
{
|
{
|
||||||
return _bezelStyle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -917,11 +661,7 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
|||||||
CPButtonImageDimsWhenDisabledKey = @"CPButtonImageDimsWhenDisabledKey",
|
CPButtonImageDimsWhenDisabledKey = @"CPButtonImageDimsWhenDisabledKey",
|
||||||
CPButtonImagePositionKey = @"CPButtonImagePositionKey",
|
CPButtonImagePositionKey = @"CPButtonImagePositionKey",
|
||||||
CPButtonKeyEquivalentKey = @"CPButtonKeyEquivalentKey",
|
CPButtonKeyEquivalentKey = @"CPButtonKeyEquivalentKey",
|
||||||
CPButtonKeyEquivalentMaskKey = @"CPButtonKeyEquivalentMaskKey",
|
CPButtonKeyEquivalentMaskKey = @"CPButtonKeyEquivalentMaskKey";
|
||||||
CPButtonPeriodicDelayKey = @"CPButtonPeriodicDelayKey",
|
|
||||||
CPButtonPeriodicIntervalKey = @"CPButtonPeriodicIntervalKey",
|
|
||||||
CPButtonHighlightsByKey = @"CPButtonHighlightsByKey",
|
|
||||||
CPButtonShowsStateByKey = @"CPButtonShowsStateByKey";
|
|
||||||
|
|
||||||
@implementation CPButton (CPCoding)
|
@implementation CPButton (CPCoding)
|
||||||
|
|
||||||
@@ -935,24 +675,13 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
[self _init];
|
_controlSize = CPRegularControlSize;
|
||||||
|
|
||||||
_title = [aCoder decodeObjectForKey:CPButtonTitleKey];
|
_title = [aCoder decodeObjectForKey:CPButtonTitleKey];
|
||||||
_alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey];
|
_alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey];
|
||||||
_allowsMixedState = [aCoder decodeBoolForKey:CPButtonAllowsMixedStateKey];
|
|
||||||
|
|
||||||
if ([aCoder containsValueForKey:CPButtonHighlightsByKey])
|
if ([aCoder containsValueForKey:CPButtonAllowsMixedStateKey])
|
||||||
{
|
_allowsMixedState = [aCoder decodeBoolForKey:CPButtonAllowsMixedStateKey];
|
||||||
// If one exists, assume both do.
|
|
||||||
_highlightsBy = [aCoder decodeIntForKey:CPButtonHighlightsByKey];
|
|
||||||
_showsStateBy = [aCoder decodeIntForKey:CPButtonShowsStateByKey];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Backwards compatibility: if this CPButton was encoded before coding of
|
|
||||||
// highlightsBy and showsStateBy were added, we should just use the
|
|
||||||
// default values from _init rather than overwriting with 0, 0.
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]];
|
[self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]];
|
||||||
|
|
||||||
@@ -962,12 +691,6 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
|||||||
if ([aCoder containsValueForKey:CPButtonKeyEquivalentKey])
|
if ([aCoder containsValueForKey:CPButtonKeyEquivalentKey])
|
||||||
[self setKeyEquivalent:CFData.decodeBase64ToUtf16String([aCoder decodeObjectForKey:CPButtonKeyEquivalentKey])];
|
[self setKeyEquivalent:CFData.decodeBase64ToUtf16String([aCoder decodeObjectForKey:CPButtonKeyEquivalentKey])];
|
||||||
|
|
||||||
if ([aCoder containsValueForKey:CPButtonPeriodicDelayKey])
|
|
||||||
_periodicDelay = [aCoder decodeObjectForKey:CPButtonPeriodicDelayKey];
|
|
||||||
|
|
||||||
if ([aCoder containsValueForKey:CPButtonPeriodicIntervalKey])
|
|
||||||
_periodicInterval = [aCoder decodeObjectForKey:CPButtonPeriodicIntervalKey];
|
|
||||||
|
|
||||||
_keyEquivalentModifierMask = [aCoder decodeIntForKey:CPButtonKeyEquivalentMaskKey];
|
_keyEquivalentModifierMask = [aCoder decodeIntForKey:CPButtonKeyEquivalentMaskKey];
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
@@ -984,16 +707,12 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
|||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
[self invalidateTimers];
|
|
||||||
|
|
||||||
[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:_allowsMixedState forKey:CPButtonAllowsMixedStateKey];
|
||||||
|
|
||||||
[aCoder encodeInt:_highlightsBy forKey:CPButtonHighlightsByKey];
|
|
||||||
[aCoder encodeInt:_showsStateBy forKey:CPButtonShowsStateByKey];
|
|
||||||
|
|
||||||
[aCoder encodeBool:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey];
|
[aCoder encodeBool:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey];
|
||||||
[aCoder encodeInt:[self imagePosition] forKey:CPButtonImagePositionKey];
|
[aCoder encodeInt:[self imagePosition] forKey:CPButtonImagePositionKey];
|
||||||
|
|
||||||
@@ -1001,9 +720,9 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
|||||||
[aCoder encodeObject:CFData.encodeBase64Utf16String(_keyEquivalent) forKey:CPButtonKeyEquivalentKey];
|
[aCoder encodeObject:CFData.encodeBase64Utf16String(_keyEquivalent) forKey:CPButtonKeyEquivalentKey];
|
||||||
|
|
||||||
[aCoder encodeInt:_keyEquivalentModifierMask forKey:CPButtonKeyEquivalentMaskKey];
|
[aCoder encodeInt:_keyEquivalentModifierMask forKey:CPButtonKeyEquivalentMaskKey];
|
||||||
|
|
||||||
[aCoder encodeObject:_periodicDelay forKey:CPButtonPeriodicDelayKey];
|
|
||||||
[aCoder encodeObject:_periodicInterval forKey:CPButtonPeriodicIntervalKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@import "CPCheckBox.j"
|
||||||
|
@import "CPRadio.j"
|
||||||
|
|||||||
+5
-40
@@ -1,31 +1,5 @@
|
|||||||
/*
|
|
||||||
* CPButtonBar.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 "CPView.j"
|
@import "CPView.j"
|
||||||
@import "CPWindow_Constants.j"
|
|
||||||
|
|
||||||
@class CPSplitView
|
|
||||||
|
|
||||||
@global CPPopUpButtonStatePullsDown
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPButtonBar : CPView
|
@implementation CPButtonBar : CPView
|
||||||
@@ -38,7 +12,7 @@
|
|||||||
+ (id)plusButton
|
+ (id)plusButton
|
||||||
{
|
{
|
||||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
||||||
image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-plus" forClass:[CPButtonBar class]];
|
image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"plus_button.png"] size:CGSizeMake(11, 12)];
|
||||||
|
|
||||||
[button setBordered:NO];
|
[button setBordered:NO];
|
||||||
[button setImage:image];
|
[button setImage:image];
|
||||||
@@ -50,7 +24,7 @@
|
|||||||
+ (id)minusButton
|
+ (id)minusButton
|
||||||
{
|
{
|
||||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
||||||
image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-minus" forClass:[CPButtonBar class]];
|
image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"minus_button.png"] size:CGSizeMake(11, 4)];
|
||||||
|
|
||||||
[button setBordered:NO];
|
[button setBordered:NO];
|
||||||
[button setImage:image];
|
[button setImage:image];
|
||||||
@@ -62,7 +36,7 @@
|
|||||||
+ (id)actionPopupButton
|
+ (id)actionPopupButton
|
||||||
{
|
{
|
||||||
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
||||||
image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-action" forClass:[CPButtonBar class]];
|
image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"action_button.png"] size:CGSizeMake(22, 14)];
|
||||||
|
|
||||||
[button addItemWithTitle:nil];
|
[button addItemWithTitle:nil];
|
||||||
[[button lastItem] setImage:image];
|
[[button lastItem] setImage:image];
|
||||||
@@ -81,17 +55,8 @@
|
|||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return @{
|
return [CPDictionary dictionaryWithObjects:[CGInsetMake(0.0, 0.0, 0.0, 0.0), CGSizeMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null]]
|
||||||
@"resize-control-inset": CGInsetMake(0.0, 0.0, 0.0, 0.0),
|
forKeys:[@"resize-control-inset", @"resize-control-size", @"resize-control-color", @"bezel-color", @"button-bezel-color", @"button-text-color"]];
|
||||||
@"resize-control-size": CGSizeMakeZero(),
|
|
||||||
@"resize-control-color": [CPNull null],
|
|
||||||
@"bezel-color": [CPNull null],
|
|
||||||
@"button-bezel-color": [CPNull null],
|
|
||||||
@"button-text-color": [CPNull null],
|
|
||||||
@"button-image-plus": [CPNull null],
|
|
||||||
@"button-image-minus": [CPNull null],
|
|
||||||
@"button-image-action": [CPNull null],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
|
|||||||
@@ -44,14 +44,6 @@ CPCheckBoxImageOffset = 4.0;
|
|||||||
return @"check-box";
|
return @"check-box";
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding === CPValueBinding)
|
|
||||||
return [_CPCheckBoxValueBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
@@ -94,48 +86,4 @@ CPCheckBoxImageOffset = 4.0;
|
|||||||
[self takeStateFromKeyPath:aKeyPath ofObjects:objects];
|
[self takeStateFromKeyPath:aKeyPath ofObjects:objects];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPImage)image
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"image"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPImage)alternateImage
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"image"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var startedTracking = [super startTrackingAt:aPoint];
|
|
||||||
[self highlight:YES];
|
|
||||||
return startedTracking;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPCheckBoxValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
|
||||||
{
|
|
||||||
[super _updatePlaceholdersWithOptions:options];
|
|
||||||
|
|
||||||
[self _setPlaceholder:CPMixedState forMarker:CPMultipleValuesMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:CPOffState forMarker:CPNoSelectionMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:CPOffState forMarker:CPNotApplicableMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:CPOffState forMarker:CPNullMarker isDefault:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[_source setAllowsMixedState:(aValue === CPMixedState)];
|
|
||||||
[_source setState:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[_source setState:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+22
-34
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
@class CPScrollView
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -45,10 +44,10 @@
|
|||||||
if (_documentView == aView)
|
if (_documentView == aView)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||||
|
|
||||||
if (_documentView)
|
if (_documentView)
|
||||||
{
|
{
|
||||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
|
||||||
|
|
||||||
[defaultCenter
|
[defaultCenter
|
||||||
removeObserver:self
|
removeObserver:self
|
||||||
name:CPViewFrameDidChangeNotification
|
name:CPViewFrameDidChangeNotification
|
||||||
@@ -67,30 +66,24 @@
|
|||||||
if (_documentView)
|
if (_documentView)
|
||||||
{
|
{
|
||||||
[self addSubview:_documentView];
|
[self addSubview:_documentView];
|
||||||
[self _observeDocumentView];
|
|
||||||
|
[_documentView setPostsFrameChangedNotifications:YES];
|
||||||
|
[_documentView setPostsBoundsChangedNotifications:YES];
|
||||||
|
|
||||||
|
[defaultCenter
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(viewFrameChanged:)
|
||||||
|
name:CPViewFrameDidChangeNotification
|
||||||
|
object:_documentView];
|
||||||
|
|
||||||
|
[defaultCenter
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(viewBoundsChanged:)
|
||||||
|
name:CPViewBoundsDidChangeNotification
|
||||||
|
object:_documentView];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_observeDocumentView
|
|
||||||
{
|
|
||||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
|
||||||
|
|
||||||
[_documentView setPostsFrameChangedNotifications:YES];
|
|
||||||
[_documentView setPostsBoundsChangedNotifications:YES];
|
|
||||||
|
|
||||||
[defaultCenter
|
|
||||||
addObserver:self
|
|
||||||
selector:@selector(viewFrameChanged:)
|
|
||||||
name:CPViewFrameDidChangeNotification
|
|
||||||
object:_documentView];
|
|
||||||
|
|
||||||
[defaultCenter
|
|
||||||
addObserver:self
|
|
||||||
selector:@selector(viewBoundsChanged:)
|
|
||||||
name:CPViewBoundsDidChangeNotification
|
|
||||||
object:_documentView];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the document view.
|
Returns the document view.
|
||||||
*/
|
*/
|
||||||
@@ -108,19 +101,19 @@
|
|||||||
- (CGPoint)constrainScrollPoint:(CGPoint)aPoint
|
- (CGPoint)constrainScrollPoint:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
if (!_documentView)
|
if (!_documentView)
|
||||||
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)));
|
||||||
|
|
||||||
return aPoint;
|
return aPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setBoundsOrigin:(CGPoint)aPoint
|
- (void)setBoundsOrigin:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
if (CGPointEqualToPoint(_bounds.origin, aPoint))
|
if (_CGPointEqualToPoint(_bounds.origin, aPoint))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[super setBoundsOrigin:aPoint];
|
[super setBoundsOrigin:aPoint];
|
||||||
@@ -240,12 +233,7 @@ var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
|
|||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
if (self = [super initWithCoder:aCoder])
|
if (self = [super initWithCoder:aCoder])
|
||||||
{
|
[self setDocumentView:[aCoder decodeObjectForKey:CPClipViewDocumentViewKey]];
|
||||||
// Don't call setDocumentView: here. It calls addSubview:, but it's A) not necessary since the
|
|
||||||
// view hierarchy is fully encoded and B) dangerous if the subview is not fully decoded.
|
|
||||||
_documentView = [aCoder decodeObjectForKey:CPClipViewDocumentViewKey];
|
|
||||||
[self _observeDocumentView];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|||||||
+172
-621
File diff suppressed because it is too large
Load Diff
@@ -22,38 +22,13 @@
|
|||||||
|
|
||||||
@import "CPViewController.j"
|
@import "CPViewController.j"
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Represents an object inside a CPCollectionView.
|
Represents an object inside a CPCollectionView.
|
||||||
*/
|
*/
|
||||||
@implementation CPCollectionViewItem : CPViewController
|
@implementation CPCollectionViewItem : CPViewController
|
||||||
{
|
{
|
||||||
BOOL _isSelected;
|
BOOL _isSelected;
|
||||||
CPData _cachedArchive;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)copy
|
|
||||||
{
|
|
||||||
var cibName = [self cibName],
|
|
||||||
copy;
|
|
||||||
|
|
||||||
if (cibName)
|
|
||||||
{
|
|
||||||
copy = [[[self class] alloc] initWithCibName:cibName bundle:[self cibBundle]];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!_cachedArchive)
|
|
||||||
_cachedArchive = [CPKeyedArchiver archivedDataWithRootObject:self];
|
|
||||||
|
|
||||||
copy = [CPKeyedUnarchiver unarchiveObjectWithData:_cachedArchive];
|
|
||||||
|
|
||||||
// copy connections
|
|
||||||
}
|
|
||||||
|
|
||||||
[copy setRepresentedObject:[self representedObject]];
|
|
||||||
[copy setSelected:_isSelected];
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting the Represented Object
|
// Setting the Represented Object
|
||||||
|
|||||||
+41
-740
@@ -21,7 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import "CPGraphicsContext.j"
|
|
||||||
|
|
||||||
@import "CGColor.j"
|
@import "CGColor.j"
|
||||||
|
|
||||||
@@ -29,18 +28,6 @@
|
|||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Orientation to use with \c CPColorPattern for vertical patterns.
|
|
||||||
*/
|
|
||||||
CPColorPatternIsVertical = YES;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Orientation to use with \c CPColorPattern for horizontal patterns.
|
|
||||||
*/
|
|
||||||
CPColorPatternIsHorizontal = NO;
|
|
||||||
|
|
||||||
/// @cond IGNORE
|
|
||||||
|
|
||||||
var _redComponent = 0,
|
var _redComponent = 0,
|
||||||
_greenComponent = 1,
|
_greenComponent = 1,
|
||||||
_blueComponent = 2,
|
_blueComponent = 2,
|
||||||
@@ -67,8 +54,6 @@ var cachedBlackColor,
|
|||||||
cachedShadowColor,
|
cachedShadowColor,
|
||||||
cachedClearColor;
|
cachedClearColor;
|
||||||
|
|
||||||
/// @endcond
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
|
|
||||||
@@ -87,7 +72,7 @@ var cachedBlackColor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a color in the RGB colorspace, 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.
|
||||||
@@ -101,13 +86,13 @@ var cachedBlackColor,
|
|||||||
*/
|
*/
|
||||||
+ (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
|
||||||
{
|
{
|
||||||
return [[CPColor alloc] _initWithRGBA:[MAX(0.0, MIN(1.0, red)), MAX(0.0, MIN(1.0, green)), MAX(0.0, MIN(1.0, blue)), MAX(0.0, MIN(1.0, alpha))]];
|
return [[CPColor alloc] _initWithRGBA:[red, green, blue, alpha]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@deprecated in favor of colorWithRed:green:blue:alpha:
|
@deprecated in favor of colorWithRed:green:blue:alpha:
|
||||||
|
|
||||||
Creates a color in the RGB colorspace, 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.
|
||||||
@@ -140,7 +125,7 @@ var cachedBlackColor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@deprecated in favor of colorWithWhite:alpha:
|
@deprecated in favor of colorWithWhite:apha:
|
||||||
|
|
||||||
Creates a new color object with \c white for the RGB components.
|
Creates a new color object with \c white for the RGB components.
|
||||||
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
|
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
|
||||||
@@ -156,15 +141,11 @@ var cachedBlackColor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a new color based on the given HSB components.
|
Creates a new color in HSB space.
|
||||||
|
|
||||||
Note: earlier versions of this method took a hue component as degrees between 0-360,
|
@param hue the hue value
|
||||||
and saturation and brightness components as percent between 0-100. This method has
|
@param saturation the saturation value
|
||||||
now been corrected to take all components in the 0-1 range as in Cocoa.
|
@param brightness the brightness value
|
||||||
|
|
||||||
@param hue the hue component (0.0-1.0)
|
|
||||||
@param saturation the saturation component (0.0-1.0)
|
|
||||||
@param brightness the brightness component (0.0-1.0)
|
|
||||||
|
|
||||||
@return the initialized color
|
@return the initialized color
|
||||||
*/
|
*/
|
||||||
@@ -173,61 +154,25 @@ var cachedBlackColor,
|
|||||||
return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0];
|
return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Calibrated colors are not supported in Cappuccino.
|
|
||||||
|
|
||||||
This method has the same result as [CPColor colorWithHue:saturation:brightness:alpha:].
|
|
||||||
*/
|
|
||||||
+ (CPColor)colorWithCalibratedHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
|
|
||||||
{
|
|
||||||
return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:alpha];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Creates a new color based on the given HSB components.
|
|
||||||
|
|
||||||
Note: earlier versions of this method took a hue component as degrees between 0-360,
|
|
||||||
and saturation and brightness components as percent between 0-100. This method has
|
|
||||||
now been corrected to take all components in the 0-1 range as in Cocoa.
|
|
||||||
|
|
||||||
@param hue the hue component (0.0-1.0)
|
|
||||||
@param saturation the saturation component (0.0-1.0)
|
|
||||||
@param brightness the brightness component (0.0-1.0)
|
|
||||||
@param alpha the opacity component (0.0-1.0)
|
|
||||||
|
|
||||||
@return the initialized color
|
|
||||||
*/
|
|
||||||
+ (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
|
||||||
{
|
{
|
||||||
// Clamp values.
|
|
||||||
hue = MAX(MIN(hue, 1.0), 0.0);
|
|
||||||
saturation = MAX(MIN(saturation, 1.0), 0.0);
|
|
||||||
brightness = MAX(MIN(brightness, 1.0), 0.0);
|
|
||||||
|
|
||||||
if (saturation === 0.0)
|
if (saturation === 0.0)
|
||||||
return [CPColor colorWithCalibratedWhite:brightness alpha:alpha];
|
return [CPColor colorWithCalibratedWhite:brightness / 100.0 alpha:alpha];
|
||||||
|
|
||||||
var f = (hue * 360) % 60,
|
var f = hue % 60,
|
||||||
p = (brightness * (1 - saturation)),
|
p = (brightness * (100 - saturation)) / 10000,
|
||||||
q = (brightness * (60 - saturation * f)) / 60,
|
q = (brightness * (6000 - saturation * f)) / 600000,
|
||||||
t = (brightness * (60 - saturation * (60 - f))) / 60,
|
t = (brightness * (6000 - saturation * (60 -f))) / 600000,
|
||||||
b = brightness;
|
b = brightness / 100.0;
|
||||||
|
|
||||||
switch (FLOOR(hue * 6))
|
switch (FLOOR(hue / 60))
|
||||||
{
|
{
|
||||||
case 0:
|
case 0: return [CPColor colorWithCalibratedRed: b green: t blue: p alpha: alpha];
|
||||||
case 6:
|
case 1: return [CPColor colorWithCalibratedRed: q green: b blue: p alpha: alpha];
|
||||||
return [CPColor colorWithCalibratedRed:b green:t blue:p alpha:alpha];
|
case 2: return [CPColor colorWithCalibratedRed: p green: b blue: t alpha: alpha];
|
||||||
case 1:
|
case 3: return [CPColor colorWithCalibratedRed: p green: q blue: b alpha: alpha];
|
||||||
return [CPColor colorWithCalibratedRed:q green:b blue:p alpha:alpha];
|
case 4: return [CPColor colorWithCalibratedRed: t green: p blue: b alpha: alpha];
|
||||||
case 2:
|
case 5: return [CPColor colorWithCalibratedRed: b green: p blue: q alpha: alpha];
|
||||||
return [CPColor colorWithCalibratedRed:p green:b blue:t alpha:alpha];
|
|
||||||
case 3:
|
|
||||||
return [CPColor colorWithCalibratedRed:p green:q blue:b alpha:alpha];
|
|
||||||
case 4:
|
|
||||||
return [CPColor colorWithCalibratedRed:t green:p blue:b alpha:alpha];
|
|
||||||
case 5:
|
|
||||||
return [CPColor colorWithCalibratedRed:b green:p blue:q alpha:alpha];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,17 +192,6 @@ var cachedBlackColor,
|
|||||||
return rgba ? [[CPColor alloc] _initWithRGBA: rgba] : null;
|
return rgba ? [[CPColor alloc] _initWithRGBA: rgba] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Creates a color in the sRGB colorspace with the given components and alpha values.
|
|
||||||
Values below 0.0 are treated as 0.0 and values above 1.0 are treated as 1.0.
|
|
||||||
*/
|
|
||||||
+ (CPColor)colorWithSRGBRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
|
|
||||||
{
|
|
||||||
// TODO If Cappuccino is ported to a colorspace aware platform, this color should be in
|
|
||||||
// sRGBColorSpace.
|
|
||||||
return [self colorWithRed:red green:green blue:blue alpha:alpha];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a black color object. (RGBA=[0.0, 0.0, 0.0, 1.0])
|
Returns a black color object. (RGBA=[0.0, 0.0, 0.0, 1.0])
|
||||||
*/
|
*/
|
||||||
@@ -482,12 +416,10 @@ var cachedBlackColor,
|
|||||||
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] ? parseFloat(parts[3], 10) : 1.0
|
parts[3] ? parseInt(parts[3], 10) / 255.0 : 1.0
|
||||||
];
|
];
|
||||||
|
|
||||||
// We can't reuse aString as _cssString because the browser might not support the `rgba` syntax, and aString might
|
_cssString = aString;
|
||||||
// use it (issue #1413.)
|
|
||||||
[self _initCSSStringFromComponents];
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -501,23 +433,18 @@ var cachedBlackColor,
|
|||||||
{
|
{
|
||||||
_components = components;
|
_components = components;
|
||||||
|
|
||||||
[self _initCSSStringFromComponents];
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_initCSSStringFromComponents
|
|
||||||
{
|
|
||||||
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]) : "") + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (id)_initWithPatternImage:(CPImage)anImage
|
- (id)_initWithPatternImage:(CPImage)anImage
|
||||||
{
|
{
|
||||||
@@ -605,19 +532,8 @@ var cachedBlackColor,
|
|||||||
return [[[self class] alloc] _initWithRGBA:components];
|
return [[[self class] alloc] _initWithRGBA:components];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the receiver. This method is a placeholder that does nothing but may be implemented in the future.
|
|
||||||
*/
|
|
||||||
- (CPColor)colorUsingColorSpaceName:(id)aColorSpaceName
|
|
||||||
{
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns an array with the HSB values for this color.
|
Returns an array with the HSB values for this color.
|
||||||
|
|
||||||
The values are expressed as fractions between 0.0-1.0.
|
|
||||||
|
|
||||||
The index values are ordered as:
|
The index values are ordered as:
|
||||||
<pre>
|
<pre>
|
||||||
<b>Index</b> <b>Component</b>
|
<b>Index</b> <b>Component</b>
|
||||||
@@ -664,36 +580,12 @@ var cachedBlackColor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
hue,
|
ROUND(hue * 360.0),
|
||||||
saturation,
|
ROUND(saturation * 100.0),
|
||||||
brightness
|
ROUND(brightness * 100.0)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the hue component, the H in HSB, of the receiver.
|
|
||||||
*/
|
|
||||||
- (float)hueComponent
|
|
||||||
{
|
|
||||||
return [self hsbComponents][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the saturation component, the S in HSB, of the receiver.
|
|
||||||
*/
|
|
||||||
- (float)saturationComponent
|
|
||||||
{
|
|
||||||
return [self hsbComponents][1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the brightness component, the B in HSB, of the receiver.
|
|
||||||
*/
|
|
||||||
- (float)brightnessComponent
|
|
||||||
{
|
|
||||||
return [self hsbComponents][2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the CSS representation of this color. The color will
|
Returns the CSS representation of this color. The color will
|
||||||
be in one of the following forms:
|
be in one of the following forms:
|
||||||
@@ -713,7 +605,7 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
|||||||
*/
|
*/
|
||||||
- (CPString)hexString
|
- (CPString)hexString
|
||||||
{
|
{
|
||||||
return rgbToHex([self redComponent], [self greenComponent], [self blueComponent]);
|
return rgbToHex([self redComponent], [self greenComponent], [self blueComponent])
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(CPColor)aColor
|
- (BOOL)isEqual:(CPColor)aColor
|
||||||
@@ -724,53 +616,12 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
|||||||
if (aColor === self)
|
if (aColor === self)
|
||||||
return YES;
|
return YES;
|
||||||
|
|
||||||
if (![aColor isKindOfClass:CPColor])
|
return [aColor isKindOfClass:CPColor] && [aColor cssString] === [self cssString];
|
||||||
return NO;
|
|
||||||
|
|
||||||
if (_patternImage || [aColor patternImage])
|
|
||||||
return [_patternImage isEqual:[aColor patternImage]];
|
|
||||||
|
|
||||||
// We don't require the components to be equal beyond 8 bits since otherwise
|
|
||||||
// simple rounding errors will make two colours which are exactly the same on
|
|
||||||
// screen compare unequal.
|
|
||||||
return ROUND([self redComponent] * 255.0) == ROUND([aColor redComponent] * 255.0) &&
|
|
||||||
ROUND([self greenComponent] * 255.0) == ROUND([aColor greenComponent] * 255.0) &&
|
|
||||||
ROUND([self blueComponent] * 255.0) == ROUND([aColor blueComponent] * 255.0) &&
|
|
||||||
[self alphaComponent] == [aColor alphaComponent];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPString)description
|
- (CPString)description
|
||||||
{
|
{
|
||||||
var description = [super description],
|
return [super description]+" "+[self cssString];
|
||||||
patternImage = [self patternImage];
|
|
||||||
|
|
||||||
if (!patternImage)
|
|
||||||
return description + " " + [self cssString];
|
|
||||||
|
|
||||||
description += " {\n";
|
|
||||||
|
|
||||||
if ([patternImage isThreePartImage] || [patternImage isNinePartImage])
|
|
||||||
{
|
|
||||||
var slices = [patternImage imageSlices];
|
|
||||||
|
|
||||||
if ([patternImage isThreePartImage])
|
|
||||||
description += " orientation: " + ([patternImage isVertical] ? "vertical" : "horizontal") + ",\n";
|
|
||||||
|
|
||||||
description += " patternImage (" + slices.length + " part): [\n";
|
|
||||||
|
|
||||||
for (var i = 0; i < slices.length; ++i)
|
|
||||||
{
|
|
||||||
var imgDescription = [slices[i] description] || "nil";
|
|
||||||
|
|
||||||
description += imgDescription.replace(/^/mg, " ") + ",\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
description = description.substr(0, description.length - 2) + "\n ]\n}";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
description += ([patternImage description] || "nil").replace(/^/mg, " ") + "\n}";
|
|
||||||
|
|
||||||
return description;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -815,10 +666,8 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/// @cond IGNORE
|
|
||||||
var CPColorComponentsKey = @"CPColorComponentsKey",
|
var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||||
CPColorPatternImageKey = @"CPColorPatternImageKey";
|
CPColorPatternImageKey = @"CPColorPatternImageKey";
|
||||||
/// @endcond
|
|
||||||
|
|
||||||
@implementation CPColor (CPCoding)
|
@implementation CPColor (CPCoding)
|
||||||
|
|
||||||
@@ -849,12 +698,13 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
/// @cond IGNORE
|
|
||||||
var hexCharacters = "0123456789ABCDEF";
|
var hexCharacters = "0123456789ABCDEF";
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
Used for the CPColor +colorWithHexString: implementation.
|
Used for the CPColor \c +colorWithHexString: implementation
|
||||||
Returns an array of rgb components.
|
@ignore
|
||||||
|
@class CPColor
|
||||||
|
@return an array of rgb components
|
||||||
*/
|
*/
|
||||||
var hexToRGB = function(hex)
|
var hexToRGB = function(hex)
|
||||||
{
|
{
|
||||||
@@ -892,552 +742,3 @@ var byteToHex = function(n)
|
|||||||
return hexCharacters.charAt((n - n % 16) / 16) +
|
return hexCharacters.charAt((n - n % 16) / 16) +
|
||||||
hexCharacters.charAt(n % 16);
|
hexCharacters.charAt(n % 16);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
To create a simple color with a pattern image:
|
|
||||||
CPColorWithImages(name, width, height{, bundle})
|
|
||||||
|
|
||||||
To create a color with a three-part/nine-part image using a pattern string:
|
|
||||||
CPColorWithImages(pattern, attributes{, bundle})
|
|
||||||
|
|
||||||
<pattern> must have one or more placeholders. There are there possible
|
|
||||||
placeholders available that together can be used to build a filename:
|
|
||||||
|
|
||||||
style A top-level style, for example normal (empty) and "default".
|
|
||||||
state The state of a view, for example "highlighted" and "disabled".
|
|
||||||
position The position of an image within a 3-part or 9-part image.
|
|
||||||
This must be provided.
|
|
||||||
|
|
||||||
Each placeholder is filled with the values you pass in a the attributes object.
|
|
||||||
Neither style nor state are necessary, if you omit them the next level of the
|
|
||||||
hierarchy is the top level.
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
----------
|
|
||||||
styles: [...]
|
|
||||||
An array of style names. For example, if you want to generate the filenames
|
|
||||||
"button-bezel" and "default-button-bezel", you would use ["", "default"]
|
|
||||||
for the styles.
|
|
||||||
|
|
||||||
states: [...]
|
|
||||||
An array of state names.
|
|
||||||
|
|
||||||
positions: <spec>
|
|
||||||
Specifies the naming convention for the slices of the image. For 3-part images,
|
|
||||||
<spec> may be one of:
|
|
||||||
|
|
||||||
"@" Equivalent to ["left", "center", "right"] for horizontal
|
|
||||||
images, ["top", "center", "bottom"] for vertical images
|
|
||||||
"#" Equivalent to ["0", "1", "2"]
|
|
||||||
[...] An array of any three literal strings
|
|
||||||
|
|
||||||
For 9-part images, <spec> may be one of:
|
|
||||||
|
|
||||||
"@" | "abbrev" Equivalent to ["top-left", "top", "top-right",
|
|
||||||
"left", "center", "right",
|
|
||||||
"bottom-left", "bottom", "bottom-right"]
|
|
||||||
"full" Equivalent to ["top-left", "top-center", "top-right",
|
|
||||||
"center-left", "center-center", "center-right",
|
|
||||||
"bottom-left", "bottom-center", "bottom-right"]
|
|
||||||
"#" Equivalent to ["0", "1", "2", "3", "4", "5", "6", "7", "8"]
|
|
||||||
[...] An array of any nine literal strings
|
|
||||||
|
|
||||||
width: <number>
|
|
||||||
The width of the left image for horizontal 3-part images, the top image for
|
|
||||||
vertical 3-part images, or the top left corner for 9-part images.
|
|
||||||
|
|
||||||
height: <number>
|
|
||||||
The height of the left image for horizontal 3-part images, the top image for
|
|
||||||
vertical 3-part images, or the top left corner for 9-part images.
|
|
||||||
|
|
||||||
centerWidth: <number>
|
|
||||||
For a horizontal 3-part image that has a center slice width that is not 1.0,
|
|
||||||
set this for the width of the center slice. If omitted, it defaults
|
|
||||||
to 1.0. Not used for 9-part images.
|
|
||||||
|
|
||||||
rightWidth: <number>
|
|
||||||
For a horizontal 3-part image that has different left/right slice widths,
|
|
||||||
or a 9-part image that has different left/right corner slice widths,
|
|
||||||
set this for the width of the right slice. If omitted, the "width"
|
|
||||||
value is used for left and right widths.
|
|
||||||
|
|
||||||
centerHeight: <number>
|
|
||||||
For a vertical 3-part image that has a center slice height that is not 1.0,
|
|
||||||
set this for the height of the center slice. If omitted, it defaults
|
|
||||||
to 1.0. Not used for 9-part images.
|
|
||||||
|
|
||||||
bottomHeight: <number>
|
|
||||||
For a vertical image that has different top/bottom slice heights,
|
|
||||||
or a 9-part image that has different top/bottom corner slice heights,
|
|
||||||
set this for the height of the bottom slice. If omitted, the "height"
|
|
||||||
value is used for top and bottom heights.
|
|
||||||
|
|
||||||
centerIsNil: <BOOL>
|
|
||||||
For 9-part images, if the center image is nil, set this to YES.
|
|
||||||
If omitted or set to NO, a center image must be provided.
|
|
||||||
|
|
||||||
separator: <string>
|
|
||||||
The separator to use between components of the pattern.
|
|
||||||
If it is omitted, it defaults to "-".
|
|
||||||
|
|
||||||
orientation: PatternIsHorizontal | PatternIsVertical
|
|
||||||
The orientation of the image. This must be specified for 3-part images,
|
|
||||||
it should NOT be specified for 9-part images.
|
|
||||||
|
|
||||||
Using a pattern, all of this:
|
|
||||||
|
|
||||||
bezelColor = PatternColor(
|
|
||||||
[
|
|
||||||
["button-bezel-left.png", 4.0, 24.0],
|
|
||||||
["button-bezel-center.png", 1.0, 24.0],
|
|
||||||
["button-bezel-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
PatternIsHorizontal),
|
|
||||||
|
|
||||||
highlightedBezelColor = PatternColor(
|
|
||||||
[
|
|
||||||
["button-bezel-highlighted-left.png", 4.0, 24.0],
|
|
||||||
["button-bezel-highlighted-center.png", 1.0, 24.0],
|
|
||||||
["button-bezel-highlighted-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
PatternIsHorizontal),
|
|
||||||
|
|
||||||
disabledBezelColor = PatternColor(
|
|
||||||
[
|
|
||||||
["button-bezel-disabled-left.png", 4.0, 24.0],
|
|
||||||
["button-bezel-disabled-center.png", 1.0, 24.0],
|
|
||||||
["button-bezel-disabled-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
PatternIsHorizontal),
|
|
||||||
|
|
||||||
defaultBezelColor = PatternColor(
|
|
||||||
[
|
|
||||||
["default-button-bezel-left.png", 4.0, 24.0],
|
|
||||||
["default-button-bezel-center.png", 1.0, 24.0],
|
|
||||||
["default-button-bezel-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
PatternIsHorizontal),
|
|
||||||
|
|
||||||
defaultHighlightedBezelColor = PatternColor(
|
|
||||||
[
|
|
||||||
["default-button-bezel-highlighted-left.png", 4.0, 24.0],
|
|
||||||
["default-button-bezel-highlighted-center.png", 1.0, 24.0],
|
|
||||||
["default-button-bezel-highlighted-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
PatternIsHorizontal),
|
|
||||||
|
|
||||||
defaultDisabledBezelColor = PatternColor(
|
|
||||||
[
|
|
||||||
["default-button-bezel-disabled-left.png", 4.0, 24.0],
|
|
||||||
["default-button-bezel-disabled-center.png", 1.0, 24.0],
|
|
||||||
["default-button-bezel-disabled-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
PatternIsHorizontal)
|
|
||||||
|
|
||||||
can be replaced with this:
|
|
||||||
|
|
||||||
bezelColors = PatternColor(
|
|
||||||
"{style}button-bezel{state}{position}.png",
|
|
||||||
{
|
|
||||||
styles: ["", "default"],
|
|
||||||
states: ["", "highlighted", "disabled"],
|
|
||||||
positions: "@",
|
|
||||||
width: 4.0,
|
|
||||||
height: 24.0,
|
|
||||||
orientation: PatternIsHorizontal
|
|
||||||
})
|
|
||||||
|
|
||||||
Which would effectively create the following object:
|
|
||||||
|
|
||||||
{
|
|
||||||
"@":
|
|
||||||
{
|
|
||||||
"@": <ThreePartImage>:
|
|
||||||
[
|
|
||||||
["button-bezel-left.png", 4.0, 24.0],
|
|
||||||
["button-bezel-center.png", 1.0, 24.0],
|
|
||||||
["button-bezel-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
"highlighted": <ThreePartImage>:
|
|
||||||
[
|
|
||||||
["button-bezel-highlighted-left.png", 4.0, 24.0],
|
|
||||||
["button-bezel-highlighted-center.png", 1.0, 24.0],
|
|
||||||
["button-bezel-highlighted-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
"disabled": <ThreePartImage>:
|
|
||||||
[
|
|
||||||
["button-bezel-disabled-left.png", 4.0, 24.0],
|
|
||||||
["button-bezel-disabled-center.png", 1.0, 24.0],
|
|
||||||
["button-bezel-disabled-right.png", 4.0, 24.0]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"default":
|
|
||||||
{
|
|
||||||
"@": <ThreePartImage>:
|
|
||||||
[
|
|
||||||
["default-button-bezel-left.png", 4.0, 24.0],
|
|
||||||
["default-button-bezel-center.png", 1.0, 24.0],
|
|
||||||
["default-button-bezel-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
"highlighted": <ThreePartImage>:
|
|
||||||
[
|
|
||||||
["default-button-bezel-highlighted-left.png", 4.0, 24.0],
|
|
||||||
["default-button-bezel-highlighted-center.png", 1.0, 24.0],
|
|
||||||
["default-button-bezel-highlighted-right.png", 4.0, 24.0]
|
|
||||||
],
|
|
||||||
"disabled": <ThreePartImage>:
|
|
||||||
[
|
|
||||||
["default-button-bezel-disabled-left.png", 4.0, 24.0],
|
|
||||||
["button-bezel-disabled-center.png", 1.0, 24.0],
|
|
||||||
["default-button-bezel-disabled-right.png", 4.0, 24.0]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
To reference empty names in the pattern color, use the key "@".
|
|
||||||
So, for example, to reference the "default" style, "disabled" state,
|
|
||||||
you would use the following expression:
|
|
||||||
|
|
||||||
bezelColors["@"]["disabled"]
|
|
||||||
|
|
||||||
To create a color with a three-part pattern image explicitly:
|
|
||||||
CPColorWithImages(slices, orientation)
|
|
||||||
|
|
||||||
where slices is an array of three [name, width, height{, bundle}] arrays,
|
|
||||||
and orientation is CPColorPatternIsVertical or CPColorPatternIsHorizontal.
|
|
||||||
|
|
||||||
To create a color with a nine-part pattern image explicitly:
|
|
||||||
CPColorWithImages(slices);
|
|
||||||
|
|
||||||
where slices is an array of nine [name, width, height{, bundle}] arrays.
|
|
||||||
*/
|
|
||||||
function CPColorWithImages()
|
|
||||||
{
|
|
||||||
var slices = nil,
|
|
||||||
numParts = 0,
|
|
||||||
isVertical = false,
|
|
||||||
imageFactory = CPImageInBundle,
|
|
||||||
args = Array.prototype.slice.apply(arguments);
|
|
||||||
|
|
||||||
if (typeof(args[args.length - 1]) === "function")
|
|
||||||
imageFactory = args.pop();
|
|
||||||
|
|
||||||
switch (args.length)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
return imageFromSlices(args[0], isVertical, imageFactory);
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
// New-style 3-part and 9-part images
|
|
||||||
if (typeof(args[0]) === "string")
|
|
||||||
return patternColorsFromPattern.call(this, args[0], args[1], imageFactory);
|
|
||||||
|
|
||||||
return imageFromSlices(args[0], args[1], imageFactory);
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
return [CPColor colorWithPatternImage:imageFactory(args[0], args[1], args[2], args[3])];
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw("ERROR: Invalid argument count: " + args.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var imageFromSlices = function(slices, isVertical, imageFactory)
|
|
||||||
{
|
|
||||||
var imageSlices = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < slices.length; ++i)
|
|
||||||
{
|
|
||||||
var slice = slices[i];
|
|
||||||
|
|
||||||
imageSlices.push(slice ? imageFactory(slice[0], slice[1], slice[2], slice[3]) : nil);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (slices.length)
|
|
||||||
{
|
|
||||||
case 3:
|
|
||||||
return [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:imageSlices isVertical:isVertical]];
|
|
||||||
|
|
||||||
case 9:
|
|
||||||
return [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:imageSlices]];
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw("ERROR: Invalid number of image slices: " + slices.length);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var patternColorsFromPattern = function(pattern, attributes, imageFactory)
|
|
||||||
{
|
|
||||||
if (pattern.match(/^.*\{[^}]+\}/))
|
|
||||||
{
|
|
||||||
var width = attributes["width"],
|
|
||||||
height = attributes["height"],
|
|
||||||
separator = attributes["separator"] || "-",
|
|
||||||
orientation = attributes["orientation"],
|
|
||||||
rightWidth,
|
|
||||||
bottomHeight,
|
|
||||||
centerWidthHeight,
|
|
||||||
centerIsNil,
|
|
||||||
numParts;
|
|
||||||
|
|
||||||
// positions are mandatory
|
|
||||||
if (pattern.indexOf("{position}") < 0)
|
|
||||||
throw("ERROR: Pattern strings must have a {position} placeholder (\"" + pattern + "\")");
|
|
||||||
|
|
||||||
if (orientation === undefined)
|
|
||||||
{
|
|
||||||
numParts = 9;
|
|
||||||
|
|
||||||
if (attributes["centerIsNil"] !== undefined)
|
|
||||||
centerIsNil = attributes["centerIsNil"];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
numParts = 3;
|
|
||||||
isVertical = orientation === PatternIsVertical;
|
|
||||||
|
|
||||||
if (isVertical)
|
|
||||||
{
|
|
||||||
if (attributes["centerHeight"])
|
|
||||||
centerWidthHeight = attributes["centerHeight"];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (attributes["centerWidth"])
|
|
||||||
centerWidthHeight = attributes["centerWidth"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes["rightWidth"])
|
|
||||||
rightWidth = attributes["rightWidth"];
|
|
||||||
|
|
||||||
if (attributes["bottomHeight"])
|
|
||||||
bottomHeight = attributes["bottomHeight"];
|
|
||||||
|
|
||||||
var positions = attributes["positions"] || "@",
|
|
||||||
states = nil,
|
|
||||||
styles = nil;
|
|
||||||
|
|
||||||
if (numParts === 3)
|
|
||||||
{
|
|
||||||
if (positions === "@")
|
|
||||||
{
|
|
||||||
if (isVertical)
|
|
||||||
positions = ["top", "center", "bottom"];
|
|
||||||
else
|
|
||||||
positions = ["left", "center", "right"];
|
|
||||||
}
|
|
||||||
else if (positions === "#")
|
|
||||||
positions = ["0", "1", "2"];
|
|
||||||
else
|
|
||||||
throw("ERROR: Invalid positions: " + positions)
|
|
||||||
}
|
|
||||||
else // numParts === 9
|
|
||||||
{
|
|
||||||
if (positions === "@" || positions === "abbrev")
|
|
||||||
positions = ["top-left", "top", "top-right", "left", "center", "right", "bottom-left", "bottom", "bottom-right"];
|
|
||||||
else if (positions === "full")
|
|
||||||
positions = ["top-left", "top-center", "top-right", "center-left", "center-center", "center-right", "bottom-left", "bottom-center", "bottom-right"];
|
|
||||||
else if (positions === "#")
|
|
||||||
positions = ["0", "1", "2", "3", "4", "5", "6", "7", "8"];
|
|
||||||
else
|
|
||||||
throw("ERROR: Invalid positions: " + positions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// states
|
|
||||||
if (pattern.indexOf("{state}") >= 0)
|
|
||||||
{
|
|
||||||
states = attributes["states"];
|
|
||||||
|
|
||||||
if (!states)
|
|
||||||
throw("ERROR: {state} placeholder in the pattern (\"" + pattern + "\") but no states item in the attributes");
|
|
||||||
}
|
|
||||||
|
|
||||||
// styles
|
|
||||||
if (pattern.indexOf("{style}") >= 0)
|
|
||||||
{
|
|
||||||
styles = attributes["styles"];
|
|
||||||
|
|
||||||
if (!styles)
|
|
||||||
throw("ERROR: {style} placeholder in the pattern (\"" + pattern + "\") but no styles item in the attributes");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now assemble the hierarchy
|
|
||||||
var placeholder = "{position}",
|
|
||||||
pos = pattern.indexOf(placeholder),
|
|
||||||
i;
|
|
||||||
|
|
||||||
for (i = 0; i < positions.length; ++i)
|
|
||||||
positions[i] = pattern.replace(placeholder, pos === 0 ? positions[i] + separator : separator + positions[i]);
|
|
||||||
|
|
||||||
var slices = positions,
|
|
||||||
object = slices,
|
|
||||||
key,
|
|
||||||
sep;
|
|
||||||
|
|
||||||
if (states)
|
|
||||||
{
|
|
||||||
placeholder = "{state}";
|
|
||||||
pos = pattern.indexOf(placeholder);
|
|
||||||
object = {};
|
|
||||||
|
|
||||||
for (i = 0; i < states.length; ++i)
|
|
||||||
{
|
|
||||||
var state = states[i];
|
|
||||||
key = state || "@";
|
|
||||||
sep = state ? separator : "";
|
|
||||||
|
|
||||||
object[key] = slices.slice(0);
|
|
||||||
replacePlaceholderInArray(object[key], placeholder, pos === 0 ? state + sep : sep + state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (styles)
|
|
||||||
{
|
|
||||||
placeholder = "{style}";
|
|
||||||
pos = pattern.indexOf(placeholder);
|
|
||||||
|
|
||||||
var styleObject = {};
|
|
||||||
|
|
||||||
for (i = 0; i < styles.length; ++i)
|
|
||||||
{
|
|
||||||
var style = styles[i];
|
|
||||||
key = style || "@";
|
|
||||||
sep = style ? separator : "";
|
|
||||||
|
|
||||||
if (states)
|
|
||||||
{
|
|
||||||
styleObject[key] = cloneObject(object);
|
|
||||||
replacePlaceholderInObject(styleObject[key], placeholder, pos === 0 ? style + sep : sep + style);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
styleObject[key] = slices.slice(0);
|
|
||||||
replacePlaceholderInArray(styleObject[key], placeholder, pos === 0 ? style + sep : sep + style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object = styleObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (styles || states)
|
|
||||||
{
|
|
||||||
if (numParts === 3)
|
|
||||||
makeThreePartSlicesFromObject(object, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
|
|
||||||
else
|
|
||||||
makeNinePartSlicesFromObject(object, width, height, rightWidth, bottomHeight, centerIsNil);
|
|
||||||
|
|
||||||
makeImagesFromObject(object, isVertical, imageFactory);
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (numParts === 3)
|
|
||||||
makeThreePartSlicesFromArray(object, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
|
|
||||||
else
|
|
||||||
makeNinePartSlicesFromArray(object, width, height, rightWidth, bottomHeight, centerIsNil);
|
|
||||||
|
|
||||||
return imageFromSlices(object, isVertical, imageFactory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw("ERROR: No placeholders in slice pattern (\"" + pattern + "\")");
|
|
||||||
};
|
|
||||||
|
|
||||||
var replacePlaceholderInArray = function(array, find, replacement)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < array.length; ++i)
|
|
||||||
array[i] = array[i].replace(find, replacement);
|
|
||||||
};
|
|
||||||
|
|
||||||
var replacePlaceholderInObject = function(object, find, replacement)
|
|
||||||
{
|
|
||||||
for (var key in object)
|
|
||||||
if (object.hasOwnProperty(key))
|
|
||||||
if (object[key].constructor === Array)
|
|
||||||
replacePlaceholderInArray(object[key], find, replacement);
|
|
||||||
else
|
|
||||||
replacePlaceholderInObject(object[key], find, replacement);
|
|
||||||
};
|
|
||||||
|
|
||||||
var cloneObject = function(object)
|
|
||||||
{
|
|
||||||
var clone = {};
|
|
||||||
|
|
||||||
for (var key in object)
|
|
||||||
if (object.hasOwnProperty(key))
|
|
||||||
if (object[key].constructor === Array)
|
|
||||||
clone[key] = object[key].slice(0);
|
|
||||||
else if (typeof(object[key]) === "object")
|
|
||||||
clone[key] = cloneObject(object[key]);
|
|
||||||
else
|
|
||||||
clone[key] = object[key];
|
|
||||||
|
|
||||||
return clone;
|
|
||||||
};
|
|
||||||
|
|
||||||
var makeThreePartSlicesFromObject = function(object, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical)
|
|
||||||
{
|
|
||||||
for (var key in object)
|
|
||||||
if (object.hasOwnProperty(key))
|
|
||||||
if (object[key].constructor === Array)
|
|
||||||
makeThreePartSlicesFromArray(object[key], width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
|
|
||||||
else // object
|
|
||||||
makeThreePartSlicesFromObject(object[key], width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
|
|
||||||
};
|
|
||||||
|
|
||||||
var makeThreePartSlicesFromArray = function(array, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical)
|
|
||||||
{
|
|
||||||
array[0] = [array[0], width, height];
|
|
||||||
|
|
||||||
if (isVertical)
|
|
||||||
{
|
|
||||||
array[1] = [array[1], width, centerWidthHeight ? centerWidthHeight : 1.0];
|
|
||||||
array[2] = [array[2], width, bottomHeight ? bottomHeight : height];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
array[1] = [array[1], centerWidthHeight ? centerWidthHeight : 1.0, height];
|
|
||||||
array[2] = [array[2], rightWidth ? rightWidth : width, height];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var makeNinePartSlicesFromObject = function(object, width, height, rightWidth, bottomHeight, centerIsNil)
|
|
||||||
{
|
|
||||||
for (var key in object)
|
|
||||||
if (object.hasOwnProperty(key))
|
|
||||||
if (object[key].constructor === Array)
|
|
||||||
makeNinePartSlicesFromArray(object[key], width, height, rightWidth, bottomHeight, centerIsNil);
|
|
||||||
else // object
|
|
||||||
makeNinePartSlicesFromObject(object[key], width, height, rightWidth, bottomHeight, centerIsNil);
|
|
||||||
};
|
|
||||||
|
|
||||||
var makeNinePartSlicesFromArray = function(array, width, height, rightWidth, bottomHeight, centerIsNil)
|
|
||||||
{
|
|
||||||
rightWidth = rightWidth ? rightWidth : width;
|
|
||||||
bottomHeight = bottomHeight ? bottomHeight : height;
|
|
||||||
|
|
||||||
array[0] = [array[0], width, height]; // top-left
|
|
||||||
array[1] = [array[1], 1.0, height]; // top
|
|
||||||
array[2] = [array[2], rightWidth, height]; // top-right
|
|
||||||
array[3] = [array[3], width, 1.0]; // left
|
|
||||||
array[4] = centerIsNil ? nil : [array[4], 1.0, 1.0]; // center
|
|
||||||
array[5] = [array[5], rightWidth, 1.0]; // right
|
|
||||||
array[6] = [array[6], width, bottomHeight]; // bottom-left
|
|
||||||
array[7] = [array[7], 1.0, bottomHeight]; // bottom
|
|
||||||
array[8] = [array[8], rightWidth, bottomHeight]; // bottom-right
|
|
||||||
};
|
|
||||||
|
|
||||||
var makeImagesFromObject = function(object, isVertical, imageFactory)
|
|
||||||
{
|
|
||||||
for (var key in object)
|
|
||||||
if (object.hasOwnProperty(key))
|
|
||||||
if (object[key].constructor === Array)
|
|
||||||
object[key] = imageFromSlices(object[key], isVertical, imageFactory);
|
|
||||||
else // object
|
|
||||||
makeImagesFromObject(object[key], isVertical, imageFactory);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @endcond
|
|
||||||
|
|||||||
+61
-67
@@ -20,32 +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 "CPApplication.j"
|
||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
@import "CPCookie.j"
|
@import "CPCookie.j"
|
||||||
@import "CPPanel.j"
|
@import "CPPanel.j"
|
||||||
@import "CPPasteboard.j"
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
@class CPSlider
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
/*
|
|
||||||
A color wheel
|
|
||||||
@global
|
|
||||||
@group CPColorPanelMode
|
|
||||||
*/
|
|
||||||
CPWheelColorPickerMode = 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Slider based picker
|
|
||||||
@global
|
|
||||||
@group CPColorPanelMode
|
|
||||||
*/
|
|
||||||
CPSliderColorPickerMode = 2;
|
|
||||||
|
|
||||||
CPColorPickerViewWidth = 265;
|
|
||||||
CPColorPickerViewHeight = 370;
|
|
||||||
|
|
||||||
CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification";
|
CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification";
|
||||||
|
|
||||||
@@ -58,6 +38,22 @@ var PREVIEW_HEIGHT = 20.0,
|
|||||||
var SharedColorPanel = nil,
|
var SharedColorPanel = nil,
|
||||||
ColorPickerClasses = [];
|
ColorPickerClasses = [];
|
||||||
|
|
||||||
|
/*
|
||||||
|
A color wheel
|
||||||
|
@global
|
||||||
|
@group CPColorPanelMode
|
||||||
|
*/
|
||||||
|
CPWheelColorPickerMode = 1;
|
||||||
|
/*
|
||||||
|
Slider based picker
|
||||||
|
@global
|
||||||
|
@group CPColorPanelMode
|
||||||
|
*/
|
||||||
|
CPSliderColorPickerMode = 2;
|
||||||
|
|
||||||
|
CPColorPickerViewWidth = 265,
|
||||||
|
CPColorPickerViewHeight = 370;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPColorPanel
|
@class CPColorPanel
|
||||||
@@ -127,7 +123,7 @@ var SharedColorPanel = nil,
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
//[[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]];
|
[[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]];
|
||||||
|
|
||||||
[self setTitle:@"Color Panel"];
|
[self setTitle:@"Color Panel"];
|
||||||
[self setLevel:CPFloatingWindowLevel];
|
[self setLevel:CPFloatingWindowLevel];
|
||||||
@@ -135,7 +131,7 @@ var SharedColorPanel = nil,
|
|||||||
[self setFloatingPanel:YES];
|
[self setFloatingPanel:YES];
|
||||||
[self setBecomesKeyOnlyIfNeeded:YES];
|
[self setBecomesKeyOnlyIfNeeded:YES];
|
||||||
|
|
||||||
[self setMinSize:CGSizeMake(219.0, 363.0)];
|
[self setMinSize:CGSizeMake(219.0, 342.0)];
|
||||||
[self setMaxSize:CGSizeMake(323.0, 537.0)];
|
[self setMaxSize:CGSizeMake(323.0, 537.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +144,7 @@ var SharedColorPanel = nil,
|
|||||||
- (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];
|
||||||
|
|
||||||
@@ -252,8 +248,8 @@ var SharedColorPanel = nil,
|
|||||||
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:CGSizeMake(bounds.size.width - 10, bounds.size.height - height)];
|
[view setFrameSize:CPSizeMake(bounds.size.width - 10, bounds.size.height - height)];
|
||||||
[view setFrameOrigin:CGPointMake(5, height)];
|
[view setFrameOrigin:CPPointMake(5, height)];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_currentView removeFromSuperview];
|
[_currentView removeFromSuperview];
|
||||||
@@ -303,7 +299,7 @@ var SharedColorPanel = nil,
|
|||||||
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,
|
||||||
@@ -343,7 +339,7 @@ var SharedColorPanel = nil,
|
|||||||
|
|
||||||
[previewBox addSubview:_previewView];
|
[previewBox addSubview:_previewView];
|
||||||
|
|
||||||
var _previewLabel = [[CPTextField alloc] initWithFrame:CGRectMake(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];
|
||||||
@@ -361,14 +357,14 @@ var SharedColorPanel = nil,
|
|||||||
|
|
||||||
[swatchBox addSubview:_swatchView];
|
[swatchBox addSubview:_swatchView];
|
||||||
|
|
||||||
var _swatchLabel = [[CPTextField alloc] initWithFrame:CGRectMake(10, TOOLBAR_HEIGHT + 8 + PREVIEW_HEIGHT + 6, 60, 15)];
|
var _swatchLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 8 + PREVIEW_HEIGHT + 6, 60, 15)];
|
||||||
[_swatchLabel setStringValue:"Swatches:"];
|
[_swatchLabel setStringValue: "Swatches:"];
|
||||||
[_swatchLabel setTextColor:[CPColor blackColor]];
|
[_swatchLabel setTextColor:[CPColor blackColor]];
|
||||||
[_swatchLabel setAlignment:CPRightTextAlignment];
|
[_swatchLabel setAlignment:CPRightTextAlignment];
|
||||||
|
|
||||||
|
|
||||||
var opacityLabel = [[CPTextField alloc] initWithFrame:CGRectMake(10, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 35, 60, 20)];
|
var opacityLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 35, 60, 20)];
|
||||||
[opacityLabel setStringValue:"Opacity:"];
|
[opacityLabel setStringValue: "Opacity:"];
|
||||||
[opacityLabel setTextColor:[CPColor blackColor]];
|
[opacityLabel setTextColor:[CPColor blackColor]];
|
||||||
[opacityLabel setAlignment:CPRightTextAlignment];
|
[opacityLabel setAlignment:CPRightTextAlignment];
|
||||||
|
|
||||||
@@ -376,7 +372,6 @@ var SharedColorPanel = nil,
|
|||||||
|
|
||||||
[_opacitySlider setMinValue:0.0];
|
[_opacitySlider setMinValue:0.0];
|
||||||
[_opacitySlider setMaxValue:1.0];
|
[_opacitySlider setMaxValue:1.0];
|
||||||
[_opacitySlider setAutoresizingMask:CPViewWidthSizable];
|
|
||||||
|
|
||||||
[_opacitySlider setTarget:self];
|
[_opacitySlider setTarget:self];
|
||||||
[_opacitySlider setAction:@selector(setOpacity:)];
|
[_opacitySlider setAction:@selector(setOpacity:)];
|
||||||
@@ -393,7 +388,7 @@ var SharedColorPanel = nil,
|
|||||||
_action = nil;
|
_action = nil;
|
||||||
_activePicker = nil;
|
_activePicker = nil;
|
||||||
|
|
||||||
[_previewView setBackgroundColor:_color];
|
[_previewView setBackgroundColor: _color];
|
||||||
|
|
||||||
if (buttonForLater)
|
if (buttonForLater)
|
||||||
[self _setPicker:buttonForLater];
|
[self _setPicker:buttonForLater];
|
||||||
@@ -411,29 +406,28 @@ var SharedColorPanel = nil,
|
|||||||
|
|
||||||
|
|
||||||
CPColorDragType = "CPColorDragType";
|
CPColorDragType = "CPColorDragType";
|
||||||
|
|
||||||
var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
@implementation _CPColorPanelSwatches : CPView
|
@implementation _CPColorPanelSwatches : CPView
|
||||||
{
|
{
|
||||||
CPArray _swatches;
|
CPView[] _swatches;
|
||||||
CPColor _dragColor;
|
CPColor _dragColor;
|
||||||
CPColorPanel _colorPanel;
|
CPColorPanel _colorPanel;
|
||||||
CPCookie _swatchCookie;
|
CPCookie _swatchCookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)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 = [];
|
||||||
@@ -441,15 +435,15 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
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:CGRectMake(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);
|
||||||
}
|
}
|
||||||
@@ -466,7 +460,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
{
|
{
|
||||||
var cookieValue = [_swatchCookie value];
|
var cookieValue = [_swatchCookie value];
|
||||||
|
|
||||||
if (!cookieValue)
|
if (cookieValue == "")
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[CPColor blackColor],
|
[CPColor blackColor],
|
||||||
@@ -485,7 +479,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
result = [];
|
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;
|
||||||
}
|
}
|
||||||
@@ -500,7 +494,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
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];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setColorPanel:(CPColorPanel)panel
|
- (void)setColorPanel:(CPColorPanel)panel
|
||||||
@@ -533,14 +527,14 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
if (!CGRectContainsPoint(bounds, point) || 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];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDragged:(CPEvent)anEvent
|
- (void)mouseDragged:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||||
|
|
||||||
if (point.x > [self bounds].size.width - 1 || point.x < 1)
|
if(point.x > [self bounds].size.width - 1 || point.x < 1)
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
||||||
@@ -550,10 +544,10 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
// 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 = CGRectMakeCopy([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]];
|
||||||
@@ -561,13 +555,13 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
|
|
||||||
[dragView addSubview:dragFillView];
|
[dragView addSubview:dragFillView];
|
||||||
|
|
||||||
[self dragView:dragView
|
[self dragView: dragView
|
||||||
at:CGPointMake(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:CGPointMake(0.0, 0.0)
|
offset: CPPointMake(0.0, 0.0)
|
||||||
event:anEvent
|
event: anEvent
|
||||||
pasteboard:nil
|
pasteboard: nil
|
||||||
source:self
|
source: self
|
||||||
slideBack:YES];
|
slideBack: YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||||
@@ -585,7 +579,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
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:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex: FLOOR(location.x / 13)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -596,7 +590,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
CPColorPanel _colorPanel;
|
CPColorPanel _colorPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CPRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
@@ -637,10 +631,10 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
|
|
||||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
|
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
|
||||||
|
|
||||||
var bounds = CGRectMake(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]];
|
||||||
@@ -648,13 +642,13 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
|
|
||||||
[dragView addSubview:dragFillView];
|
[dragView addSubview:dragFillView];
|
||||||
|
|
||||||
[self dragView:dragView
|
[self dragView: dragView
|
||||||
at:CGPointMake(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:CGPointMake(0.0, 0.0)
|
offset: CPPointMake(0.0, 0.0)
|
||||||
event:anEvent
|
event: anEvent
|
||||||
pasteboard:nil
|
pasteboard: nil
|
||||||
source:self
|
source: self
|
||||||
slideBack:YES];
|
slideBack: YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||||
|
|||||||
+46
-56
@@ -24,12 +24,6 @@
|
|||||||
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
@class CPSlider
|
|
||||||
|
|
||||||
@global CPColorPickerViewWidth
|
|
||||||
@global CPColorPickerViewHeight
|
|
||||||
@global CPWheelColorPickerMode
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -50,11 +44,10 @@
|
|||||||
*/
|
*/
|
||||||
- (id)initWithPickerMask:(int)aMask colorPanel:(CPColorPanel)aPanel
|
- (id)initWithPickerMask:(int)aMask colorPanel:(CPColorPanel)aPanel
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
self = [super init];
|
||||||
{
|
|
||||||
_panel = aPanel;
|
_panel = aPanel;
|
||||||
_mask = aMask;
|
_mask = aMask;
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -83,6 +76,7 @@
|
|||||||
*/
|
*/
|
||||||
- (void)setMode:(CPColorPanelMode)mode
|
- (void)setMode:(CPColorPanelMode)mode
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -91,6 +85,7 @@
|
|||||||
*/
|
*/
|
||||||
- (void)setColor:(CPColor)aColor
|
- (void)setColor:(CPColor)aColor
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -115,7 +110,7 @@
|
|||||||
|
|
||||||
- (id)initView
|
- (id)initView
|
||||||
{
|
{
|
||||||
var aFrame = CGRectMake(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];
|
||||||
@@ -133,9 +128,9 @@
|
|||||||
[_brightnessSlider setAction:@selector(brightnessSliderDidChange:)];
|
[_brightnessSlider setAction:@selector(brightnessSliderDidChange:)];
|
||||||
[_brightnessSlider setAutoresizingMask:CPViewWidthSizable | CPViewMinYMargin];
|
[_brightnessSlider setAutoresizingMask:CPViewWidthSizable | CPViewMinYMargin];
|
||||||
|
|
||||||
_hueSaturationView = [[__CPColorWheel alloc] initWithFrame:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height - 38)];
|
_hueSaturationView = [[__CPColorWheel alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, aFrame.size.height - 38)];
|
||||||
[_hueSaturationView setDelegate:self];
|
[_hueSaturationView setDelegate: self];
|
||||||
[_hueSaturationView setAutoresizingMask:(CPViewWidthSizable | CPViewHeightSizable)];
|
[_hueSaturationView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)];
|
||||||
|
|
||||||
[_pickerView addSubview:_hueSaturationView];
|
[_pickerView addSubview:_hueSaturationView];
|
||||||
[_pickerView addSubview:_brightnessSlider];
|
[_pickerView addSubview:_brightnessSlider];
|
||||||
@@ -158,12 +153,12 @@
|
|||||||
brightness = [_brightnessSlider floatValue];
|
brightness = [_brightnessSlider floatValue];
|
||||||
|
|
||||||
[_hueSaturationView setWheelBrightness:brightness / 100.0];
|
[_hueSaturationView setWheelBrightness:brightness / 100.0];
|
||||||
[_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hue / 360.0 saturation:saturation / 100.0 brightness:1]];
|
[_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hue saturation:saturation brightness:100]];
|
||||||
|
|
||||||
var colorPanel = [self colorPanel],
|
var colorPanel = [self colorPanel],
|
||||||
opacity = [colorPanel opacity];
|
opacity = [colorPanel opacity];
|
||||||
|
|
||||||
_cachedColor = [CPColor colorWithHue:hue / 360.0 saturation:saturation / 100.0 brightness:brightness / 100.0 alpha:opacity];
|
_cachedColor = [CPColor colorWithHue:hue saturation:saturation brightness:brightness alpha:opacity];
|
||||||
|
|
||||||
[[self colorPanel] setColor:_cachedColor];
|
[[self colorPanel] setColor:_cachedColor];
|
||||||
}
|
}
|
||||||
@@ -194,10 +189,10 @@
|
|||||||
var hsb = [newColor hsbComponents];
|
var hsb = [newColor hsbComponents];
|
||||||
|
|
||||||
[_hueSaturationView setPositionToColor:newColor];
|
[_hueSaturationView setPositionToColor:newColor];
|
||||||
[_brightnessSlider setFloatValue:hsb[2] * 100.0];
|
[_brightnessSlider setFloatValue:hsb[2]];
|
||||||
[_hueSaturationView setWheelBrightness:hsb[2]];
|
[_hueSaturationView setWheelBrightness:hsb[2] / 100.0];
|
||||||
|
|
||||||
[_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hsb[0] saturation:hsb[1] brightness:1]];
|
[_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hsb[0] saturation:hsb[1] brightness:100]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPImage)provideNewButtonImage
|
- (CPImage)provideNewButtonImage
|
||||||
@@ -228,64 +223,60 @@
|
|||||||
float _radius;
|
float _radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CPRect)aFrame
|
||||||
{
|
{
|
||||||
if (self = [super initWithFrame:aFrame])
|
self = [super initWithFrame:aFrame];
|
||||||
{
|
|
||||||
|
var path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel.png"];
|
||||||
|
|
||||||
|
_wheelImage = new Image();
|
||||||
|
_wheelImage.src = path;
|
||||||
|
_wheelImage.style.position = "absolute";
|
||||||
|
|
||||||
|
path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel_black.png"];
|
||||||
|
|
||||||
|
_blackWheelImage = new Image();
|
||||||
|
_blackWheelImage.src = path;
|
||||||
|
_blackWheelImage.style.opacity = "0";
|
||||||
|
_blackWheelImage.style.filter = "alpha(opacity=0)"
|
||||||
|
_blackWheelImage.style.position = "absolute";
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
var path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel.png"];
|
_DOMElement.appendChild(_wheelImage);
|
||||||
|
_DOMElement.appendChild(_blackWheelImage);
|
||||||
_wheelImage = new Image();
|
|
||||||
_wheelImage.src = path;
|
|
||||||
_wheelImage.style.position = "absolute";
|
|
||||||
|
|
||||||
path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel_black.png"];
|
|
||||||
|
|
||||||
_blackWheelImage = new Image();
|
|
||||||
_blackWheelImage.src = path;
|
|
||||||
_blackWheelImage.style.opacity = "0";
|
|
||||||
_blackWheelImage.style.filter = "alpha(opacity=0)"
|
|
||||||
_blackWheelImage.style.position = "absolute";
|
|
||||||
|
|
||||||
_DOMElement.appendChild(_wheelImage);
|
|
||||||
_DOMElement.appendChild(_blackWheelImage);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[self setWheelSize:aFrame.size];
|
[self setWheelSize:aFrame.size];
|
||||||
|
|
||||||
_crosshair = [[CPView alloc] initWithFrame:CGRectMake(_radius - 2, _radius - 2, 4, 4)];
|
_crosshair = [[CPView alloc] initWithFrame:CPRectMake(_radius - 2, _radius - 2, 4, 4)];
|
||||||
[_crosshair setBackgroundColor:[CPColor blackColor]];
|
[_crosshair setBackgroundColor:[CPColor blackColor]];
|
||||||
|
|
||||||
var view = [[CPView alloc] initWithFrame:CGRectInset([_crosshair bounds], 1.0, 1.0)];
|
var view = [[CPView alloc] initWithFrame:CGRectInset([_crosshair bounds], 1.0, 1.0)];
|
||||||
[view setBackgroundColor:[CPColor whiteColor]];
|
[view setBackgroundColor:[CPColor whiteColor]];
|
||||||
|
|
||||||
[_crosshair addSubview:view];
|
[_crosshair addSubview:view];
|
||||||
|
|
||||||
[self addSubview:_crosshair];
|
[self addSubview:_crosshair];
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setWheelBrightness:(float)brightness
|
- (void)setWheelBrightness:(float)brightness
|
||||||
{
|
{
|
||||||
#if PLATFORM(DOM)
|
|
||||||
_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 + ")"
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFrameSize:(CGSize)aSize
|
- (void)setFrameSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
[super setFrameSize:aSize];
|
[super setFrameSize:aSize];
|
||||||
[self setWheelSize:aSize];
|
[self setWheelSize:aSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setWheelSize:(CGSize)aSize
|
- (void)setWheelSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
var min = MIN(aSize.width, aSize.height);
|
var min = MIN(aSize.width, aSize.height);
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
_blackWheelImage.style.width = min;
|
_blackWheelImage.style.width = min;
|
||||||
_blackWheelImage.style.height = min;
|
_blackWheelImage.style.height = min;
|
||||||
_blackWheelImage.width = min;
|
_blackWheelImage.width = min;
|
||||||
@@ -299,7 +290,6 @@
|
|||||||
_wheelImage.height = min;
|
_wheelImage.height = min;
|
||||||
_wheelImage.style.top = (aSize.height - min) / 2.0 + "px";
|
_wheelImage.style.top = (aSize.height - min) / 2.0 + "px";
|
||||||
_wheelImage.style.left = (aSize.width - min) / 2.0 + "px";
|
_wheelImage.style.left = (aSize.width - min) / 2.0 + "px";
|
||||||
#endif
|
|
||||||
|
|
||||||
_radius = min / 2.0;
|
_radius = min / 2.0;
|
||||||
|
|
||||||
@@ -359,15 +349,15 @@
|
|||||||
_angle = [self radiansToDegrees:angle];
|
_angle = [self radiansToDegrees:angle];
|
||||||
_distance = (distance / _radius) * 100.0;
|
_distance = (distance / _radius) * 100.0;
|
||||||
|
|
||||||
[_crosshair setFrameOrigin:CGPointMake(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] * 360.0],
|
angle = [self degreesToRadians:hsb[0]],
|
||||||
distance = hsb[1] * _radius;
|
distance = (hsb[1] / 100.0) * _radius;
|
||||||
|
|
||||||
[self setAngle:angle distance:distance];
|
[self setAngle:angle distance:distance];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPColorSpace.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Alexander Ljungberg.
|
|
||||||
* Copyright 2012, SlevenBits Ltd.
|
|
||||||
*
|
|
||||||
* 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>
|
|
||||||
|
|
||||||
var sRGBColorSpace = nil;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Represent color spaces.
|
|
||||||
*/
|
|
||||||
@implementation CPColorSpace : CPObject
|
|
||||||
{
|
|
||||||
CGColorSpace _cgColorSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Return an object representing the standard sRGB color space.
|
|
||||||
*/
|
|
||||||
+ (CPColorSpace)sRGBColorSpace
|
|
||||||
{
|
|
||||||
if (!sRGBColorSpace)
|
|
||||||
sRGBColorSpace = [[self alloc] initWithCGColorSpace:CGColorSpaceCreateDeviceRGB()];
|
|
||||||
return sRGBColorSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCGColorSpace:(CGColorSpace)cgColorSpace
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_cgColorSpace = cgColorSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Return a Core Graphics color space representing the receiver.
|
|
||||||
*/
|
|
||||||
- (CGColorSpace)CGColorSpace
|
|
||||||
{
|
|
||||||
return _cgColorSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
+78
-149
@@ -43,38 +43,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
BOOL _bordered;
|
BOOL _bordered;
|
||||||
|
|
||||||
CPColor _color;
|
CPColor _color;
|
||||||
}
|
CPView _wellView;
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding == CPValueBinding)
|
|
||||||
return [CPColorWellValueBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return @"colorwell";
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return @{
|
|
||||||
@"bezel-inset": CGInsetMakeZero(),
|
|
||||||
@"bezel-color": [CPNull null],
|
|
||||||
@"content-inset": CGInsetMake(3.0, 3.0, 3.0, 3.0),
|
|
||||||
@"content-border-inset": CGInsetMakeZero(),
|
|
||||||
@"content-border-color": [CPNull null],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_reverseSetBinding
|
|
||||||
{
|
|
||||||
var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
|
|
||||||
theBinding = [binderClass getBinding:CPValueBinding forObject:self];
|
|
||||||
|
|
||||||
[theBinding reverseSetValueFor:@"color"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
@@ -84,8 +53,11 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_active = NO;
|
_active = NO;
|
||||||
|
_bordered = YES;
|
||||||
_color = [CPColor whiteColor];
|
_color = [CPColor whiteColor];
|
||||||
[self setBordered:YES];
|
|
||||||
|
[self drawBezelWithHighlight:NO];
|
||||||
|
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||||
|
|
||||||
[self _registerForNotifications];
|
[self _registerForNotifications];
|
||||||
}
|
}
|
||||||
@@ -110,23 +82,25 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
object:[CPColorPanel sharedColorPanel]];
|
object:[CPColorPanel sharedColorPanel]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets whether the color well is bordered.
|
|
||||||
*/
|
|
||||||
- (void)setBordered:(BOOL)shouldBeBordered
|
|
||||||
{
|
|
||||||
if (shouldBeBordered)
|
|
||||||
[self setThemeState:CPThemeStateBordered];
|
|
||||||
else
|
|
||||||
[self unsetThemeState:CPThemeStateBordered];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns whether the color well is bordered
|
Returns whether the color well is bordered
|
||||||
*/
|
*/
|
||||||
- (BOOL)isBordered
|
- (BOOL)isBordered
|
||||||
{
|
{
|
||||||
return [self hasThemeState:CPThemeStateBordered];
|
return _bordered;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the color well's current color.
|
||||||
|
*/
|
||||||
|
- (void)setBordered:(BOOL)bordered
|
||||||
|
{
|
||||||
|
if (_bordered == bordered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_bordered = bordered;
|
||||||
|
|
||||||
|
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managing Color From Color Wells
|
// Managing Color From Color Wells
|
||||||
@@ -149,7 +123,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
|
|
||||||
_color = aColor;
|
_color = aColor;
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -212,6 +186,31 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
return _active;
|
return _active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drawing a Color Well
|
||||||
|
|
||||||
|
- (void)drawBezelWithHighlight:(BOOL)shouldHighlight
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Draws the colored area inside the color well without borders.
|
||||||
|
@param aRect the location at which to draw
|
||||||
|
*/
|
||||||
|
- (void)drawWellInside:(CGRect)aRect
|
||||||
|
{
|
||||||
|
if (!_wellView)
|
||||||
|
{
|
||||||
|
_wellView = [[CPView alloc] initWithFrame:aRect];
|
||||||
|
[_wellView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
|
|
||||||
|
[self addSubview:_wellView];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
[_wellView setFrame:aRect];
|
||||||
|
|
||||||
|
[_wellView setBackgroundColor:_color];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)colorPanelDidChangeColor:(CPNotification)aNotification
|
- (void)colorPanelDidChangeColor:(CPNotification)aNotification
|
||||||
{
|
{
|
||||||
[self takeColorFrom:[aNotification object]];
|
[self takeColorFrom:[aNotification object]];
|
||||||
@@ -230,11 +229,27 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
[self deactivate];
|
[self deactivate];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
[self highlight:NO];
|
if (![self isEnabled])
|
||||||
|
return;
|
||||||
|
|
||||||
if (!mouseIsUp || !CGRectContainsPoint([self bounds], aPoint) || ![self isEnabled])
|
[self drawBezelWithHighlight:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)mouseDragged:(CPEvent)anEvent
|
||||||
|
{
|
||||||
|
if (![self isEnabled])
|
||||||
|
return;
|
||||||
|
|
||||||
|
[self drawBezelWithHighlight:CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil])];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)mouseUp:(CPEvent)anEvent
|
||||||
|
{
|
||||||
|
[self drawBezelWithHighlight:NO];
|
||||||
|
|
||||||
|
if (!CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]) || ![self isEnabled])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self activate:YES];
|
[self activate:YES];
|
||||||
@@ -245,104 +260,6 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
|||||||
[colorPanel orderFront:self];
|
[colorPanel orderFront:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)contentRectForBounds:(CGRect)bounds
|
|
||||||
{
|
|
||||||
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
|
||||||
|
|
||||||
return CGRectInsetByInset(bounds, contentInset);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)bezelRectForBounds:(CGRect)bounds
|
|
||||||
{
|
|
||||||
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
|
|
||||||
|
|
||||||
return CGRectInsetByInset(bounds, bezelInset);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)contentBorderRectForBounds:(CGRect)bounds
|
|
||||||
{
|
|
||||||
var contentBorderInset = [self currentValueForThemeAttribute:@"content-border-inset"];
|
|
||||||
|
|
||||||
return CGRectInsetByInset(bounds, contentBorderInset);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
|
||||||
{
|
|
||||||
switch (aName)
|
|
||||||
{
|
|
||||||
case "bezel-view":
|
|
||||||
return [self bezelRectForBounds:[self bounds]];
|
|
||||||
case "content-view":
|
|
||||||
return [self contentRectForBounds:[self bounds]];
|
|
||||||
case "content-border-view":
|
|
||||||
return [self contentBorderRectForBounds:[self bounds]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [super rectForEphemeralSubviewNamed:aName];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
|
||||||
{
|
|
||||||
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
[view setHitTests:NO];
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
|
||||||
positioned:CPWindowBelow
|
|
||||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
|
||||||
|
|
||||||
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
|
||||||
|
|
||||||
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
|
||||||
positioned:CPWindowAbove
|
|
||||||
relativeToEphemeralSubviewNamed:@"bezel-view"];
|
|
||||||
|
|
||||||
|
|
||||||
[contentView setBackgroundColor:_color];
|
|
||||||
|
|
||||||
var contentBorderView = [self layoutEphemeralSubviewNamed:@"content-border-view"
|
|
||||||
positioned:CPWindowAbove
|
|
||||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
|
||||||
|
|
||||||
[contentBorderView setBackgroundColor:[self currentValueForThemeAttribute:@"content-border-color"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPColorWellValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
|
||||||
{
|
|
||||||
var placeholderColor = [CPColor blackColor];
|
|
||||||
|
|
||||||
[self _setPlaceholder:placeholderColor forMarker:CPMultipleValuesMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:placeholderColor forMarker:CPNoSelectionMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:placeholderColor forMarker:CPNotApplicableMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:placeholderColor forMarker:CPNullMarker isDefault:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
return [_source color];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)theBinding
|
|
||||||
{
|
|
||||||
[_source setColor:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[_source setColor:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPColorWellColorKey = "CPColorWellColorKey",
|
var CPColorWellColorKey = "CPColorWellColorKey",
|
||||||
@@ -361,8 +278,11 @@ var CPColorWellColorKey = "CPColorWellColorKey",
|
|||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_active = NO;
|
_active = NO;
|
||||||
|
_bordered = [aCoder decodeBoolForKey:CPColorWellBorderedKey];
|
||||||
_color = [aCoder decodeObjectForKey:CPColorWellColorKey];
|
_color = [aCoder decodeObjectForKey:CPColorWellColorKey];
|
||||||
[self setBordered:[aCoder decodeBoolForKey:CPColorWellBorderedKey]];
|
|
||||||
|
[self drawBezelWithHighlight:NO];
|
||||||
|
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||||
|
|
||||||
[self _registerForNotifications];
|
[self _registerForNotifications];
|
||||||
}
|
}
|
||||||
@@ -376,10 +296,19 @@ var CPColorWellColorKey = "CPColorWellColorKey",
|
|||||||
*/
|
*/
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
|
// We do this in order to avoid encoding the _wellView, which
|
||||||
|
// should just automatically be created programmatically as needed.
|
||||||
|
var actualSubviews = _subviews;
|
||||||
|
|
||||||
|
_subviews = [_subviews copy];
|
||||||
|
[_subviews removeObjectIdenticalTo:_wellView];
|
||||||
|
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
|
_subviews = actualSubviews;
|
||||||
|
|
||||||
[aCoder encodeObject:_color forKey:CPColorWellColorKey];
|
[aCoder encodeObject:_color forKey:CPColorWellColorKey];
|
||||||
[aCoder encodeObject:[self isBordered] forKey:CPColorWellBorderedKey];
|
[aCoder encodeObject:_bordered forKey:CPColorWellBorderedKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
-1198
File diff suppressed because it is too large
Load Diff
+49
-216
@@ -20,7 +20,7 @@
|
|||||||
* 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 "CPEvent_Constants.j"
|
@import "CPEvent.j"
|
||||||
@import "CPPlatform.j"
|
@import "CPPlatform.j"
|
||||||
|
|
||||||
|
|
||||||
@@ -38,58 +38,41 @@ CPWindowsOperatingSystem = 1;
|
|||||||
CPOtherOperatingSystem = 2;
|
CPOtherOperatingSystem = 2;
|
||||||
|
|
||||||
// Features
|
// Features
|
||||||
CPCSSRGBAFeature = 5;
|
CPCSSRGBAFeature = 1 << 5;
|
||||||
|
|
||||||
CPHTMLCanvasFeature = 6;
|
CPHTMLCanvasFeature = 1 << 6;
|
||||||
CPHTMLContentEditableFeature = 7;
|
CPHTMLContentEditableFeature = 1 << 7;
|
||||||
CPHTMLDragAndDropFeature = 8;
|
CPHTMLDragAndDropFeature = 1 << 8;
|
||||||
|
|
||||||
CPJavaScriptInnerTextFeature = 9;
|
CPJavascriptInnerTextFeature = 1 << 9;
|
||||||
CPJavaScriptTextContentFeature = 10;
|
CPJavascriptTextContentFeature = 1 << 10;
|
||||||
CPJavaScriptClipboardEventsFeature = 11;
|
CPJavascriptClipboardEventsFeature = 1 << 11;
|
||||||
CPJavaScriptClipboardAccessFeature = 12;
|
CPJavascriptClipboardAccessFeature = 1 << 12;
|
||||||
CPJavaScriptCanvasDrawFeature = 13;
|
CPJavaScriptCanvasDrawFeature = 1 << 13;
|
||||||
CPJavaScriptCanvasTransformFeature = 14;
|
CPJavaScriptCanvasTransformFeature = 1 << 14;
|
||||||
|
|
||||||
CPVMLFeature = 15;
|
CPVMLFeature = 1 << 15;
|
||||||
|
|
||||||
CPJavaScriptRemedialKeySupport = 16;
|
CPJavascriptRemedialKeySupport = 1 << 16;
|
||||||
CPJavaScriptShadowFeature = 20;
|
CPJavaScriptShadowFeature = 1 << 20;
|
||||||
|
|
||||||
CPJavaScriptNegativeMouseWheelValues = 22;
|
CPJavaScriptNegativeMouseWheelValues = 1 << 22;
|
||||||
CPJavaScriptMouseWheelValues_8_15 = 23;
|
CPJavaScriptMouseWheelValues_8_15 = 1 << 23
|
||||||
|
|
||||||
CPOpacityRequiresFilterFeature = 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 = 25;
|
CPInputTypeCanBeChangedFeature = 1 << 25;
|
||||||
CPHTML5DragAndDropSourceYOffBy1 = 26;
|
CPHTML5DragAndDropSourceYOffBy1 = 1 << 26;
|
||||||
|
|
||||||
CPSOPDisabledFromFileURLs = 27;
|
|
||||||
|
|
||||||
// element.style.font can be set for an element not in the DOM.
|
|
||||||
CPInputSetFontOutsideOfDOM = 28;
|
|
||||||
|
|
||||||
// Input elements have 1 px of extra padding on the left regardless of padding setting.
|
|
||||||
CPInput1PxLeftPadding = 29;
|
|
||||||
CPInputOnInputEventFeature = 30;
|
|
||||||
|
|
||||||
CPFileAPIFeature = 31;
|
|
||||||
|
|
||||||
/*
|
|
||||||
When an absolutely positioned div (CPView) with an absolutely positioned canvas in it (CPView with drawRect:) moves things on top of the canvas (subviews) don't redraw correctly. E.g. if you have a bunch of text fields in a CPBox in a sheet which animates in, some of the text fields might not be visible because the CPBox has a canvas at the bottom and the box moved form offscreen to onscreen. This bug is probably very related: https://bugs.webkit.org/show_bug.cgi?id=67203
|
|
||||||
*/
|
|
||||||
CPCanvasParentDrawErrorsOnMovementBug = 1 << 0;
|
|
||||||
|
|
||||||
var USER_AGENT = "",
|
var USER_AGENT = "",
|
||||||
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
||||||
PLATFORM_FEATURES = [],
|
PLATFORM_FEATURES = 0;
|
||||||
PLATFORM_BUGS = 0,
|
|
||||||
PLATFORM_STYLE_JS_PROPERTIES = {};
|
|
||||||
|
|
||||||
// default these features to true
|
// default these features to true
|
||||||
PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = YES;
|
|
||||||
PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = YES;
|
PLATFORM_FEATURES |= CPInputTypeCanBeChangedFeature;
|
||||||
|
|
||||||
if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
|
if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
|
||||||
USER_AGENT = window.navigator.userAgent;
|
USER_AGENT = window.navigator.userAgent;
|
||||||
@@ -99,7 +82,7 @@ if (typeof window !== "undefined" && window.opera)
|
|||||||
{
|
{
|
||||||
PLATFORM_ENGINE = CPOperaBrowserEngine;
|
PLATFORM_ENGINE = CPOperaBrowserEngine;
|
||||||
|
|
||||||
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internet Explorer
|
// Internet Explorer
|
||||||
@@ -108,16 +91,13 @@ else if (typeof window !== "undefined" && window.attachEvent) // Must follow Ope
|
|||||||
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] = YES;
|
PLATFORM_FEATURES |= CPVMLFeature;
|
||||||
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
|
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||||
PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
|
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||||
|
|
||||||
PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = YES;
|
PLATFORM_FEATURES |= CPOpacityRequiresFilterFeature;
|
||||||
|
|
||||||
PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = NO;
|
PLATFORM_FEATURES &= ~CPInputTypeCanBeChangedFeature;
|
||||||
|
|
||||||
// Tested in Internet Explore 8 and 9.
|
|
||||||
PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = NO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebKit
|
// WebKit
|
||||||
@@ -126,15 +106,15 @@ 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] = YES;
|
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||||
PLATFORM_FEATURES[CPHTMLContentEditableFeature] = YES;
|
PLATFORM_FEATURES |= CPHTMLContentEditableFeature;
|
||||||
|
|
||||||
if (USER_AGENT.indexOf("Chrome") === -1)
|
if (USER_AGENT.indexOf("Chrome") === -1)
|
||||||
PLATFORM_FEATURES[CPHTMLDragAndDropFeature] = YES;
|
PLATFORM_FEATURES |= CPHTMLDragAndDropFeature;
|
||||||
|
|
||||||
PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
|
PLATFORM_FEATURES |= CPJavascriptClipboardEventsFeature;
|
||||||
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = YES;
|
PLATFORM_FEATURES |= CPJavascriptClipboardAccessFeature;
|
||||||
PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
|
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),
|
||||||
@@ -144,26 +124,15 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
|||||||
minorVersion = parseInt(versionString.substr(versionDivision + 1));
|
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("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
|
||||||
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
|
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||||
|
|
||||||
// FIXME this is a terrible hack to get around this bug:
|
// FIXME this is a terrible hack to get around this bug:
|
||||||
// https://bugs.webkit.org/show_bug.cgi?id=21548
|
// https://bugs.webkit.org/show_bug.cgi?id=21548
|
||||||
if (![CPPlatform isBrowser])
|
if (![CPPlatform isBrowser])
|
||||||
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
|
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||||
|
|
||||||
if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
|
if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
|
||||||
PLATFORM_FEATURES[CPHTML5DragAndDropSourceYOffBy1] = YES;
|
PLATFORM_FEATURES |= CPHTML5DragAndDropSourceYOffBy1;
|
||||||
|
|
||||||
// This is supposedly fixed in webkit r123603. Seems to work in Chrome 21 but not Safari 6.0.
|
|
||||||
if (majorVersion < 537)
|
|
||||||
PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
|
|
||||||
|
|
||||||
if (USER_AGENT.indexOf("Chrome") === CPNotFound)
|
|
||||||
PLATFORM_FEATURES[CPSOPDisabledFromFileURLs] = YES;
|
|
||||||
|
|
||||||
// Assume this bug was introduced around Safari 5.1/Chrome 16. This could probably be tighter.
|
|
||||||
if (majorVersion > 533)
|
|
||||||
PLATFORM_BUGS |= CPCanvasParentDrawErrorsOnMovementBug;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// KHTML
|
// KHTML
|
||||||
@@ -177,79 +146,46 @@ else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
|
|||||||
{
|
{
|
||||||
PLATFORM_ENGINE = CPGeckoBrowserEngine;
|
PLATFORM_ENGINE = CPGeckoBrowserEngine;
|
||||||
|
|
||||||
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
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] = YES;
|
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||||
|
|
||||||
if (version < 3.0)
|
if (version < 3.0)
|
||||||
PLATFORM_FEATURES[CPJavaScriptMouseWheelValues_8_15] = YES;
|
PLATFORM_FEATURES |= CPJavaScriptMouseWheelValues_8_15;
|
||||||
|
|
||||||
// Some day this might be fixed and should be version prefixed. No known fixed version yet.
|
|
||||||
PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Feature-specific checks
|
// Feature Specific Checks
|
||||||
if (typeof document != "undefined")
|
if (typeof document != "undefined")
|
||||||
{
|
{
|
||||||
var canvasElement = document.createElement("canvas");
|
var canvasElement = document.createElement("canvas");
|
||||||
|
// Detect Canvas Support
|
||||||
// Detect canvas support
|
|
||||||
if (canvasElement && canvasElement.getContext)
|
if (canvasElement && canvasElement.getContext)
|
||||||
{
|
{
|
||||||
PLATFORM_FEATURES[CPHTMLCanvasFeature] = YES;
|
PLATFORM_FEATURES |= CPHTMLCanvasFeature;
|
||||||
|
|
||||||
// Any browser that supports canvas supports CSS opacity
|
// Detect Canvas setTransform/transform support
|
||||||
PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = NO;
|
|
||||||
|
|
||||||
// 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] = YES;
|
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] = YES;
|
PLATFORM_FEATURES |= CPJavascriptInnerTextFeature;
|
||||||
else if (DOMElement.textContent != undefined)
|
else if (DOMElement.textContent != undefined)
|
||||||
PLATFORM_FEATURES[CPJavaScriptTextContentFeature] = YES;
|
PLATFORM_FEATURES |= CPJavascriptTextContentFeature;
|
||||||
|
|
||||||
var DOMInputElement = document.createElement("input");
|
|
||||||
|
|
||||||
if ("oninput" in DOMInputElement)
|
|
||||||
PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
|
|
||||||
else if (typeof DOMInputElement.setAttribute === "function")
|
|
||||||
{
|
|
||||||
DOMInputElement.setAttribute("oninput", "return;");
|
|
||||||
|
|
||||||
if (typeof DOMInputElement.oninput === "function")
|
|
||||||
PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect FileAPI support
|
|
||||||
if (typeof DOMInputElement.setAttribute === "function")
|
|
||||||
{
|
|
||||||
DOMInputElement.setAttribute("type", "file");
|
|
||||||
PLATFORM_FEATURES[CPFileAPIFeature] = !!DOMInputElement["files"];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
PLATFORM_FEATURES[CPFileAPIFeature] = NO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function CPFeatureIsCompatible(aFeature)
|
function CPFeatureIsCompatible(aFeature)
|
||||||
{
|
{
|
||||||
return !!PLATFORM_FEATURES[aFeature];
|
return PLATFORM_FEATURES & aFeature;
|
||||||
}
|
|
||||||
|
|
||||||
function CPPlatformHasBug(aBug)
|
|
||||||
{
|
|
||||||
return PLATFORM_BUGS & aBug;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function CPBrowserIsEngine(anEngine)
|
function CPBrowserIsEngine(anEngine)
|
||||||
@@ -289,106 +225,3 @@ else
|
|||||||
CPUndoKeyEquivalentModifierMask = CPControlKeyMask;
|
CPUndoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||||
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
|
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Return the properly prefixed JS property for the given name. E.g. in a webkit browser,
|
|
||||||
CPBrowserStyleProperty('transition') -> WebkitTransition
|
|
||||||
|
|
||||||
While technically not a style property, style related event handler names are also supported.
|
|
||||||
CPBrowserStyleProperty('transitionend') -> 'webkitTransitionEnd'
|
|
||||||
|
|
||||||
CSS is only available in platform(dom), so don't rely too heavily on it.
|
|
||||||
*/
|
|
||||||
function CPBrowserStyleProperty(aProperty)
|
|
||||||
{
|
|
||||||
var lowerProperty = aProperty.toLowerCase();
|
|
||||||
|
|
||||||
if (PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] === undefined)
|
|
||||||
{
|
|
||||||
var r = nil;
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var testElement = document.createElement('div');
|
|
||||||
|
|
||||||
switch (lowerProperty)
|
|
||||||
{
|
|
||||||
case 'transitionend':
|
|
||||||
var candidates = {
|
|
||||||
'WebkitTransition' : 'webkitTransitionEnd',
|
|
||||||
'MozTransition' : 'transitionend',
|
|
||||||
'OTransition' : 'oTransitionEnd',
|
|
||||||
'msTransition' : 'MSTransitionEnd',
|
|
||||||
'transition' : 'transitionend'
|
|
||||||
};
|
|
||||||
|
|
||||||
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transition']] || nil;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
var prefixes = ["Webkit", "Moz", "O", "ms"],
|
|
||||||
strippedProperty = aProperty.split('-').join(' '),
|
|
||||||
capProperty = [strippedProperty capitalizedString].split(' ').join('');
|
|
||||||
|
|
||||||
for (var i = 0; i < prefixes.length; i++)
|
|
||||||
{
|
|
||||||
// First check if the property is already valid without being formatted, otherwise try the capitalized property
|
|
||||||
if (prefixes[i] + aProperty in testElement.style)
|
|
||||||
{
|
|
||||||
r = prefixes[i] + aProperty;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (prefixes[i] + capProperty in testElement.style)
|
|
||||||
{
|
|
||||||
r = prefixes[i] + capProperty;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!r && lowerProperty in testElement.style)
|
|
||||||
r = lowerProperty;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PLATFORM_STYLE_JS_PROPERTIES[lowerProperty];
|
|
||||||
}
|
|
||||||
|
|
||||||
function CPBrowserCSSProperty(aProperty)
|
|
||||||
{
|
|
||||||
var browserProperty = CPBrowserStyleProperty(aProperty);
|
|
||||||
|
|
||||||
if (!browserProperty)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
var prefixes = {
|
|
||||||
'Webkit': '-webkit-',
|
|
||||||
'Moz': '-moz-',
|
|
||||||
'O': '-o-',
|
|
||||||
'ms': '-ms-'
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var prefix in prefixes)
|
|
||||||
{
|
|
||||||
if (browserProperty.substring(0, prefix.length) == prefix)
|
|
||||||
{
|
|
||||||
var browserPropertyWithoutPrefix = browserProperty.substring(prefix.length),
|
|
||||||
parts = browserPropertyWithoutPrefix.match(/[A-Z][a-z]+/g);
|
|
||||||
|
|
||||||
// If there were any capitalized words in the browserProperty, insert a "-" between each one
|
|
||||||
if (parts && parts.length > 0)
|
|
||||||
browserPropertyWithoutPrefix = parts.join("-");
|
|
||||||
|
|
||||||
return prefixes[prefix] + browserPropertyWithoutPrefix.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var parts = browserProperty.match(/[A-Z][a-z]+/g);
|
|
||||||
|
|
||||||
if (parts && parts.length > 0)
|
|
||||||
browserProperty = parts.join("-");
|
|
||||||
|
|
||||||
return browserProperty.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|||||||
+136
-338
@@ -20,46 +20,36 @@
|
|||||||
* 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/CPFormatter.j>
|
|
||||||
@import <Foundation/CPTimer.j>
|
|
||||||
|
|
||||||
@import "CPFont.j"
|
@import "CPFont.j"
|
||||||
@import "CPShadow.j"
|
@import "CPShadow.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
@import "CPKeyValueBinding.j"
|
@import "CPKeyValueBinding.j"
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
CPLeftTextAlignment = 0;
|
CPLeftTextAlignment = 0;
|
||||||
CPRightTextAlignment = 1;
|
CPRightTextAlignment = 1;
|
||||||
CPCenterTextAlignment = 2;
|
CPCenterTextAlignment = 2;
|
||||||
CPJustifiedTextAlignment = 3;
|
CPJustifiedTextAlignment = 3;
|
||||||
CPNaturalTextAlignment = 4;
|
CPNaturalTextAlignment = 4;
|
||||||
|
|
||||||
CPRegularControlSize = 0;
|
CPRegularControlSize = 0;
|
||||||
CPSmallControlSize = 1;
|
CPSmallControlSize = 1;
|
||||||
CPMiniControlSize = 2;
|
CPMiniControlSize = 2;
|
||||||
|
|
||||||
CPLineBreakByWordWrapping = 0;
|
CPLineBreakByWordWrapping = 0;
|
||||||
CPLineBreakByCharWrapping = 1;
|
CPLineBreakByCharWrapping = 1;
|
||||||
CPLineBreakByClipping = 2;
|
CPLineBreakByClipping = 2;
|
||||||
CPLineBreakByTruncatingHead = 3;
|
CPLineBreakByTruncatingHead = 3;
|
||||||
CPLineBreakByTruncatingTail = 4;
|
CPLineBreakByTruncatingTail = 4;
|
||||||
CPLineBreakByTruncatingMiddle = 5;
|
CPLineBreakByTruncatingMiddle = 5;
|
||||||
|
|
||||||
CPTopVerticalTextAlignment = 1;
|
CPTopVerticalTextAlignment = 1,
|
||||||
CPCenterVerticalTextAlignment = 2;
|
CPCenterVerticalTextAlignment = 2,
|
||||||
CPBottomVerticalTextAlignment = 3;
|
CPBottomVerticalTextAlignment = 3;
|
||||||
|
|
||||||
// Deprecated for use with images, use the CPImageScale constants
|
CPScaleProportionally = 0;
|
||||||
CPScaleProportionally = 0;
|
CPScaleToFit = 1;
|
||||||
CPScaleToFit = 1;
|
CPScaleNone = 2;
|
||||||
CPScaleNone = 2;
|
|
||||||
|
|
||||||
CPImageScaleProportionallyDown = 0;
|
|
||||||
CPImageScaleAxesIndependently = 1;
|
|
||||||
CPImageScaleNone = 2;
|
|
||||||
CPImageScaleProportionallyUpOrDown = 3;
|
|
||||||
|
|
||||||
CPNoImage = 0;
|
CPNoImage = 0;
|
||||||
CPImageOnly = 1;
|
CPImageOnly = 1;
|
||||||
@@ -69,9 +59,9 @@ CPImageBelow = 4;
|
|||||||
CPImageAbove = 5;
|
CPImageAbove = 5;
|
||||||
CPImageOverlaps = 6;
|
CPImageOverlaps = 6;
|
||||||
|
|
||||||
CPOnState = 1;
|
CPOnState = 1;
|
||||||
CPOffState = 0;
|
CPOffState = 0;
|
||||||
CPMixedState = -1;
|
CPMixedState = -1;
|
||||||
|
|
||||||
CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
|
CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
|
||||||
CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
|
CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
|
||||||
@@ -82,7 +72,7 @@ CPControlTextDidBeginEditingNotification = "CPControlTextDidBeginEditingNotif
|
|||||||
CPControlTextDidChangeNotification = "CPControlTextDidChangeNotification";
|
CPControlTextDidChangeNotification = "CPControlTextDidChangeNotification";
|
||||||
CPControlTextDidEndEditingNotification = "CPControlTextDidEndEditingNotification";
|
CPControlTextDidEndEditingNotification = "CPControlTextDidEndEditingNotification";
|
||||||
|
|
||||||
var CPControlBlackColor = [CPColor blackColor];
|
var CPControlBlackColor = [CPColor blackColor];
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -93,7 +83,6 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
@implementation CPControl : CPView
|
@implementation CPControl : CPView
|
||||||
{
|
{
|
||||||
id _value;
|
id _value;
|
||||||
CPFormatter _formatter @accessors(property=formatter);
|
|
||||||
|
|
||||||
// Target-Action Support
|
// Target-Action Support
|
||||||
id _target;
|
id _target;
|
||||||
@@ -106,71 +95,66 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
BOOL _trackingWasWithinFrame;
|
BOOL _trackingWasWithinFrame;
|
||||||
unsigned _trackingMouseDownFlags;
|
unsigned _trackingMouseDownFlags;
|
||||||
CGPoint _previousTrackingLocation;
|
CGPoint _previousTrackingLocation;
|
||||||
|
|
||||||
|
CPString _toolTip;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPDictionary)themeAttributes
|
+ (CPDictionary)themeAttributes
|
||||||
{
|
{
|
||||||
return @{
|
return [CPDictionary dictionaryWithObjects:[CPLeftTextAlignment,
|
||||||
@"alignment": CPLeftTextAlignment,
|
CPTopVerticalTextAlignment,
|
||||||
@"vertical-alignment": CPTopVerticalTextAlignment,
|
CPLineBreakByClipping,
|
||||||
@"line-break-mode": CPLineBreakByClipping,
|
[CPColor blackColor],
|
||||||
@"text-color": [CPColor blackColor],
|
[CPFont systemFontOfSize:12.0],
|
||||||
@"font": [CPFont systemFontOfSize:CPFontCurrentSystemSize],
|
[CPNull null],
|
||||||
@"text-shadow-color": [CPNull null],
|
_CGSizeMakeZero(),
|
||||||
@"text-shadow-offset": CGSizeMakeZero(),
|
CPImageLeft,
|
||||||
@"image-position": CPImageLeft,
|
CPScaleToFit,
|
||||||
@"image-scaling": CPScaleToFit,
|
_CGSizeMakeZero(),
|
||||||
@"min-size": CGSizeMakeZero(),
|
_CGSizeMake(-1.0, -1.0)]
|
||||||
@"max-size": CGSizeMake(-1.0, -1.0),
|
forKeys:[@"alignment",
|
||||||
};
|
@"vertical-alignment",
|
||||||
|
@"line-break-mode",
|
||||||
|
@"text-color",
|
||||||
|
@"font",
|
||||||
|
@"text-shadow-color",
|
||||||
|
@"text-shadow-offset",
|
||||||
|
@"image-position",
|
||||||
|
@"image-scaling",
|
||||||
|
@"min-size",
|
||||||
|
@"max-size"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self !== [CPControl class])
|
if (self === [CPControl class])
|
||||||
return;
|
{
|
||||||
|
[self exposeBinding:@"value"];
|
||||||
|
[self exposeBinding:@"objectValue"];
|
||||||
|
[self exposeBinding:@"stringValue"];
|
||||||
|
[self exposeBinding:@"integerValue"];
|
||||||
|
[self exposeBinding:@"intValue"];
|
||||||
|
[self exposeBinding:@"doubleValue"];
|
||||||
|
[self exposeBinding:@"floatValue"];
|
||||||
|
|
||||||
[self exposeBinding:@"value"];
|
[self exposeBinding:@"enabled"];
|
||||||
[self exposeBinding:@"objectValue"];
|
}
|
||||||
[self exposeBinding:@"stringValue"];
|
|
||||||
[self exposeBinding:@"integerValue"];
|
|
||||||
[self exposeBinding:@"intValue"];
|
|
||||||
[self exposeBinding:@"doubleValue"];
|
|
||||||
[self exposeBinding:@"floatValue"];
|
|
||||||
|
|
||||||
[self exposeBinding:@"enabled"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding === CPValueBinding)
|
|
||||||
return [_CPValueBinder class];
|
|
||||||
else if ([aBinding hasPrefix:CPEnabledBinding])
|
|
||||||
return [CPMultipleValueAndBinding class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Reverse set the binding iff the CPContinuouslyUpdatesValueBindingOption is set.
|
|
||||||
*/
|
|
||||||
- (void)_continuouslyReverseSetBinding
|
|
||||||
{
|
|
||||||
var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
|
|
||||||
theBinding = [binderClass getBinding:CPValueBinding forObject:self];
|
|
||||||
|
|
||||||
if ([theBinding continuouslyUpdatesValue])
|
|
||||||
[theBinding reverseSetValueFor:@"objectValue"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_reverseSetBinding
|
- (void)_reverseSetBinding
|
||||||
{
|
{
|
||||||
var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
|
var theBinding = [CPKeyValueBinding getBinding:CPValueBinding forObject:self];
|
||||||
theBinding = [binderClass getBinding:CPValueBinding forObject:self];
|
|
||||||
|
|
||||||
[theBinding reverseSetValueFor:@"objectValue"];
|
[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];
|
||||||
@@ -185,8 +169,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's target action.
|
Sets the receiver's target action
|
||||||
|
|
||||||
@param anAction Sets the action message that gets sent to the target.
|
@param anAction Sets the action message that gets sent to the target.
|
||||||
*/
|
*/
|
||||||
- (void)setAction:(SEL)anAction
|
- (void)setAction:(SEL)anAction
|
||||||
@@ -195,7 +178,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's target action.
|
Returns the receiver's target action
|
||||||
*/
|
*/
|
||||||
- (SEL)action
|
- (SEL)action
|
||||||
{
|
{
|
||||||
@@ -204,7 +187,6 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's target. The target receives action messages from the receiver.
|
Sets the receiver's target. The target receives action messages from the receiver.
|
||||||
|
|
||||||
@param aTarget the object that will receive the message specified by action
|
@param aTarget the object that will receive the message specified by action
|
||||||
*/
|
*/
|
||||||
- (void)setTarget:(id)aTarget
|
- (void)setTarget:(id)aTarget
|
||||||
@@ -222,18 +204,13 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Causes \c anAction to be sent to \c anObject.
|
Causes \c anAction to be sent to \c anObject.
|
||||||
|
|
||||||
@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
|
||||||
*/
|
*/
|
||||||
- (BOOL)sendAction:(SEL)anAction to:(id)anObject
|
- (void)sendAction:(SEL)anAction to:(id)anObject
|
||||||
{
|
{
|
||||||
[self _reverseSetBinding];
|
[self _reverseSetBinding];
|
||||||
|
[CPApp sendAction:anAction to:anObject from:self];
|
||||||
var binding = [CPBinder getBinding:CPTargetBinding forObject:self];
|
|
||||||
[binding invokeAction];
|
|
||||||
|
|
||||||
return [CPApp sendAction:anAction to:anObject from:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)sendActionOn:(int)mask
|
- (int)sendActionOn:(int)mask
|
||||||
@@ -245,6 +222,33 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
return previousMask;
|
return previousMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the tooltip for the receiver.
|
||||||
|
@param aToolTip the tooltip
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
-(void)setToolTip:(CPString)aToolTip
|
||||||
|
{
|
||||||
|
if (_toolTip == aToolTip)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_toolTip = aToolTip;
|
||||||
|
|
||||||
|
#if PLATFORM(DOM)
|
||||||
|
_DOMElement.title = aToolTip;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
Returns the receiver's tooltip
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
-(CPString)toolTip
|
||||||
|
{
|
||||||
|
return _toolTip;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns whether the control can continuously send its action messages.
|
Returns whether the control can continuously send its action messages.
|
||||||
*/
|
*/
|
||||||
@@ -256,7 +260,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets whether the cell can continuously send its action messages.
|
Sets whether the cell can continuously send its action messages.
|
||||||
*/
|
*/
|
||||||
- (void)setContinuous:(BOOL)flag
|
- (void)setContinuous:(BOOL)flag
|
||||||
{
|
{
|
||||||
// Some subclasses should redefine this with CPLeftMouseDraggedMask
|
// Some subclasses should redefine this with CPLeftMouseDraggedMask
|
||||||
@@ -266,9 +270,6 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
_sendActionOn &= ~CPPeriodicMask;
|
_sendActionOn &= ~CPPeriodicMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns YES if the receiver tracks the mouse outside the frame, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)tracksMouseOutsideOfFrame
|
- (BOOL)tracksMouseOutsideOfFrame
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
@@ -285,14 +286,9 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
[self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:YES];
|
[self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:YES];
|
||||||
|
|
||||||
_trackingMouseDownFlags = 0;
|
_trackingMouseDownFlags = 0;
|
||||||
|
|
||||||
if (isWithinFrame)
|
|
||||||
[self setThemeState:CPThemeStateHovered];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[self unsetThemeState:CPThemeStateHovered];
|
|
||||||
|
|
||||||
if (type === CPLeftMouseDown)
|
if (type === CPLeftMouseDown)
|
||||||
{
|
{
|
||||||
_trackingMouseDownFlags = [anEvent modifierFlags];
|
_trackingMouseDownFlags = [anEvent modifierFlags];
|
||||||
@@ -331,11 +327,6 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Perform a click on the receiver.
|
|
||||||
|
|
||||||
@param sender - The sender object
|
|
||||||
*/
|
|
||||||
- (void)performClick:(id)sender
|
- (void)performClick:(id)sender
|
||||||
{
|
{
|
||||||
if (![self isEnabled])
|
if (![self isEnabled])
|
||||||
@@ -358,18 +349,11 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
Fired when the button timer finished, usually after the user hits enter.
|
|
||||||
*/
|
|
||||||
- (void)unhighlightButtonTimerDidFinish:(id)sender
|
- (void)unhighlightButtonTimerDidFinish:(id)sender
|
||||||
{
|
{
|
||||||
[self highlight:NO];
|
[self highlight:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the mask of modifier keys held down when the user clicked.
|
|
||||||
*/
|
|
||||||
- (unsigned)mouseDownFlags
|
- (unsigned)mouseDownFlags
|
||||||
{
|
{
|
||||||
return _trackingMouseDownFlags;
|
return _trackingMouseDownFlags;
|
||||||
@@ -389,18 +373,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
|
|
||||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
||||||
{
|
{
|
||||||
if (mouseIsUp)
|
[self highlight:NO];
|
||||||
[self highlight:NO];
|
|
||||||
else
|
|
||||||
[self highlight:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Enabled controls accept first mouse by default.
|
|
||||||
*/
|
|
||||||
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
return [self isEnabled];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
@@ -411,27 +384,8 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
[self trackMouse:anEvent];
|
[self trackMouse:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseEntered:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
if (![self isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self setThemeState:CPThemeStateHovered];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseExited:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
var currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
|
||||||
isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
|
|
||||||
|
|
||||||
// Make sure we're not still in the frame because Cappuccino will sent mouseExited events
|
|
||||||
// for all of the (ephemeral) subviews of a view as well.
|
|
||||||
if (!isWithinFrame)
|
|
||||||
[self unsetThemeState:CPThemeStateHovered];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's object value.
|
Returns the receiver's object value
|
||||||
*/
|
*/
|
||||||
- (id)objectValue
|
- (id)objectValue
|
||||||
{
|
{
|
||||||
@@ -439,7 +393,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's object value.
|
Set's the receiver's object value
|
||||||
*/
|
*/
|
||||||
- (void)setObjectValue:(id)anObject
|
- (void)setObjectValue:(id)anObject
|
||||||
{
|
{
|
||||||
@@ -450,7 +404,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's float value.
|
Returns the receiver's float value
|
||||||
*/
|
*/
|
||||||
- (float)floatValue
|
- (float)floatValue
|
||||||
{
|
{
|
||||||
@@ -459,7 +413,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's float value.
|
Sets the receiver's float value
|
||||||
*/
|
*/
|
||||||
- (void)setFloatValue:(float)aValue
|
- (void)setFloatValue:(float)aValue
|
||||||
{
|
{
|
||||||
@@ -467,7 +421,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's double value.
|
Returns the receiver's double value
|
||||||
*/
|
*/
|
||||||
- (double)doubleValue
|
- (double)doubleValue
|
||||||
{
|
{
|
||||||
@@ -476,7 +430,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's double value.
|
Set's the receiver's double value
|
||||||
*/
|
*/
|
||||||
- (void)setDoubleValue:(double)anObject
|
- (void)setDoubleValue:(double)anObject
|
||||||
{
|
{
|
||||||
@@ -484,7 +438,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's int value.
|
Returns the receiver's int value
|
||||||
*/
|
*/
|
||||||
- (int)intValue
|
- (int)intValue
|
||||||
{
|
{
|
||||||
@@ -493,7 +447,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's int value.
|
Set's the receiver's int value
|
||||||
*/
|
*/
|
||||||
- (void)setIntValue:(int)anObject
|
- (void)setIntValue:(int)anObject
|
||||||
{
|
{
|
||||||
@@ -501,7 +455,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's int value.
|
Returns the receiver's int value
|
||||||
*/
|
*/
|
||||||
- (int)integerValue
|
- (int)integerValue
|
||||||
{
|
{
|
||||||
@@ -510,7 +464,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's int value.
|
Set's the receiver's int value
|
||||||
*/
|
*/
|
||||||
- (void)setIntegerValue:(int)anObject
|
- (void)setIntegerValue:(int)anObject
|
||||||
{
|
{
|
||||||
@@ -518,50 +472,19 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's string value.
|
Returns the receiver's string value
|
||||||
*/
|
*/
|
||||||
- (CPString)stringValue
|
- (CPString)stringValue
|
||||||
{
|
{
|
||||||
if (_formatter && _value !== undefined)
|
|
||||||
{
|
|
||||||
var formattedValue = [self hasThemeState:CPThemeStateEditing] ? [_formatter editingStringForObjectValue:_value] : [_formatter stringForObjectValue:_value];
|
|
||||||
|
|
||||||
if (formattedValue !== nil && formattedValue !== undefined)
|
|
||||||
return formattedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (_value === undefined || _value === nil) ? "" : String(_value);
|
return (_value === undefined || _value === nil) ? "" : String(_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's string value.
|
Set's the receiver's string value
|
||||||
*/
|
*/
|
||||||
- (void)setStringValue:(CPString)aString
|
- (void)setStringValue:(CPString)anObject
|
||||||
{
|
{
|
||||||
// Cocoa raises an invalid parameter assertion and returns if you pass nil.
|
[self setObjectValue:anObject];
|
||||||
if (aString === nil || aString === undefined)
|
|
||||||
{
|
|
||||||
CPLog.warn("nil or undefined sent to CPControl -setStringValue");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var value;
|
|
||||||
|
|
||||||
if (_formatter)
|
|
||||||
{
|
|
||||||
value = nil;
|
|
||||||
|
|
||||||
if ([_formatter getObjectValue:@ref(value) forString:aString errorDescription:nil] === NO)
|
|
||||||
{
|
|
||||||
// If the given string is non-empty and doesn't work, Cocoa tries an empty string.
|
|
||||||
if (!aString || [_formatter getObjectValue:@ref(value) forString:@"" errorDescription:nil] === NO)
|
|
||||||
value = undefined; // Means the value is invalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
value = aString;
|
|
||||||
|
|
||||||
[self setObjectValue:value];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)takeDoubleValueFrom:(id)sender
|
- (void)takeDoubleValueFrom:(id)sender
|
||||||
@@ -577,18 +500,21 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
[self setFloatValue:[sender floatValue]];
|
[self setFloatValue:[sender floatValue]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)takeIntegerValueFrom:(id)sender
|
- (void)takeIntegerValueFrom:(id)sender
|
||||||
{
|
{
|
||||||
if ([sender respondsToSelector:@selector(integerValue)])
|
if ([sender respondsToSelector:@selector(integerValue)])
|
||||||
[self setIntegerValue:[sender integerValue]];
|
[self setIntegerValue:[sender integerValue]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)takeIntValueFrom:(id)sender
|
- (void)takeIntValueFrom:(id)sender
|
||||||
{
|
{
|
||||||
if ([sender respondsToSelector:@selector(intValue)])
|
if ([sender respondsToSelector:@selector(intValue)])
|
||||||
[self setIntValue:[sender intValue]];
|
[self setIntValue:[sender intValue]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)takeObjectValueFrom:(id)sender
|
- (void)takeObjectValueFrom:(id)sender
|
||||||
{
|
{
|
||||||
if ([sender respondsToSelector:@selector(objectValue)])
|
if ([sender respondsToSelector:@selector(objectValue)])
|
||||||
@@ -607,7 +533,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
if ([note object] != self)
|
if ([note object] != self)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:@{ "CPFieldEditor": [note object] }];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)textDidChange:(CPNotification)note
|
- (void)textDidChange:(CPNotification)note
|
||||||
@@ -616,7 +542,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
if ([note object] != self)
|
if ([note object] != self)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:@{ "CPFieldEditor": [note object] }];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)textDidEndEditing:(CPNotification)note
|
- (void)textDidEndEditing:(CPNotification)note
|
||||||
@@ -627,203 +553,99 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
|
|
||||||
[self _reverseSetBinding];
|
[self _reverseSetBinding];
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:@{ "CPFieldEditor": [note object] }];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the text alignment of the control.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPLeftTextAlignment
|
|
||||||
CPCenterTextAlignment
|
|
||||||
CPRightTextAlignment
|
|
||||||
CPJustifiedTextAlignment
|
|
||||||
CPNaturalTextAlignment
|
|
||||||
</pre>
|
|
||||||
*/
|
|
||||||
- (void)setAlignment:(CPTextAlignment)alignment
|
- (void)setAlignment:(CPTextAlignment)alignment
|
||||||
{
|
{
|
||||||
[self setValue:alignment forThemeAttribute:@"alignment"];
|
[self setValue:alignment forThemeAttribute:@"alignment"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the text alignment of the control.
|
|
||||||
*/
|
|
||||||
- (CPTextAlignment)alignment
|
- (CPTextAlignment)alignment
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"alignment"];
|
return [self valueForThemeAttribute:@"alignment"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the vertical text alignment of the control.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPTopVerticalTextAlignment
|
|
||||||
CPCenterVerticalTextAlignment
|
|
||||||
CPBottomVerticalTextAlignment
|
|
||||||
</pre>
|
|
||||||
*/
|
|
||||||
- (void)setVerticalAlignment:(CPTextVerticalAlignment)alignment
|
- (void)setVerticalAlignment:(CPTextVerticalAlignment)alignment
|
||||||
{
|
{
|
||||||
[self setValue:alignment forThemeAttribute:@"vertical-alignment"];
|
[self setValue:alignment forThemeAttribute:@"vertical-alignment"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the vertical text alignment of the receiver.
|
|
||||||
*/
|
|
||||||
- (CPTextVerticalAlignment)verticalAlignment
|
- (CPTextVerticalAlignment)verticalAlignment
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"vertical-alignment"];
|
return [self valueForThemeAttribute:@"vertical-alignment"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the line break mode of the receiver.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPLineBreakByWordWrapping
|
|
||||||
CPLineBreakByCharWrapping
|
|
||||||
CPLineBreakByClipping
|
|
||||||
CPLineBreakByTruncatingHead
|
|
||||||
CPLineBreakByTruncatingTail
|
|
||||||
CPLineBreakByTruncatingMiddle
|
|
||||||
</pre>
|
|
||||||
*/
|
|
||||||
- (void)setLineBreakMode:(CPLineBreakMode)mode
|
- (void)setLineBreakMode:(CPLineBreakMode)mode
|
||||||
{
|
{
|
||||||
[self setValue:mode forThemeAttribute:@"line-break-mode"];
|
[self setValue:mode forThemeAttribute:@"line-break-mode"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the line break mode of the control.
|
|
||||||
*/
|
|
||||||
- (CPLineBreakMode)lineBreakMode
|
- (CPLineBreakMode)lineBreakMode
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"line-break-mode"];
|
return [self valueForThemeAttribute:@"line-break-mode"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the text color of the receiver.
|
|
||||||
|
|
||||||
@param aColor - A CPColor object.
|
|
||||||
*/
|
|
||||||
- (void)setTextColor:(CPColor)aColor
|
- (void)setTextColor:(CPColor)aColor
|
||||||
{
|
{
|
||||||
[self setValue:aColor forThemeAttribute:@"text-color"];
|
[self setValue:aColor forThemeAttribute:@"text-color"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the text color of the receiver.
|
|
||||||
*/
|
|
||||||
- (CPColor)textColor
|
- (CPColor)textColor
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"text-color"];
|
return [self valueForThemeAttribute:@"text-color"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the shadow color of the text for the receiver.
|
|
||||||
*/
|
|
||||||
- (void)setTextShadowColor:(CPColor)aColor
|
- (void)setTextShadowColor:(CPColor)aColor
|
||||||
{
|
{
|
||||||
[self setValue:aColor forThemeAttribute:@"text-shadow-color"];
|
[self setValue:aColor forThemeAttribute:@"text-shadow-color"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the shadow color of the text for the control.
|
|
||||||
*/
|
|
||||||
- (CPColor)textShadowColor
|
- (CPColor)textShadowColor
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"text-shadow-color"];
|
return [self valueForThemeAttribute:@"text-shadow-color"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (void)setTextShadowOffset:(float)offset
|
||||||
Sets the shadow offset for the text.
|
|
||||||
|
|
||||||
@param offset - a CGSize with the x and y offsets.
|
|
||||||
*/
|
|
||||||
- (void)setTextShadowOffset:(CGSize)offset
|
|
||||||
{
|
{
|
||||||
[self setValue:offset forThemeAttribute:@"text-shadow-offset"];
|
[self setValue:offset forThemeAttribute:@"text-shadow-offset"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (float)textShadowOffset
|
||||||
Returns the text shadow offset of the receiver.
|
|
||||||
*/
|
|
||||||
- (CGSize)textShadowOffset
|
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"text-shadow-offset"];
|
return [self valueForThemeAttribute:@"text-shadow-offset"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the font of the control.
|
|
||||||
*/
|
|
||||||
- (void)setFont:(CPFont)aFont
|
- (void)setFont:(CPFont)aFont
|
||||||
{
|
{
|
||||||
[self setValue:aFont forThemeAttribute:@"font"];
|
[self setValue:aFont forThemeAttribute:@"font"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the font of the control.
|
|
||||||
*/
|
|
||||||
- (CPFont)font
|
- (CPFont)font
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"font"];
|
return [self valueForThemeAttribute:@"font"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the image position of the control.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPNoImage
|
|
||||||
CPImageOnly
|
|
||||||
CPImageLeft
|
|
||||||
CPImageRight
|
|
||||||
CPImageBelow
|
|
||||||
CPImageAbove
|
|
||||||
CPImageOverlaps
|
|
||||||
</pre>
|
|
||||||
*/
|
|
||||||
- (void)setImagePosition:(CPCellImagePosition)position
|
- (void)setImagePosition:(CPCellImagePosition)position
|
||||||
{
|
{
|
||||||
[self setValue:position forThemeAttribute:@"image-position"];
|
[self setValue:position forThemeAttribute:@"image-position"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the image position of the receiver.
|
|
||||||
*/
|
|
||||||
- (CPCellImagePosition)imagePosition
|
- (CPCellImagePosition)imagePosition
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"image-position"];
|
return [self valueForThemeAttribute:@"image-position"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the image scaling of the control.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPImageScaleProportionallyDown
|
|
||||||
CPImageScaleAxesIndependently
|
|
||||||
CPImageScaleNone
|
|
||||||
CPImageScaleProportionallyUpOrDown
|
|
||||||
</pre>
|
|
||||||
*/
|
|
||||||
- (void)setImageScaling:(CPImageScaling)scaling
|
- (void)setImageScaling:(CPImageScaling)scaling
|
||||||
{
|
{
|
||||||
[self setValue:scaling forThemeAttribute:@"image-scaling"];
|
[self setValue:scaling forThemeAttribute:@"image-scaling"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the image scaling of the control.
|
|
||||||
*/
|
|
||||||
- (CPImageScaling)imageScaling
|
- (CPImageScaling)imageScaling
|
||||||
{
|
{
|
||||||
return [self valueForThemeAttribute:@"image-scaling"];
|
return [self valueForThemeAttribute:@"image-scaling"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the enabled status of the control.
|
|
||||||
Controls that are not enabled can not be used by the user and obtain the CPThemeStateDisabled theme state.
|
|
||||||
|
|
||||||
@param BOOL - YES if the control should be enabled, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setEnabled:(BOOL)isEnabled
|
- (void)setEnabled:(BOOL)isEnabled
|
||||||
{
|
{
|
||||||
if (isEnabled)
|
if (isEnabled)
|
||||||
@@ -832,29 +654,16 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
[self setThemeState:CPThemeStateDisabled];
|
[self setThemeState:CPThemeStateDisabled];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns YES if the receiver is enabled, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)isEnabled
|
- (BOOL)isEnabled
|
||||||
{
|
{
|
||||||
return ![self hasThemeState:CPThemeStateDisabled];
|
return ![self hasThemeState:CPThemeStateDisabled];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Highlights the receiver.
|
|
||||||
|
|
||||||
@param BOOL - YES if the receiver should be highlighted, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)highlight:(BOOL)shouldHighlight
|
- (void)highlight:(BOOL)shouldHighlight
|
||||||
{
|
{
|
||||||
[self setHighlighted:shouldHighlight];
|
[self setHighlighted:shouldHighlight];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Highlights the receiver.
|
|
||||||
|
|
||||||
@param BOOL - YES if the receiver should be highlighted, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setHighlighted:(BOOL)isHighlighted
|
- (void)setHighlighted:(BOOL)isHighlighted
|
||||||
{
|
{
|
||||||
if (isHighlighted)
|
if (isHighlighted)
|
||||||
@@ -863,9 +672,6 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
[self unsetThemeState:CPThemeStateHighlighted];
|
[self unsetThemeState:CPThemeStateHighlighted];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns YES if the control is highlighted, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)isHighlighted
|
- (BOOL)isHighlighted
|
||||||
{
|
{
|
||||||
return [self hasThemeState:CPThemeStateHighlighted];
|
return [self hasThemeState:CPThemeStateHighlighted];
|
||||||
@@ -873,22 +679,22 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPControlValueKey = @"CPControlValueKey",
|
var CPControlValueKey = "CPControlValueKey",
|
||||||
CPControlControlStateKey = @"CPControlControlStateKey",
|
CPControlControlStateKey = @"CPControlControlStateKey",
|
||||||
CPControlIsEnabledKey = @"CPControlIsEnabledKey",
|
CPControlIsEnabledKey = "CPControlIsEnabledKey",
|
||||||
CPControlTargetKey = @"CPControlTargetKey",
|
|
||||||
CPControlActionKey = @"CPControlActionKey",
|
|
||||||
CPControlSendActionOnKey = @"CPControlSendActionOnKey",
|
|
||||||
CPControlFormatterKey = @"CPControlFormatterKey",
|
|
||||||
CPControlSendsActionOnEndEditingKey = @"CPControlSendsActionOnEndEditingKey",
|
|
||||||
|
|
||||||
__Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
CPControlTargetKey = "CPControlTargetKey",
|
||||||
|
CPControlActionKey = "CPControlActionKey",
|
||||||
|
CPControlSendActionOnKey = "CPControlSendActionOnKey",
|
||||||
|
|
||||||
|
CPControlSendsActionOnEndEditingKey = "CPControlSendsActionOnEndEditingKey";
|
||||||
|
|
||||||
|
var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||||
|
|
||||||
@implementation CPControl (CPCoding)
|
@implementation CPControl (CPCoding)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initializes the control by unarchiving it from a coder.
|
Initializes the control by unarchiving it from a coder.
|
||||||
|
|
||||||
@param aCoder the coder from which to unarchive the control
|
@param aCoder the coder from which to unarchive the control
|
||||||
@return the initialized control
|
@return the initialized control
|
||||||
*/
|
*/
|
||||||
@@ -905,8 +711,6 @@ var CPControlValueKey = @"CPControlValueKey",
|
|||||||
|
|
||||||
[self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
|
[self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
|
||||||
[self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]];
|
[self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]];
|
||||||
|
|
||||||
[self setFormatter:[aCoder decodeObjectForKey:CPControlFormatterKey]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -914,7 +718,6 @@ var CPControlValueKey = @"CPControlValueKey",
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Archives the control to the provided coder.
|
Archives the control to the provided coder.
|
||||||
|
|
||||||
@param aCoder the coder to which the control will be archived.
|
@param aCoder the coder to which the control will be archived.
|
||||||
*/
|
*/
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
@@ -924,21 +727,16 @@ var CPControlValueKey = @"CPControlValueKey",
|
|||||||
if (_sendsActionOnEndEditing)
|
if (_sendsActionOnEndEditing)
|
||||||
[aCoder encodeBool:_sendsActionOnEndEditing forKey:CPControlSendsActionOnEndEditingKey];
|
[aCoder encodeBool:_sendsActionOnEndEditing forKey:CPControlSendsActionOnEndEditingKey];
|
||||||
|
|
||||||
var objectValue = [self objectValue];
|
if (_value !== nil)
|
||||||
|
[aCoder encodeObject:_value forKey:CPControlValueKey];
|
||||||
if (objectValue !== nil)
|
|
||||||
[aCoder encodeObject:objectValue forKey:CPControlValueKey];
|
|
||||||
|
|
||||||
if (_target !== nil)
|
if (_target !== nil)
|
||||||
[aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
|
[aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
|
||||||
|
|
||||||
if (_action !== nil)
|
if (_action !== NULL)
|
||||||
[aCoder encodeObject:_action forKey:CPControlActionKey];
|
[aCoder encodeObject:_action forKey:CPControlActionKey];
|
||||||
|
|
||||||
[aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey];
|
[aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey];
|
||||||
|
|
||||||
if (_formatter !== nil)
|
|
||||||
[aCoder encodeObject:_formatter forKey:CPControlFormatterKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ var CPControllerDeclaredKeysKey = @"CPControllerDeclaredKeysKey";
|
|||||||
_declaredKeys = [aDecoder decodeObjectForKey:CPControllerDeclaredKeysKey] || [];
|
_declaredKeys = [aDecoder decodeObjectForKey:CPControllerDeclaredKeysKey] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEditing
|
- (BOOL)isEditing
|
||||||
@@ -64,7 +64,7 @@ var CPControllerDeclaredKeysKey = @"CPControllerDeclaredKeysKey";
|
|||||||
count = _editors.length;
|
count = _editors.length;
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
if (![[_editors objectAtIndex:index] commitEditing])
|
if (![[_editors objectAtIndex:i] commitEditing])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
|
|||||||
+3
-7
@@ -94,7 +94,7 @@
|
|||||||
domain = "";
|
domain = "";
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
document.cookie = _cookieName + "=" + value + expires + "; path=/" + domain;
|
document.cookie = _cookieName+"="+value+expires+"; path=/"+domain;
|
||||||
#else
|
#else
|
||||||
_cookieValue = value;
|
_cookieValue = value;
|
||||||
_expires = expires;
|
_expires = expires;
|
||||||
@@ -111,12 +111,8 @@
|
|||||||
for (var i = 0; i < ca.length; i++)
|
for (var i = 0; i < ca.length; i++)
|
||||||
{
|
{
|
||||||
var c = ca[i];
|
var c = ca[i];
|
||||||
|
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||||||
while (c.charAt(0) == ' ')
|
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
||||||
c = c.substring(1, c.length);
|
|
||||||
|
|
||||||
if (c.indexOf(nameEQ) == 0)
|
|
||||||
return c.substring(nameEQ.length, c.length);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
+200
-254
@@ -1,56 +1,34 @@
|
|||||||
/*
|
/*
|
||||||
Cursor support by browser:
|
Testing browsers:
|
||||||
OS X 10.6/Chrome 8 : All
|
WebKit r50235 (Safari 4.0.3+)
|
||||||
OS X 10.6/Safari 5 : All
|
FireFox 3.5
|
||||||
OS X 10.6/Firefox 3 : All except disappearingItemCursor (no url() support)
|
Opera 10.0.0
|
||||||
OS X 10.6/Firefox 3.5 : All except disappearingItemCursor (no url() support)
|
|
||||||
OS X 10.6/Firefox 3.6 : All except disappearingItemCursor, contextualMenuCursor, dragLinkCursor, dragCopyCursor, operationNotAllowedCursor (no url() support)
|
Implemented class methods:
|
||||||
OS X 10.6/Firefox 4.0b10 : All
|
Webkit : All
|
||||||
OS X/Opera 9 : All except disappearingItemCursor, closedHandCursor, openHandCursor, contextualMenuCursor, dragLinkCursor, dragCopyCursor, operationNotAllowedCursor, resizeUpDownCursor, resizeLeftRightCursor (no url() support)
|
IE : All
|
||||||
OS X/Opera 10 : All except disappearingItemCursor, closedHandCursor, contextualMenuCursor, dragLinkCursor, dragCopyCursor, operationNotAllowedCursor (no url() support)
|
FireFox Win : All
|
||||||
OS X/Opera 11 : All except disappearingItemCursor, closedHandCursor, contextualMenuCursor, dragLinkCursor, dragCopyCursor, operationNotAllowedCursor (no url() support)
|
FireFox Mac : All except closedHandCursor disappearingItemCursor // Firefox mac does not support url cursors
|
||||||
Win XP/Chrome 8 : All
|
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
|
||||||
Win XP/Safari 5 : All
|
|
||||||
Win XP/Firefox 3 : All
|
|
||||||
Win XP/Firefox 3.5 : All
|
|
||||||
Win XP/Firefox 3.6 : All
|
|
||||||
Win XP/Firefox 4.0b10 : All
|
|
||||||
Win XP/Opera 10 : All except disappearingItemCursor, closedHandCursor, openHandCursor, contextualMenuCursor, dragLinkCursor, dragCopyCursor, operationNotAllowedCursor, resizeUpDownCursor, resizeLeftRightCursor (no url() support)
|
|
||||||
Win XP/Opera 11 : All except disappearingItemCursor, closedHandCursor, openHandCursor, contextualMenuCursor, dragLinkCursor, dragCopyCursor, operationNotAllowedCursor, resizeUpDownCursor, resizeLeftRightCursor (no url() support)
|
|
||||||
Win XP/IE 7 : All
|
|
||||||
Win XP/IE 8 : All
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
var currentCursor = nil,
|
var currentCursor = nil,
|
||||||
cursorStack = [],
|
cursorStack = [],
|
||||||
cursors = {},
|
cursors = {},
|
||||||
ieCursorMap = {};
|
cursorURLFormat = nil;
|
||||||
|
|
||||||
@implementation CPCursor : CPObject
|
@implementation CPCursor : CPObject
|
||||||
{
|
{
|
||||||
CPString _cssString @accessors(readonly);
|
CPString _cssString;
|
||||||
CPString _hotSpot @accessors(readonly, getter=hotSpot);
|
|
||||||
CPImage _image @accessors(readonly, getter=image);
|
|
||||||
BOOL _isSetOnMouseEntered @accessors(readwrite, getter=isSetOnMouseEntered, setter=setOnMouseEntered:);
|
BOOL _isSetOnMouseEntered @accessors(readwrite, getter=isSetOnMouseEntered, setter=setOnMouseEntered:);
|
||||||
BOOL _isSetOnMouseExited @accessors(readwrite, getter=isSetOnMouseExited, setter=setOnMouseExited:);
|
BOOL _isSetOnMouseExited @accessors(readwrite, getter=isSetOnMouseExited, setter=setOnMouseExited:);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)initialize
|
+ (CPCursor)currentCursor
|
||||||
{
|
{
|
||||||
if (self !== CPCursor)
|
return currentCursor;
|
||||||
return;
|
|
||||||
|
|
||||||
// IE < 9 does not support some CSS cursors, we map them to supported ones
|
|
||||||
ieCursorMap = {
|
|
||||||
"ew-resize": "e-resize",
|
|
||||||
"ns-resize": "n-resize",
|
|
||||||
"nesw-resize": "ne-resize",
|
|
||||||
"nwse-resize": "nw-resize"
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithCSSString:(CPString)aString
|
- (id)initWithCSSString:(CPString)aString
|
||||||
@@ -61,30 +39,160 @@ var currentCursor = nil,
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
+ (CPCursor)cursorWithCSSString:(CPString)cssString
|
||||||
Init a cursor with the given image and hotspot.
|
|
||||||
hotspot is supported in CSS3 (but not IE).
|
|
||||||
*/
|
|
||||||
- (id)initWithImage:(CPImage)image hotSpot:(CGPoint)hotSpot
|
|
||||||
{
|
{
|
||||||
_hotSpot = hotSpot;
|
var cursor = cursors[cssString];
|
||||||
_image = image;
|
|
||||||
return [self initWithCSSString:"url(" + [_image filename] + ")" + hotSpot.x + " " + hotSpot.y + ", auto"];
|
if (typeof cursor == 'undefined')
|
||||||
|
{
|
||||||
|
cursor = [[CPCursor alloc] initWithCSSString:cssString];
|
||||||
|
cursors[cssString] = cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
+ (CPCursor)cursorWithImageNamed:(CPString)imageName
|
||||||
Init a cursor with the given image and hotspot. This is provided
|
|
||||||
for compliance with Cocoa. Note that foregroundColor and backgroundColor are ignored
|
|
||||||
(as they are in Cocoa). See http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSCursor_Class/Reference/Reference.html
|
|
||||||
*/
|
|
||||||
- (id)initWithImage:(CPImage)image foregroundColorHint:(CPColor)foregroundColor backgroundColorHint:(CPColor)backgroundColor hotSpot:(CGPoint)aHotSpot
|
|
||||||
{
|
{
|
||||||
return [self initWithImage:image hotSpot:aHotSpot];
|
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
|
+ (void)hide
|
||||||
{
|
{
|
||||||
[self _setCursorCSS:@"none"]; // Not supported in IE < 9
|
[self _setCursorCSS:"none"]; // Not supported in IE
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)unhide
|
+ (void)unhide
|
||||||
@@ -100,6 +208,44 @@ var currentCursor = nil,
|
|||||||
[CPCursor unhide];
|
[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
|
- (void)pop
|
||||||
{
|
{
|
||||||
[CPCursor pop];
|
[CPCursor pop];
|
||||||
@@ -114,207 +260,6 @@ var currentCursor = nil,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)push
|
|
||||||
{
|
|
||||||
currentCursor = cursorStack.push(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)set
|
|
||||||
{
|
|
||||||
currentCursor = self;
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
[[self class] _setCursorCSS:_cssString];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseEntered:(CPEvent)event
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseExited:(CPEvent)event
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)currentCursor
|
|
||||||
{
|
|
||||||
return currentCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal method that is used to return the system cursors. Caches the system cursors for performance.
|
|
||||||
+ (CPCursor)_systemCursorWithName:(CPString)cursorName cssString:(CPString)aString hasImage:(BOOL)doesHaveImage
|
|
||||||
{
|
|
||||||
var cursor = cursors[cursorName];
|
|
||||||
|
|
||||||
if (typeof cursor === "undefined")
|
|
||||||
{
|
|
||||||
var cssString;
|
|
||||||
|
|
||||||
if (doesHaveImage)
|
|
||||||
{
|
|
||||||
var themeResourcePath = [[[CPApp themeBlend] bundle] resourcePath],
|
|
||||||
extension = CPBrowserIsEngine(CPInternetExplorerBrowserEngine) ? @"cur" : @"png";
|
|
||||||
|
|
||||||
cssString = [CPString stringWithFormat:@"url(%@cursors/%@.%@), %@", themeResourcePath, cursorName, extension, aString];
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// IE <= 8 does not support some cursors, map them to supported cursors
|
|
||||||
var ieLessThan9 = CPBrowserIsEngine(CPInternetExplorerBrowserEngine) && !CPFeatureIsCompatible(CPHTMLCanvasFeature);
|
|
||||||
|
|
||||||
if (ieLessThan9)
|
|
||||||
cssString = ieCursorMap[aString] || aString;
|
|
||||||
else
|
|
||||||
cssString = aString;
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor = [[CPCursor alloc] initWithCSSString:cssString];
|
|
||||||
cursors[cursorName] = cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)arrowCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"default" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)crosshairCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"crosshair" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)IBeamCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"text" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)pointingHandCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"pointer" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeNorthwestCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nw-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeNorthwestSoutheastCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nwse-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeNortheastCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ne-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeNortheastSouthwestCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nesw-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeSouthwestCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"sw-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeSoutheastCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"se-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeDownCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"s-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeUpCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"n-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeLeftCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"w-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeRightCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"e-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeLeftRightCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"col-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeEastWestCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ew-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeUpDownCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"row-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)resizeNorthSouthCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ns-resize" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)operationNotAllowedCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"not-allowed" hasImage:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)dragCopyCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"copy" hasImage:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)dragLinkCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"alias" hasImage:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)contextualMenuCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"context-menu" hasImage:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)openHandCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"move" hasImage:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)closedHandCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"-moz-grabbing" hasImage:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPCursor)disappearingItemCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"auto" hasImage:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPCursor(CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)coder
|
- (id)initWithCoder:(CPCoder)coder
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
@@ -329,3 +274,4 @@ var currentCursor = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -1,691 +0,0 @@
|
|||||||
/* CPDatePicker.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Alexandre Wilhelm
|
|
||||||
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "CPControl.j"
|
|
||||||
@import "CPFont.j"
|
|
||||||
@import "CPTextField.j"
|
|
||||||
@import "_CPDatePickerTextField.j"
|
|
||||||
@import "_CPDatePickerCalendar.j"
|
|
||||||
|
|
||||||
@import <Foundation/CPArray.j>
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
@import <Foundation/CPDate.j>
|
|
||||||
@import <Foundation/CPDateFormatter.j>
|
|
||||||
@import <Foundation/CPLocale.j>
|
|
||||||
@import <Foundation/CPTimeZone.j>
|
|
||||||
|
|
||||||
@class CPStepper
|
|
||||||
@class CPApp
|
|
||||||
|
|
||||||
@global CPLocaleLanguageCode
|
|
||||||
@global CPDateFormatterShortStyle
|
|
||||||
|
|
||||||
var CPDatePicker_validateProposedDateValue_timeInterval = 1 << 1;
|
|
||||||
|
|
||||||
CPSingleDateMode = 0;
|
|
||||||
CPRangeDateMode = 1;
|
|
||||||
|
|
||||||
CPTextFieldAndStepperDatePickerStyle = 0;
|
|
||||||
CPClockAndCalendarDatePickerStyle = 1;
|
|
||||||
CPTextFieldDatePickerStyle = 2;
|
|
||||||
|
|
||||||
CPHourMinuteDatePickerElementFlag = 0x000c;
|
|
||||||
CPHourMinuteSecondDatePickerElementFlag = 0x000e;
|
|
||||||
CPTimeZoneDatePickerElementFlag = 0x0010;
|
|
||||||
CPYearMonthDatePickerElementFlag = 0x00c0;
|
|
||||||
CPYearMonthDayDatePickerElementFlag = 0x00e0;
|
|
||||||
CPEraDatePickerElementFlag = 0x0100;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
This control displays a datepicker in a Cappuccino application
|
|
||||||
*/
|
|
||||||
@implementation CPDatePicker : CPControl
|
|
||||||
{
|
|
||||||
BOOL _isBordered @accessors(getter=isBordered, setter=setBordered:);
|
|
||||||
BOOL _isBezeled @accessors(getter=isBezeled, setter=setBezeled:);
|
|
||||||
BOOL _drawsBackground @accessors(property=drawsBackground);
|
|
||||||
CPDate _dateValue @accessors(property=dateValue);
|
|
||||||
CPDate _minDate @accessors(property=minDate);
|
|
||||||
CPDate _maxDate @accessors(property=maxDate);
|
|
||||||
CPFont _textFont @accessors(property=textFont);
|
|
||||||
CPLocale _locale @accessors(property=locale);
|
|
||||||
//CPCalendar _calendar @accessors(property=calendar);
|
|
||||||
CPTimeZone _timeZone @accessors(property=timeZone);
|
|
||||||
id _delegate @accessors(property=delegate);
|
|
||||||
unsigned _datePickerElements @accessors(property=datePickerElements);
|
|
||||||
CPInteger _datePickerMode @accessors(property=datePickerMode);
|
|
||||||
CPInteger _datePickerStyle @accessors(property=datePickerStyle);
|
|
||||||
CPInteger _timeInterval @accessors(property=timeInterval);
|
|
||||||
|
|
||||||
_CPDatePickerTextField _datePickerTextfield;
|
|
||||||
_CPDatePickerCalendar _datePickerCalendar;
|
|
||||||
unsigned _implementedCDatePickerDelegateMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Theme methods
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return @"datePicker";
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return @{
|
|
||||||
@"bezel-color": [CPColor clearColor],
|
|
||||||
@"border-width" : 1.0,
|
|
||||||
@"border-color": [CPColor clearColor],
|
|
||||||
@"content-inset": CGInsetMakeZero(),
|
|
||||||
@"bezel-inset": CGInsetMakeZero(),
|
|
||||||
@"datepicker-textfield-bezel-color": [CPColor clearColor],
|
|
||||||
@"min-size-datepicker-textfield": CGSizeMakeZero(),
|
|
||||||
@"content-inset-datepicker-textfield": CGInsetMakeZero(),
|
|
||||||
@"content-inset-datepicker-textfield-separator": CGInsetMakeZero(),
|
|
||||||
@"separator-content-inset": CGInsetMakeZero(),
|
|
||||||
@"date-hour-margin": 5.0,
|
|
||||||
@"stepper-margin": 5.0,
|
|
||||||
@"bezel-color-calendar": [CPColor clearColor],
|
|
||||||
@"title-text-color": [CPColor blackColor],
|
|
||||||
@"title-text-shadow-color": [CPColor clearColor],
|
|
||||||
@"title-text-shadow-offset": CGSizeMakeZero(),
|
|
||||||
@"title-font": [CPNull null],
|
|
||||||
@"weekday-text-color": [CPColor blackColor],
|
|
||||||
@"weekday-text-shadow-color": [CPColor clearColor],
|
|
||||||
@"weekday-text-shadow-offset": CGSizeMakeZero(),
|
|
||||||
@"weekday-font": [CPNull null],
|
|
||||||
@"arrow-image-left": [CPNull null],
|
|
||||||
@"arrow-image-right": [CPNull null],
|
|
||||||
@"arrow-image-left-highlighted": [CPNull null],
|
|
||||||
@"arrow-image-right-highlighted": [CPNull null],
|
|
||||||
@"arrow-inset": CGInsetMakeZero(),
|
|
||||||
@"circle-image": [CPNull null],
|
|
||||||
@"circle-image-highlighted": [CPNull null],
|
|
||||||
@"tile-text-color": [CPColor blackColor],
|
|
||||||
@"tile-text-shadow-color": [CPColor clearColor],
|
|
||||||
@"tile-text-shadow-offset": CGSizeMakeZero(),
|
|
||||||
@"tile-font": [CPNull null],
|
|
||||||
@"size-tile": CGSizeMakeZero(),
|
|
||||||
@"size-calendar": CGSizeMakeZero(),
|
|
||||||
@"size-header": CGSizeMakeZero(),
|
|
||||||
@"min-size-calendar": CGSizeMakeZero(),
|
|
||||||
@"max-size-calendar": CGSizeMakeZero(),
|
|
||||||
@"bezel-color-clock": [CPColor clearColor],
|
|
||||||
@"clock-text-color": [CPColor blackColor],
|
|
||||||
@"clock-text-shadow-color": [CPColor clearColor],
|
|
||||||
@"clock-text-shadow-offset": CGSizeMakeZero(),
|
|
||||||
@"clock-font": [CPNull null],
|
|
||||||
@"second-hand-color": [CPColor clearColor],
|
|
||||||
@"hour-hand-color": [CPColor clearColor],
|
|
||||||
@"middle-hand-color": [CPColor clearColor],
|
|
||||||
@"minute-hand-color": [CPColor clearColor],
|
|
||||||
@"size-clock": CGSizeMakeZero(),
|
|
||||||
@"second-hand-size": CGSizeMakeZero(),
|
|
||||||
@"hour-hand-size": CGSizeMakeZero(),
|
|
||||||
@"middle-hand-size": CGSizeMakeZero(),
|
|
||||||
@"minute-hand-size": CGSizeMakeZero(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Binding methods
|
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
|
||||||
{
|
|
||||||
if (theBinding == CPValueBinding || theBinding == CPMinValueBinding || theBinding == CPMaxValueBinding)
|
|
||||||
return [_CPDatePickerValueBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:theBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_replacementKeyPathForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding == CPValueBinding)
|
|
||||||
return @"dateValue";
|
|
||||||
|
|
||||||
if (aBinding == CPMinValueBinding)
|
|
||||||
return @"minDate";
|
|
||||||
|
|
||||||
if (aBinding == CPMaxValueBinding)
|
|
||||||
return @"maxDate";
|
|
||||||
|
|
||||||
return [super _replacementKeyPathForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Init methods
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
{
|
|
||||||
_drawsBackground = YES;
|
|
||||||
_datePickerStyle = CPTextFieldAndStepperDatePickerStyle;
|
|
||||||
_datePickerMode = CPSingleDateMode;
|
|
||||||
_datePickerElements = CPYearMonthDayDatePickerElementFlag | CPHourMinuteSecondDatePickerElementFlag;
|
|
||||||
_timeInterval = 0;
|
|
||||||
_implementedCDatePickerDelegateMethods = 0;
|
|
||||||
|
|
||||||
[self setObjectValue:[CPDate date]];
|
|
||||||
_minDate = [CPDate distantPast];
|
|
||||||
_maxDate = [CPDate distantFuture];
|
|
||||||
|
|
||||||
[self setBezeled:YES];
|
|
||||||
[self setBordered:YES];
|
|
||||||
|
|
||||||
[self _init];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
if (!_locale)
|
|
||||||
_locale = [CPLocale currentLocale];
|
|
||||||
|
|
||||||
if (!_timeZone)
|
|
||||||
_timeZone = [CPTimeZone systemTimeZone];
|
|
||||||
|
|
||||||
_datePickerTextfield = [[_CPDatePickerTextField alloc] initWithFrame:[self bounds] withDatePicker:self];
|
|
||||||
|
|
||||||
[_datePickerTextfield setDateValue:_dateValue];
|
|
||||||
[self addSubview:_datePickerTextfield];
|
|
||||||
|
|
||||||
_datePickerCalendar = [[_CPDatePickerCalendar alloc] initWithFrame:[self bounds] withDatePicker:self];
|
|
||||||
[_datePickerCalendar setDateValue:_dateValue];
|
|
||||||
[_datePickerCalendar setHidden:YES];
|
|
||||||
[self addSubview:_datePickerCalendar];
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Delegate methods
|
|
||||||
|
|
||||||
/*! Set the delegate of the datePicker
|
|
||||||
@param aDelegate delegate of the datePicker
|
|
||||||
*/
|
|
||||||
- (void)setDelegate:(id)aDelegate
|
|
||||||
{
|
|
||||||
_delegate = aDelegate;
|
|
||||||
_implementedCDatePickerDelegateMethods = 0;
|
|
||||||
|
|
||||||
// Look if the delegate implements or not the delegate methods
|
|
||||||
if ([_delegate respondsToSelector:@selector(datePicker:validateProposedDateValue:timeInterval:)])
|
|
||||||
_implementedCDatePickerDelegateMethods |= CPDatePicker_validateProposedDateValue_timeInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Layout method
|
|
||||||
|
|
||||||
/*! Layout the subviews
|
|
||||||
*/
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
[super layoutSubviews];
|
|
||||||
|
|
||||||
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
|
|
||||||
{
|
|
||||||
[_datePickerTextfield setHidden:NO];
|
|
||||||
[_datePickerCalendar setHidden:YES];
|
|
||||||
[_datePickerTextfield setNeedsLayout];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[_datePickerCalendar setHidden:NO];
|
|
||||||
[_datePickerTextfield setHidden:YES];
|
|
||||||
[_datePickerCalendar setNeedsLayout];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Setter
|
|
||||||
|
|
||||||
/*! Return the objectValue of the datePicker. The objectValue should take the timeZoneEffect
|
|
||||||
*/
|
|
||||||
- (void)objectValue
|
|
||||||
{
|
|
||||||
// TODO : add timeZone effect. How to do it because js ???
|
|
||||||
return _dateValue
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the objectValue ofhe datePier. It has to be a CPDate
|
|
||||||
@param aDateValue the dateValue
|
|
||||||
*/
|
|
||||||
- (void)setObjectValue:(CPDate)aValue
|
|
||||||
{
|
|
||||||
[self setDateValue:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the dateValue of the datePicker
|
|
||||||
@param aDateValue the dateValue
|
|
||||||
*/
|
|
||||||
- (void)setDateValue:(CPDate)aDateValue
|
|
||||||
{
|
|
||||||
if (aDateValue == nil)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self _setDateValue:aDateValue timeInterval:_timeInterval];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the dateValue and the timeInterval. This method checks the min and max date of the datePicker also. It will call the delegate if possible.
|
|
||||||
@param aDateValue the dateValue
|
|
||||||
@param aTimeInterval the timeInterval
|
|
||||||
*/
|
|
||||||
- (void)_setDateValue:(CPDate)aDateValue timeInterval:(CPTimeInterval)aTimeInterval
|
|
||||||
{
|
|
||||||
if (_minDate)
|
|
||||||
aDateValue = new Date (MAX(aDateValue, _minDate));
|
|
||||||
|
|
||||||
if (_maxDate)
|
|
||||||
aDateValue = new Date (MIN(aDateValue, _maxDate));
|
|
||||||
|
|
||||||
aTimeInterval = MAX(MIN(aTimeInterval, [_maxDate timeIntervalSinceDate:aDateValue]), [_minDate timeIntervalSinceDate:aDateValue]);
|
|
||||||
|
|
||||||
if ([aDateValue isEqualToDate:_dateValue] && aTimeInterval == _timeInterval)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_implementedCDatePickerDelegateMethods & CPDatePicker_validateProposedDateValue_timeInterval)
|
|
||||||
{
|
|
||||||
// constrain timeInterval also
|
|
||||||
var aStartDateRef = function(x){if (typeof x == 'undefined') return aDateValue; aDateValue = x;}
|
|
||||||
var aTimeIntervalRef = function(x){if (typeof x == 'undefined') return aTimeInterval; aTimeInterval = x;}
|
|
||||||
|
|
||||||
[_delegate datePicker:self validateProposedDateValue:aStartDateRef timeInterval:aTimeIntervalRef];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self willChangeValueForKey:@"objectValue"];
|
|
||||||
[self willChangeValueForKey:@"dateValue"];
|
|
||||||
_dateValue = aDateValue;
|
|
||||||
[super setObjectValue:_dateValue];
|
|
||||||
[self didChangeValueForKey:@"objectValue"];
|
|
||||||
[self didChangeValueForKey:@"dateValue"];
|
|
||||||
|
|
||||||
[self willChangeValueForKey:@"timeInterval"];
|
|
||||||
_timeInterval = (_datePickerMode == CPSingleDateMode)? 0 : aTimeInterval;
|
|
||||||
[self didChangeValueForKey:@"timeInterval"];
|
|
||||||
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
|
|
||||||
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
|
|
||||||
[_datePickerTextfield setDateValue:_dateValue];
|
|
||||||
else
|
|
||||||
[_datePickerCalendar setDateValue:_dateValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the minDate of the datePicker
|
|
||||||
@param aMinDate the minDate
|
|
||||||
*/
|
|
||||||
- (void)setMinDate:(CPDate)aMinDate
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"minDate"];
|
|
||||||
_minDate = aMinDate;
|
|
||||||
[self didChangeValueForKey:@"minDate"];
|
|
||||||
|
|
||||||
[self _setDateValue:_dateValue timeInterval:_timeInterval];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the maxDate of the datePicker
|
|
||||||
@param aMaxDate the maxDate
|
|
||||||
*/
|
|
||||||
- (void)setMaxDate:(CPDate)aMaxDate
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"maxDate"];
|
|
||||||
_maxDate = aMaxDate;
|
|
||||||
[self didChangeValueForKey:@"maxDate"];
|
|
||||||
|
|
||||||
[self _setDateValue:_dateValue timeInterval:_timeInterval];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the syle of the datePicker
|
|
||||||
@param aDatePickerStyle the datePicker style
|
|
||||||
*/
|
|
||||||
- (void)setDatePickerStyle:(CPDate)aDatePickerStyle
|
|
||||||
{
|
|
||||||
_datePickerStyle = aDatePickerStyle;
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the elements of the datePicker
|
|
||||||
@param aDatePickerElements the datePicker elements
|
|
||||||
*/
|
|
||||||
- (void)setDatePickerElements:(CPDate)aDatePickerElements
|
|
||||||
{
|
|
||||||
_datePickerElements = aDatePickerElements;
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the mode of the datePicker
|
|
||||||
@param aDatePickerMode the datePicker mode
|
|
||||||
*/
|
|
||||||
- (void)setDatePickerMode:(CPDate)aDatePickerMode
|
|
||||||
{
|
|
||||||
_datePickerMode = aDatePickerMode;
|
|
||||||
|
|
||||||
if (_datePickerMode == CPSingleDateMode)
|
|
||||||
[self _setDateValue:[self dateValue] timeInterval:0];
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the timeInterval of the datePicker
|
|
||||||
@param aTimeInterval the timeInterval of the datePicker
|
|
||||||
*/
|
|
||||||
- (void)setTimeInterval:(CPInteger)aTimeInterval
|
|
||||||
{
|
|
||||||
if (_datePickerMode == CPSingleDateMode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self _setDateValue:[self dateValue] timeInterval:aTimeInterval];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the locale of the datePicker. This update laso the locale of the formatter.
|
|
||||||
@param aLocale the locale
|
|
||||||
*/
|
|
||||||
- (void)setLocale:(CPLocale)aLocale
|
|
||||||
{
|
|
||||||
_locale = aLocale;
|
|
||||||
|
|
||||||
if (_formatter)
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"locale"];
|
|
||||||
[_formatter setLocale:_locale];
|
|
||||||
[self didChangeValueForKey:@"locale"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will update the textFields (usefull when changing with a date with pm and am)
|
|
||||||
[_datePickerTextfield setDateValue:_dateValue];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets whether the datepicker will have a bezeled border.
|
|
||||||
@param shouldBeBezeled \c YES means the datepicker will draw a bezeled border
|
|
||||||
*/
|
|
||||||
- (void)setBezeled:(BOOL)shouldBeBezeled
|
|
||||||
{
|
|
||||||
_isBezeled = shouldBeBezeled;
|
|
||||||
|
|
||||||
if (shouldBeBezeled)
|
|
||||||
[self setThemeState:CPThemeStateBezeled];
|
|
||||||
else
|
|
||||||
[self unsetThemeState:CPThemeStateBezeled];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets whether the datepicker will have a border drawn. (actually it does nothing)
|
|
||||||
@param shouldBeBordered \c YES makes the datepicker draw a border
|
|
||||||
*/
|
|
||||||
- (void)setBordered:(BOOL)shouldBeBordered
|
|
||||||
{
|
|
||||||
_isBordered = shouldBeBordered;
|
|
||||||
|
|
||||||
if (shouldBeBordered)
|
|
||||||
[self setThemeState:CPThemeStateBordered];
|
|
||||||
else
|
|
||||||
[self unsetThemeState:CPThemeStateBordered];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the font of the control.
|
|
||||||
@param aFont
|
|
||||||
*/
|
|
||||||
- (void)setTextFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
[self setFont:aFont];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Sets the enabled status of the control. Controls that are not enabled can not be used by the user and obtain the CPThemeStateDisabled theme state.
|
|
||||||
@param a boolean. YES if the control should be enabled, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setEnabled:(BOOL)aBoolean
|
|
||||||
{
|
|
||||||
[super setEnabled:aBoolean];
|
|
||||||
|
|
||||||
[_datePickerTextfield setEnabled:aBoolean];
|
|
||||||
[_datePickerCalendar setEnabled:aBoolean];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the background color of the datePicker
|
|
||||||
@param aColor
|
|
||||||
*/
|
|
||||||
- (void)setBackgroundColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
_backgroundColor = aColor;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the boolean drawsBackgroundColor
|
|
||||||
@param aBoolean
|
|
||||||
*/
|
|
||||||
- (void)setDrawsBackground:(BOOL)aBoolean
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"drawsBackground"];
|
|
||||||
_drawsBackground = aBoolean;
|
|
||||||
[self didChangeValueForKey:@"drawsBackground"];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the timeZone
|
|
||||||
@param aTimeZone
|
|
||||||
*/
|
|
||||||
- (void)setTimeZone:(CPTimeZone)aTimeZone
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"timeZone"];
|
|
||||||
_timeZone = aTimeZone;
|
|
||||||
[self didChangeValueForKey:@"timeZone"];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark First responder methods
|
|
||||||
|
|
||||||
/*! Return YES if style is set to CPTextFieldAndStepperDatePickerStyle or CPTextFieldDatePickerStyle
|
|
||||||
*/
|
|
||||||
- (BOOL)becomeFirstResponder
|
|
||||||
{
|
|
||||||
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
|
|
||||||
[_datePickerTextfield _selecteTextFieldWithFlags:[[CPApp currentEvent] modifierFlags]];
|
|
||||||
else
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Return YES
|
|
||||||
*/
|
|
||||||
- (BOOL)acceptsFirstResponder
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Return YES
|
|
||||||
*/
|
|
||||||
- (BOOL)resignFirstResponder
|
|
||||||
{
|
|
||||||
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
|
|
||||||
[_datePickerTextfield resignFirstResponder];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark getter
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c YES if the textfield is bezeled.
|
|
||||||
*/
|
|
||||||
- (BOOL)isBezeled
|
|
||||||
{
|
|
||||||
return [self hasThemeState:CPThemeStateBezeled];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c YES if the textfield has a border.
|
|
||||||
*/
|
|
||||||
- (BOOL)isBordered
|
|
||||||
{
|
|
||||||
return [self hasThemeState:CPThemeStateBordered];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the font of the control.
|
|
||||||
*/
|
|
||||||
- (CPFont)textFont
|
|
||||||
{
|
|
||||||
return [self font];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Check if we are in the american format or not. Depending on the locale
|
|
||||||
*/
|
|
||||||
- (BOOL)_isAmericanFormat
|
|
||||||
{
|
|
||||||
return [[_locale objectForKey:CPLocaleCountryCode] isEqualToString:@"US"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Check if we are in the english format or not. Depending on the locale
|
|
||||||
*/
|
|
||||||
- (BOOL)_isEnglishFormat
|
|
||||||
{
|
|
||||||
return [[_locale objectForKey:CPLocaleLanguageCode] isEqualToString:@"en"];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Key event
|
|
||||||
|
|
||||||
/*! Key down event
|
|
||||||
@param anEvent
|
|
||||||
*/
|
|
||||||
- (void)keyDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
|
|
||||||
[_datePickerTextfield keyDown:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPDatePickerModeKey = @"CPDatePickerModeKey",
|
|
||||||
CPIntervalKey = @"CPIntervalKey",
|
|
||||||
CPMinDateKey = @"CPMinDateKey",
|
|
||||||
CPMaxDateKey = @"CPMaxDateKey",
|
|
||||||
CPBackgroundColorKey = @"CPBackgroundColorKey",
|
|
||||||
CPDrawsBackgroundKey = @"CPDrawsBackgroundKey",
|
|
||||||
CPTextFontKey = @"CPTextFontKey",
|
|
||||||
CPDatePickerElementsKey = @"CPDatePickerElementsKey",
|
|
||||||
CPDatePickerStyleKey = @"CPDatePickerStyleKey",
|
|
||||||
CPLocaleKey = @"CPLocaleKey",
|
|
||||||
CPBorderedKey = @"CPBorderedKey",
|
|
||||||
CPDateValueKey = @"CPDateValueKey";
|
|
||||||
|
|
||||||
@implementation CPDatePicker (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_textFont = [aCoder decodeObjectForKey:CPTextFontKey];
|
|
||||||
_minDate = [aCoder decodeObjectForKey:CPMinDateKey] || [CPDate distantPast];
|
|
||||||
_maxDate = [aCoder decodeObjectForKey:CPMaxDateKey] || [CPDate distantFuture];
|
|
||||||
_timeInterval = [aCoder decodeDoubleForKey:CPIntervalKey];
|
|
||||||
_datePickerMode = [aCoder decodeIntForKey:CPDatePickerModeKey];
|
|
||||||
_datePickerElements = [aCoder decodeIntForKey:CPDatePickerElementsKey];
|
|
||||||
_datePickerStyle = [aCoder decodeIntForKey:CPDatePickerStyleKey];
|
|
||||||
_locale = [aCoder decodeObjectForKey:CPLocaleKey];
|
|
||||||
_dateValue = [aCoder decodeObjectForKey:CPDateValueKey];
|
|
||||||
_backgroundColor = [aCoder decodeObjectForKey:CPBackgroundColorKey];
|
|
||||||
_drawsBackground = [aCoder decodeBoolForKey:CPDrawsBackgroundKey];
|
|
||||||
_isBordered = [aCoder decodeBoolForKey:CPBorderedKey];
|
|
||||||
[self _init];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
[aCoder encodeDouble:_timeInterval forKey:CPIntervalKey];
|
|
||||||
[aCoder encodeInt:_datePickerMode forKey:CPDatePickerModeKey];
|
|
||||||
[aCoder encodeInt:_datePickerStyle forKey:CPDatePickerStyleKey];
|
|
||||||
[aCoder encodeInt:_datePickerElements forKey:CPDatePickerElementsKey];
|
|
||||||
[aCoder encodeObject:_minDate forKey:CPMinDateKey];
|
|
||||||
[aCoder encodeObject:_maxDate forKey:CPMaxDateKey]
|
|
||||||
[aCoder encodeObject:_dateValue forKey:CPDateValueKey];;
|
|
||||||
[aCoder encodeObject:_textFont forKey:CPTextFontKey];
|
|
||||||
[aCoder encodeObject:_locale forKey:CPLocaleKey];
|
|
||||||
[aCoder encodeObject:_backgroundColor forKey:CPBackgroundColorKey];
|
|
||||||
[aCoder encodeObject:_drawsBackground forKey:CPDrawsBackgroundKey];
|
|
||||||
[aCoder encodeObject:_isBordered forKey:CPBorderedKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPDatePickerValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPDate (CPDatePickerAdditions)
|
|
||||||
|
|
||||||
- (int)_daysInMonth
|
|
||||||
{
|
|
||||||
return 32 - new Date(self.getFullYear(), self.getMonth(), 32).getDate();
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_resetToMidnight
|
|
||||||
{
|
|
||||||
self.setHours(0);
|
|
||||||
self.setMinutes(0);
|
|
||||||
self.setSeconds(0);
|
|
||||||
self.setMilliseconds(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_resetToLastSeconds
|
|
||||||
{
|
|
||||||
self.setHours(23);
|
|
||||||
self.setMinutes(59);
|
|
||||||
self.setSeconds(59);
|
|
||||||
self.setMilliseconds(99);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,287 +0,0 @@
|
|||||||
/* _CPDatePickerClock.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Alexandre Wilhelm
|
|
||||||
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "CPView.j"
|
|
||||||
@import "CPTextField.j"
|
|
||||||
@import "CPImageView.j"
|
|
||||||
@import "CALayer.j"
|
|
||||||
|
|
||||||
@class CPDatePicker
|
|
||||||
|
|
||||||
@global CPHourMinuteSecondDatePickerElementFlag
|
|
||||||
|
|
||||||
@implementation _CPDatePickerClock : CPView
|
|
||||||
{
|
|
||||||
BOOL _isEnabled;
|
|
||||||
CALayer _rootLayer;
|
|
||||||
CALayer _hourHandLayer;
|
|
||||||
CALayer _minuteHandLayer;
|
|
||||||
CALayer _secondHandLayer;
|
|
||||||
CALayer _middleHandLayer;
|
|
||||||
CPDatePicker _datePicker;
|
|
||||||
CPTextField _PMAMTextField;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Init methods
|
|
||||||
|
|
||||||
/*! Init a new _CPDatePickerClock
|
|
||||||
@param aFrame
|
|
||||||
@param aDatePicker
|
|
||||||
@return a new instance of _CPDatePickerClock
|
|
||||||
*/
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame datePicker:(CPDatePicker)aDatePicker
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
{
|
|
||||||
_datePicker = aDatePicker;
|
|
||||||
|
|
||||||
_PMAMTextField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-font" inState:CPThemeStateNormal] forThemeAttribute:@"font" inState:CPThemeStateNormal];
|
|
||||||
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-color" inState:CPThemeStateNormal];
|
|
||||||
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-shadow-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateNormal];
|
|
||||||
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-shadow-offset" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateNormal];
|
|
||||||
|
|
||||||
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-font" inState:CPThemeStateDisabled] forThemeAttribute:@"font" inState:CPThemeStateDisabled];
|
|
||||||
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
|
|
||||||
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-shadow-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateDisabled];
|
|
||||||
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-shadow-offset" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateDisabled];
|
|
||||||
|
|
||||||
[self addSubview:_PMAMTextField];
|
|
||||||
|
|
||||||
var middleHandSize = [_datePicker valueForThemeAttribute:@"middle-hand-size"],
|
|
||||||
minuteHandSize = [_datePicker valueForThemeAttribute:@"minute-hand-size"],
|
|
||||||
hourHandSize = [_datePicker valueForThemeAttribute:@"hour-hand-size"],
|
|
||||||
secondHandSize = [_datePicker valueForThemeAttribute:@"second-hand-size"];
|
|
||||||
|
|
||||||
// We use layer to make the rotation possible
|
|
||||||
_hourHandLayer = [[HandLayer alloc] initWithSize:hourHandSize];
|
|
||||||
[_hourHandLayer setBounds:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height)];
|
|
||||||
[_hourHandLayer setAnchorPoint:CGPointMakeZero()];
|
|
||||||
[_hourHandLayer setPosition:CGPointMake(0.0, 0.0)];
|
|
||||||
|
|
||||||
_minuteHandLayer = [[HandLayer alloc] initWithSize:minuteHandSize];
|
|
||||||
[_minuteHandLayer setBounds:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height)];
|
|
||||||
[_minuteHandLayer setAnchorPoint:CGPointMakeZero()];
|
|
||||||
[_minuteHandLayer setPosition:CGPointMake(0.0, 0.0)];
|
|
||||||
|
|
||||||
_secondHandLayer = [[HandLayer alloc] initWithSize:secondHandSize];
|
|
||||||
[_secondHandLayer setBounds:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height)];
|
|
||||||
[_secondHandLayer setAnchorPoint:CGPointMakeZero()];
|
|
||||||
[_secondHandLayer setPosition:CGPointMake(0.0, 0.0)];
|
|
||||||
|
|
||||||
_middleHandLayer = [[HandLayer alloc] initWithSize:middleHandSize];
|
|
||||||
[_middleHandLayer setBounds:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height)];
|
|
||||||
[_middleHandLayer setAnchorPoint:CGPointMakeZero()];
|
|
||||||
[_middleHandLayer setPosition:CGPointMake(0.0, 0.0)];
|
|
||||||
|
|
||||||
_rootLayer = [CALayer layer];
|
|
||||||
[self setWantsLayer:YES];
|
|
||||||
[self setLayer:_rootLayer];
|
|
||||||
|
|
||||||
[_hourHandLayer setNeedsDisplay];
|
|
||||||
[_middleHandLayer setNeedsDisplay];
|
|
||||||
[_secondHandLayer setNeedsDisplay];
|
|
||||||
[_minuteHandLayer setNeedsDisplay];
|
|
||||||
|
|
||||||
[_rootLayer addSublayer:_hourHandLayer];
|
|
||||||
[_rootLayer addSublayer:_minuteHandLayer];
|
|
||||||
[_rootLayer addSublayer:_secondHandLayer];
|
|
||||||
[_rootLayer addSublayer:_middleHandLayer];
|
|
||||||
|
|
||||||
[_rootLayer setNeedsDisplay];
|
|
||||||
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Layout methods
|
|
||||||
|
|
||||||
/*! Layout the subviews
|
|
||||||
*/
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
[super layoutSubviews];
|
|
||||||
|
|
||||||
var bounds = [self bounds],
|
|
||||||
dateValue = [[_datePicker dateValue] copy];
|
|
||||||
|
|
||||||
[dateValue _dateWithTimeZone:[_datePicker timeZone]];
|
|
||||||
|
|
||||||
[self setBackgroundColor:[_datePicker valueForThemeAttribute:@"bezel-color-clock" inState:[_datePicker themeState]]];
|
|
||||||
[_middleHandLayer setBackgroundHandColor:[_datePicker valueForThemeAttribute:@"middle-hand-color" inState:[_datePicker themeState]]];
|
|
||||||
[_hourHandLayer setBackgroundHandColor:[_datePicker valueForThemeAttribute:@"hour-hand-color" inState:[_datePicker themeState]]];
|
|
||||||
[_minuteHandLayer setBackgroundHandColor:[_datePicker valueForThemeAttribute:@"minute-hand-color" inState:[_datePicker themeState]]];
|
|
||||||
[_secondHandLayer setBackgroundHandColor:[_datePicker valueForThemeAttribute:@"second-hand-color" inState:[_datePicker themeState]]];
|
|
||||||
|
|
||||||
if ([_datePicker _isEnglishFormat])
|
|
||||||
{
|
|
||||||
if (dateValue.getHours() > 11)
|
|
||||||
[_PMAMTextField setStringValue:@"PM"]
|
|
||||||
else
|
|
||||||
[_PMAMTextField setStringValue:@"AM"];
|
|
||||||
|
|
||||||
[_PMAMTextField sizeToFit];
|
|
||||||
[_PMAMTextField setFrameOrigin:CGPointMake(bounds.size.width / 2 - [_PMAMTextField frameSize].width / 2, bounds.size.height / 2 + 15)];
|
|
||||||
[_PMAMTextField setHidden:NO];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[_PMAMTextField setHidden:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_hourHandLayer setRotationRadians:[self _hourPositionRadianForDate:dateValue]];
|
|
||||||
[_minuteHandLayer setRotationRadians:[self _minutePositionRadianForDate:dateValue]];
|
|
||||||
[_secondHandLayer setRotationRadians:[self _secondPositionRadianForDate:dateValue]];
|
|
||||||
|
|
||||||
// Check if we have to display the hand second
|
|
||||||
if (([_datePicker datePickerElements] & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
|
|
||||||
[_secondHandLayer setHidden:NO];
|
|
||||||
else
|
|
||||||
[_secondHandLayer setHidden:YES];
|
|
||||||
|
|
||||||
[_rootLayer setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Getter Setter methods
|
|
||||||
|
|
||||||
/*! Return the radian position of the hour
|
|
||||||
*/
|
|
||||||
- (float)_hourPositionRadianForDate:(CPDate)aDate
|
|
||||||
{
|
|
||||||
var hours = aDate.getHours() + aDate.getMinutes() / 60;
|
|
||||||
|
|
||||||
return (360 * hours / 12) * (Math.PI / 180)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Return the radian position of the second
|
|
||||||
*/
|
|
||||||
- (float)_secondPositionRadianForDate:(CPDate)aDate
|
|
||||||
{
|
|
||||||
return (360 * aDate.getSeconds() / 60) * (Math.PI / 180)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Return the radian position of the minute
|
|
||||||
*/
|
|
||||||
- (float)_minutePositionRadianForDate:(CPDate)aDate
|
|
||||||
{
|
|
||||||
var minutes = aDate.getMinutes() + aDate.getSeconds() / 60;
|
|
||||||
|
|
||||||
return (360 * minutes / 60) * (Math.PI / 180)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set enabled
|
|
||||||
@param aBoolean
|
|
||||||
*/
|
|
||||||
- (void)setEnabled:(BOOL)aBoolean
|
|
||||||
{
|
|
||||||
_isEnabled = aBoolean;
|
|
||||||
[_PMAMTextField setEnabled:aBoolean];
|
|
||||||
[_hourHandLayer setEnabled:aBoolean];
|
|
||||||
[_middleHandLayer setEnabled:aBoolean];
|
|
||||||
[_secondHandLayer setEnabled:aBoolean];
|
|
||||||
[_minuteHandLayer setEnabled:aBoolean];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@implementation HandLayer : CALayer
|
|
||||||
{
|
|
||||||
BOOL _isEnabled @accessors(setter=setEnabled:, getter=isEnabled);
|
|
||||||
|
|
||||||
CALayer _imageLayer;
|
|
||||||
float _rotationRadians;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Init methods
|
|
||||||
|
|
||||||
/*! Init a new hand layer with an image. The image will be draw in a ImageLayer
|
|
||||||
@param anImage
|
|
||||||
@return a new instance of handLayer
|
|
||||||
*/
|
|
||||||
- (id)initWithSize:(CGSize)aSize
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_isEnabled = YES;
|
|
||||||
_imageLayer = [CALayer layer];
|
|
||||||
_rotationRadians = 0;
|
|
||||||
|
|
||||||
[_imageLayer setDelegate:self];
|
|
||||||
[_imageLayer setBounds:CGRectMake(0.0, 0.0, aSize.width, aSize.height)];
|
|
||||||
|
|
||||||
[self addSublayer:_imageLayer];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Setter Getter methods
|
|
||||||
|
|
||||||
/*! Set the bounds of the layer. The imageLayer will be at the center of this bounds
|
|
||||||
@param aRect
|
|
||||||
*/
|
|
||||||
- (void)setBounds:(CGRect)aRect
|
|
||||||
{
|
|
||||||
[super setBounds:aRect];
|
|
||||||
|
|
||||||
[_imageLayer setPosition:CGPointMake(CGRectGetMidX(aRect), CGRectGetMidY(aRect))];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set the rotation of the imageLayer
|
|
||||||
@param radians
|
|
||||||
*/
|
|
||||||
- (void)setRotationRadians:(float)radians
|
|
||||||
{
|
|
||||||
if (_rotationRadians == radians)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_rotationRadians = radians;
|
|
||||||
|
|
||||||
[_imageLayer setAffineTransform:CGAffineTransformScale(
|
|
||||||
CGAffineTransformMakeRotation(_rotationRadians),
|
|
||||||
1.0, 1.0)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setBackgroundHandColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
[_imageLayer setBackgroundColor:aColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setEnabled:(BOOL)aBoolean
|
|
||||||
{
|
|
||||||
_isEnabled = aBoolean;
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPDictionaryController.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Adapted from Cocotron, by Johannes Fortmann
|
|
||||||
*
|
|
||||||
* Created by Blair Duncan
|
|
||||||
* Copyright 2013, SGL Studio, BBDO Toronto All rights reserved.
|
|
||||||
*
|
|
||||||
* 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 "CPArrayController.j"
|
|
||||||
|
|
||||||
@implementation CPDictionaryController : CPArrayController
|
|
||||||
{
|
|
||||||
CPDictionary _contentDictionary;
|
|
||||||
|
|
||||||
CPArray _includedKeys @accessors(property=includedKeys);
|
|
||||||
CPArray _excludedKeys @accessors(property=excludedKeys);
|
|
||||||
|
|
||||||
CPString _initialKey @accessors(property=initialKey);
|
|
||||||
id _initialValue @accessors(property=initialValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_initialKey = @"key";
|
|
||||||
_initialValue = @"value";
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)newObject
|
|
||||||
{
|
|
||||||
var keys = [_contentDictionary allKeys],
|
|
||||||
newKey = _initialKey,
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
if ([keys containsObject:newKey])
|
|
||||||
while ([keys containsObject:newKey])
|
|
||||||
newKey = [CPString stringWithFormat:@"%@%i", _initialKey, ++count];
|
|
||||||
|
|
||||||
return [self _newObjectWithKey:newKey value:_initialValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_newObjectWithKey:(CPString)aKey value:(id)aValue
|
|
||||||
{
|
|
||||||
var aNewObject = [_CPDictionaryControllerKeyValuePair new];
|
|
||||||
|
|
||||||
aNewObject._dictionary = _contentDictionary;
|
|
||||||
aNewObject._controller = self;
|
|
||||||
aNewObject._key = aKey;
|
|
||||||
|
|
||||||
if (aValue !== nil)
|
|
||||||
[aNewObject setValue:aValue];
|
|
||||||
|
|
||||||
return aNewObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPDictionary)contentDictionary
|
|
||||||
{
|
|
||||||
return _contentDictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setContentDictionary:(CPDictionary)aDictionary
|
|
||||||
{
|
|
||||||
if (aDictionary == _contentDictionary)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ([aDictionary isKindOfClass:[CPDictionary class]])
|
|
||||||
_contentDictionary = aDictionary;
|
|
||||||
else
|
|
||||||
_contentDictionary = nil;
|
|
||||||
|
|
||||||
var array = [CPArray array],
|
|
||||||
allKeys = [_contentDictionary allKeys];
|
|
||||||
|
|
||||||
[allKeys addObjectsFromArray:_includedKeys];
|
|
||||||
|
|
||||||
var iter = [[CPSet setWithArray:allKeys] objectEnumerator],
|
|
||||||
obj;
|
|
||||||
|
|
||||||
while ((obj = [iter nextObject]) !== nil)
|
|
||||||
if (![_excludedKeys containsObject:obj])
|
|
||||||
[array addObject:[self _newObjectWithKey:obj value:nil]];
|
|
||||||
|
|
||||||
[super setContent:array];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
var CPIncludedKeys = @"CPIncludedKeys",
|
|
||||||
CPExcludedKeys = @"CPExcludedKeys";
|
|
||||||
|
|
||||||
@implementation CPDictionaryController (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_includedKeys = [aCoder decodeObjectForKey:CPIncludedKeys];
|
|
||||||
_excludedKeys = [aCoder decodeObjectForKey:CPExcludedKeys];
|
|
||||||
_initialKey = @"key";
|
|
||||||
_initialValue = @"value";
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_includedKeys forKey:CPIncludedKeys];
|
|
||||||
[aCoder encodeObject:_excludedKeys forKey:CPExcludedKeys];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPDictionaryControllerKeyValuePair : CPObject
|
|
||||||
{
|
|
||||||
CPString _key @accessors(property=key);
|
|
||||||
CPDictionary _dictionary @accessors(property=dictionary);
|
|
||||||
CPDictionaryController _controller @accessors(property=controller);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)value
|
|
||||||
{
|
|
||||||
return [_dictionary objectForKey:_key];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue
|
|
||||||
{
|
|
||||||
[_dictionary setObject:aValue forKey:_key];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isExplicitlyIncluded
|
|
||||||
{
|
|
||||||
return [[_controller _includedKeys] containsObject:_key];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
||||||
+8
-12
@@ -23,16 +23,12 @@
|
|||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
@import <Foundation/CPArray.j>
|
@import <Foundation/CPArray.j>
|
||||||
|
|
||||||
@import "CPAlert.j"
|
@import "CPApplication.j"
|
||||||
@import "CPResponder.j"
|
@import "CPResponder.j"
|
||||||
@import "CPSavePanel.j"
|
@import "CPSavePanel.j"
|
||||||
@import "CPViewController.j"
|
@import "CPViewController.j"
|
||||||
@import "CPWindowController.j"
|
@import "CPWindowController.j"
|
||||||
|
|
||||||
@class CPDocumentController
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@global
|
@global
|
||||||
@@ -92,7 +88,7 @@ var CPDocumentUntitledCount = 0;
|
|||||||
@class CPDocument
|
@class CPDocument
|
||||||
|
|
||||||
CPDocument is used to represent a document/file in a Cappuccino application.
|
CPDocument is used to represent a document/file in a Cappuccino application.
|
||||||
In a document-based application, generally multiple documents are open simultaneously
|
In a document-based application, generally multiple documents are open simutaneously
|
||||||
(multiple text documents, slide presentations, spreadsheets, etc.), and multiple
|
(multiple text documents, slide presentations, spreadsheets, etc.), and multiple
|
||||||
CPDocuments should be used to represent this.
|
CPDocuments should be used to represent this.
|
||||||
*/
|
*/
|
||||||
@@ -129,7 +125,7 @@ var CPDocumentUntitledCount = 0;
|
|||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_windowControllers = [];
|
_windowControllers = [];
|
||||||
_viewControllersForWindowControllers = @{};
|
_viewControllersForWindowControllers = [CPDictionary dictionary];
|
||||||
|
|
||||||
_hasUndoManager = YES;
|
_hasUndoManager = YES;
|
||||||
_changeCount = 0;
|
_changeCount = 0;
|
||||||
@@ -146,7 +142,7 @@ var CPDocumentUntitledCount = 0;
|
|||||||
@param anError not used
|
@param anError not used
|
||||||
@return the initialized document
|
@return the initialized document
|
||||||
*/
|
*/
|
||||||
- (id)initWithType:(CPString)aType error:(/*{*/CPError/*}*/)anError
|
- (id)initWithType:(CPString)aType error:({CPError})anError
|
||||||
{
|
{
|
||||||
self = [self init];
|
self = [self init];
|
||||||
|
|
||||||
@@ -212,10 +208,10 @@ var CPDocumentUntitledCount = 0;
|
|||||||
throws an exception.
|
throws an exception.
|
||||||
@param aType the format of the data
|
@param aType the format of the data
|
||||||
@param anError not used
|
@param anError not used
|
||||||
@throws CPUnsupportedMethodException if this method hasn't been overridden by the subclass
|
@throws CPUnsupportedMethodException if this method hasn't been overriden by the subclass
|
||||||
@return the document data
|
@return the document data
|
||||||
*/
|
*/
|
||||||
- (CPData)dataOfType:(CPString)aType error:(/*{*/CPError/*}*/)anError
|
- (CPData)dataOfType:(CPString)aType error:({CPError})anError
|
||||||
{
|
{
|
||||||
[CPException raise:CPUnsupportedMethodException
|
[CPException raise:CPUnsupportedMethodException
|
||||||
reason:"dataOfType:error: must be overridden by the document subclass."];
|
reason:"dataOfType:error: must be overridden by the document subclass."];
|
||||||
@@ -926,9 +922,9 @@ var CPDocumentUntitledCount = 0;
|
|||||||
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 };
|
||||||
};
|
}
|
||||||
|
|
||||||
var _CPSaveSessionMake = function(anAbsoluteURL, aSaveOperation, aChangeCount, aDelegate, aDidSaveSelector, aContextInfo, aConnection)
|
var _CPSaveSessionMake = function(anAbsoluteURL, aSaveOperation, aChangeCount, aDelegate, aDidSaveSelector, aContextInfo, aConnection)
|
||||||
{
|
{
|
||||||
return { absoluteURL:anAbsoluteURL, saveOperation:aSaveOperation, changeCount:aChangeCount, delegate:aDelegate, didSaveSelector:aDidSaveSelector, contextInfo:aContextInfo, connection:aConnection };
|
return { absoluteURL:anAbsoluteURL, saveOperation:aSaveOperation, changeCount:aChangeCount, delegate:aDelegate, didSaveSelector:aDidSaveSelector, contextInfo:aContextInfo, connection:aConnection };
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -24,11 +24,7 @@
|
|||||||
@import <Foundation/CPBundle.j>
|
@import <Foundation/CPBundle.j>
|
||||||
|
|
||||||
@import "CPDocument.j"
|
@import "CPDocument.j"
|
||||||
@import "CPOpenPanel.j"
|
@import "CPOpenPanel.j";
|
||||||
@import "CPMenuItem.j"
|
|
||||||
@import "CPWindowController.j"
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
|
|
||||||
var CPSharedDocumentController = nil;
|
var CPSharedDocumentController = nil;
|
||||||
@@ -128,7 +124,7 @@ var CPSharedDocumentController = nil;
|
|||||||
@param anError not used
|
@param anError not used
|
||||||
@return the created document
|
@return the created document
|
||||||
*/
|
*/
|
||||||
- (CPDocument)makeUntitledDocumentOfType:(CPString)aType error:(/*{*/CPError/*}*/)anError
|
- (CPDocument)makeUntitledDocumentOfType:(CPString)aType error:({CPError})anError
|
||||||
{
|
{
|
||||||
return [[[self documentClassForType:aType] alloc] initWithType:aType error:anError];
|
return [[[self documentClassForType:aType] alloc] initWithType:aType error:anError];
|
||||||
}
|
}
|
||||||
@@ -148,7 +144,7 @@ var CPSharedDocumentController = nil;
|
|||||||
{
|
{
|
||||||
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:@{ @"shouldDisplay": shouldDisplay }];
|
result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:[CPDictionary dictionaryWithObject:shouldDisplay forKey:@"shouldDisplay"]];
|
||||||
|
|
||||||
[self addDocument:result];
|
[self addDocument:result];
|
||||||
|
|
||||||
@@ -181,7 +177,7 @@ var CPSharedDocumentController = nil;
|
|||||||
@param aType the document type
|
@param aType the document type
|
||||||
@param aDelegate the delegate to notify
|
@param aDelegate the delegate to notify
|
||||||
@param aSelector the selector to notify with
|
@param aSelector the selector to notify with
|
||||||
@param aContextInfo the context information passed to the delegate
|
@param aContextInfo the context infomration passed to the delegate
|
||||||
*/
|
*/
|
||||||
- (CPDocument)makeDocumentWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
|
- (CPDocument)makeDocumentWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
|
||||||
{
|
{
|
||||||
@@ -343,10 +339,10 @@ var CPSharedDocumentController = nil;
|
|||||||
- (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info
|
- (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info
|
||||||
{
|
{
|
||||||
var context = {
|
var context = {
|
||||||
delegate: aDelegate,
|
delegate: aDelegate,
|
||||||
selector: didCloseSelector,
|
selector: didCloseSelector,
|
||||||
context: info
|
context: info
|
||||||
};
|
};
|
||||||
|
|
||||||
[self _closeDocumentsStartingWith:nil shouldClose:YES context:context];
|
[self _closeDocumentsStartingWith:nil shouldClose:YES context:context];
|
||||||
}
|
}
|
||||||
@@ -394,14 +390,13 @@ var CPSharedDocumentController = nil;
|
|||||||
|
|
||||||
- (void)noteNewRecentDocument:(CPDocument)aDocument
|
- (void)noteNewRecentDocument:(CPDocument)aDocument
|
||||||
{
|
{
|
||||||
[self noteNewRecentDocumentURL:[aDocument fileURL]];
|
[self noteNewRecentDocumentURL:[[aDocument fileURL] absoluteString]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)noteNewRecentDocumentURL:(CPURL)aURL
|
- (void)noteNewRecentDocumentURL:(CPString)aURL
|
||||||
{
|
{
|
||||||
var urlAsString = [aURL isKindOfClass:CPString] ? aURL : [aURL absoluteString];
|
|
||||||
if (typeof window["cpNoteNewRecentDocumentPath"] === 'function')
|
if (typeof window["cpNoteNewRecentDocumentPath"] === 'function')
|
||||||
window.cpNoteNewRecentDocumentPath(urlAsString);
|
window.cpNoteNewRecentDocumentPath(aURL);
|
||||||
|
|
||||||
[self _updateRecentDocumentsMenu];
|
[self _updateRecentDocumentsMenu];
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-41
@@ -20,17 +20,22 @@
|
|||||||
* 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 "CPDragServer_Constants.j"
|
@import "CPApplication.j"
|
||||||
@import "CPEvent.j"
|
@import "CPEvent.j"
|
||||||
@import "CPImageView.j"
|
@import "CPImageView.j"
|
||||||
@import "CPPasteboard.j"
|
@import "CPPasteboard.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
@import "CPWindow_Constants.j"
|
@import "CPWindow.j"
|
||||||
|
|
||||||
@class CPWindow // This file is imported by CPWindow.j
|
|
||||||
@class _CPDOMDataTransferPasteboard
|
|
||||||
|
|
||||||
@global CPApp
|
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])
|
||||||
|
|
||||||
@@ -39,8 +44,8 @@ var CPDragServerPreviousEvent = nil,
|
|||||||
|
|
||||||
var CPSharedDragServer = nil;
|
var CPSharedDragServer = nil;
|
||||||
|
|
||||||
var CPDragServerSource = nil,
|
var CPDragServerSource = nil;
|
||||||
CPDragServerDraggingInfo = nil;
|
var CPDragServerDraggingInfo = nil;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CPDraggingInfo is a container of information about a specific dragging session.
|
CPDraggingInfo is a container of information about a specific dragging session.
|
||||||
@@ -67,7 +72,7 @@ var CPDragServerSource = nil,
|
|||||||
- (unsigned)draggingSourceOperationMask
|
- (unsigned)draggingSourceOperationMask
|
||||||
*/
|
*/
|
||||||
|
|
||||||
- (CGPoint)draggingLocation
|
- (CPPoint)draggingLocation
|
||||||
{
|
{
|
||||||
return [[CPDragServer sharedDragServer] draggingLocation];
|
return [[CPDragServer sharedDragServer] draggingLocation];
|
||||||
}
|
}
|
||||||
@@ -163,7 +168,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_draggedWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessWindowMask];
|
_draggedWindow = [[CPWindow alloc] initWithContentRect:_CGRectMakeZero() styleMask:CPBorderlessWindowMask];
|
||||||
|
|
||||||
[_draggedWindow setLevel:CPDraggingWindowLevel];
|
[_draggedWindow setLevel:CPDraggingWindowLevel];
|
||||||
}
|
}
|
||||||
@@ -201,12 +206,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
- (void)draggingSourceUpdatedWithGlobalLocation:(CGPoint)aGlobalLocation
|
- (void)draggingSourceUpdatedWithGlobalLocation:(CGPoint)aGlobalLocation
|
||||||
{
|
{
|
||||||
if (![CPPlatform supportsDragAndDrop])
|
if (![CPPlatform supportsDragAndDrop])
|
||||||
{
|
[_draggedWindow setFrameOrigin:_CGPointMake(aGlobalLocation.x - _draggingOffset.width, aGlobalLocation.y - _draggingOffset.height)];
|
||||||
var frame = [_draggedWindow frame];
|
|
||||||
frame.origin.x = aGlobalLocation.x - _draggingOffset.width;
|
|
||||||
frame.origin.y = aGlobalLocation.y - _draggingOffset.height;
|
|
||||||
[_draggedWindow _setFrame:frame display:YES animate:NO constrainWidth:NO constrainHeight:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_movedTo_)
|
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_movedTo_)
|
||||||
[_draggingSource draggedImage:[_draggedView image] movedTo:aGlobalLocation];
|
[_draggingSource draggedImage:[_draggedView image] movedTo:aGlobalLocation];
|
||||||
@@ -220,14 +220,14 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
[_draggingUpdateTimer invalidate];
|
[_draggingUpdateTimer invalidate];
|
||||||
_draggingUpdateTimer = nil;
|
_draggingUpdateTimer = nil;
|
||||||
|
|
||||||
var dragOperation = CPDragOperationCopy,
|
var dragOperation = CPDragOperationCopy;
|
||||||
// We have to convert base to bridge since the drag event comes from the source window, not the drag window.
|
// We have to convert base to bridge since the drag event comes from the source window, not the drag window.
|
||||||
draggingDestination = [aPlatformWindow _dragHitTest:aLocation pasteboard:[CPDragServerDraggingInfo draggingPasteboard]];
|
var draggingDestination = [aPlatformWindow _dragHitTest:aLocation pasteboard:[CPDragServerDraggingInfo draggingPasteboard]];
|
||||||
|
|
||||||
if (draggingDestination)
|
if (draggingDestination)
|
||||||
_draggingLocation = [DRAGGING_WINDOW(draggingDestination) convertPlatformWindowToBase:aLocation];
|
_draggingLocation = [DRAGGING_WINDOW(draggingDestination) convertPlatformWindowToBase:aLocation];
|
||||||
|
|
||||||
if (draggingDestination !== _draggingDestination)
|
if(draggingDestination !== _draggingDestination)
|
||||||
{
|
{
|
||||||
if ([_draggingDestination respondsToSelector:@selector(draggingExited:)])
|
if ([_draggingDestination respondsToSelector:@selector(draggingExited:)])
|
||||||
[_draggingDestination draggingExited:CPDragServerDraggingInfo];
|
[_draggingDestination draggingExited:CPDragServerDraggingInfo];
|
||||||
@@ -253,7 +253,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
_draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval
|
_draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval
|
||||||
target:self
|
target:self
|
||||||
selector:@selector(_sendPeriodicDraggingUpdate:)
|
selector:@selector(_sendPeriodicDraggingUpdate:)
|
||||||
userInfo:@{ "platformWindow":aPlatformWindow, "location":aLocation }
|
userInfo:[CPDictionary dictionaryWithJSObject:{platformWindow:aPlatformWindow, location:aLocation}]
|
||||||
repeats:NO];
|
repeats:NO];
|
||||||
|
|
||||||
var scrollView = [_draggingDestination isKindOfClass:[CPView class]] ? [_draggingDestination enclosingScrollView] : nil;
|
var scrollView = [_draggingDestination isKindOfClass:[CPView class]] ? [_draggingDestination enclosingScrollView] : nil;
|
||||||
@@ -261,7 +261,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
{
|
{
|
||||||
var contentView = [scrollView contentView],
|
var contentView = [scrollView contentView],
|
||||||
bounds = [contentView bounds],
|
bounds = [contentView bounds],
|
||||||
insetBounds = CGRectInset(bounds, 30, 30),
|
insetBounds = CGRectInset(bounds, 10, 10),
|
||||||
eventLocation = [contentView convertPoint:_draggingLocation fromView:nil],
|
eventLocation = [contentView convertPoint:_draggingLocation fromView:nil],
|
||||||
deltaX = 0,
|
deltaX = 0,
|
||||||
deltaY = 0;
|
deltaY = 0;
|
||||||
@@ -292,11 +292,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
deltaX = insetBounds.size.width;
|
deltaX = insetBounds.size.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
var scrollPoint = CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY);
|
[contentView scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
|
||||||
|
|
||||||
[contentView scrollToPoint:scrollPoint];
|
|
||||||
[[scrollView _headerView] scrollPoint:scrollPoint];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,26 +366,22 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
if (mouseDownWindow)
|
if (mouseDownWindow)
|
||||||
mouseDownEventLocation = [mouseDownWindow convertBaseToGlobal:mouseDownEventLocation];
|
mouseDownEventLocation = [mouseDownWindow convertBaseToGlobal:mouseDownEventLocation];
|
||||||
|
|
||||||
_draggingOffset = CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y);
|
_draggingOffset = _CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_draggingOffset = CGSizeMakeZero();
|
_draggingOffset = _CGSizeMakeZero();
|
||||||
|
|
||||||
if ([CPPlatform isBrowser])
|
if ([CPPlatform isBrowser])
|
||||||
[_draggedWindow setPlatformWindow:[aWindow platformWindow]];
|
[_draggedWindow setPlatformWindow:[aWindow platformWindow]];
|
||||||
|
|
||||||
[aView setFrameOrigin:CGPointMakeZero()];
|
[aView setFrameOrigin:_CGPointMakeZero()];
|
||||||
|
|
||||||
var mouseLocation = [CPEvent mouseLocation],
|
var mouseLocation = [CPEvent mouseLocation];
|
||||||
viewSize = [aView frameSize],
|
|
||||||
startDragLocationX = mouseLocation.x - _draggingOffset.width,
|
|
||||||
startDragLocationY = mouseLocation.y - _draggingOffset.height,
|
|
||||||
draggedWindowFrame = CGRectMake(startDragLocationX, startDragLocationY, viewSize.width, viewSize.height);
|
|
||||||
|
|
||||||
// Place it where the mouse pointer is.
|
// Place it where the mouse pointer is.
|
||||||
_startDragLocation = CGPointMake(startDragLocationX, startDragLocationY);
|
_startDragLocation = _CGPointMake(mouseLocation.x - _draggingOffset.width, mouseLocation.y - _draggingOffset.height);
|
||||||
|
[_draggedWindow setFrameOrigin:_startDragLocation];
|
||||||
[_draggedWindow _setFrame:draggedWindowFrame display:YES animate:NO constrainWidth:NO constrainHeight:NO];
|
[_draggedWindow setFrameSize:[aView frame].size];
|
||||||
|
|
||||||
[[_draggedWindow contentView] addSubview:aView];
|
[[_draggedWindow contentView] addSubview:aView];
|
||||||
|
|
||||||
@@ -438,7 +430,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
var imageSize = [anImage size];
|
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:_CGRectMake(0.0, 0.0, imageSize.width, imageSize.height)];
|
||||||
|
|
||||||
[_imageView setImage:anImage];
|
[_imageView setImage:anImage];
|
||||||
|
|
||||||
@@ -464,9 +456,8 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
}
|
}
|
||||||
else if (type === CPKeyDown)
|
else if (type === CPKeyDown)
|
||||||
{
|
{
|
||||||
var characters = [anEvent characters];
|
var keyCode = [anEvent keyCode];
|
||||||
|
if (keyCode === CPEscapeKeyCode)
|
||||||
if (characters === CPEscapeFunctionKey)
|
|
||||||
{
|
{
|
||||||
_dragOperation = CPDragOperationNone;
|
_dragOperation = CPDragOperationNone;
|
||||||
[self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation];
|
[self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation];
|
||||||
@@ -482,7 +473,39 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
// If we're not a mouse up, then we're going to want to grab the next event.
|
// If we're not a mouse up, then we're going to want to grab the next event.
|
||||||
[CPApp setTarget:self selector:@selector(trackDragging:)
|
[CPApp setTarget:self selector:@selector(trackDragging:)
|
||||||
forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPKeyDownMask
|
forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPKeyDownMask
|
||||||
untilDate:nil inMode:0 dequeue:YES];
|
untilDate:nil inMode:0 dequeue:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation CPWindow (CPDraggingAdditions)
|
||||||
|
|
||||||
|
/* @ignore */
|
||||||
|
- (id)_dragHitTest:(CGPoint)aPoint pasteboard:(CPPasteboard)aPasteboard
|
||||||
|
{
|
||||||
|
// If none of our views or ourselves has registered for drag events...
|
||||||
|
if (!_inclusiveRegisteredDraggedTypes)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
// We don't need to do this because the only place this gets called
|
||||||
|
// -_dragHitTest: in CPPlatformWindow does this already. Perhaps to
|
||||||
|
// be safe?
|
||||||
|
// if (![self containsPoint:aPoint])
|
||||||
|
// return nil;
|
||||||
|
|
||||||
|
var adjustedPoint = [self convertPlatformWindowToBase:aPoint],
|
||||||
|
hitView = [_windowView hitTest:adjustedPoint];
|
||||||
|
|
||||||
|
while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]])
|
||||||
|
hitView = [hitView superview];
|
||||||
|
|
||||||
|
if (hitView)
|
||||||
|
return hitView;
|
||||||
|
|
||||||
|
if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]])
|
||||||
|
return self;
|
||||||
|
|
||||||
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPDragServer_Constants.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
|
|
||||||
*/
|
|
||||||
|
|
||||||
CPDragOperationNone = 0;
|
|
||||||
CPDragOperationCopy = 1 << 1;
|
|
||||||
CPDragOperationLink = 1 << 1;
|
|
||||||
CPDragOperationGeneric = 1 << 2;
|
|
||||||
CPDragOperationPrivate = 1 << 3;
|
|
||||||
CPDragOperationMove = 1 << 4;
|
|
||||||
CPDragOperationDelete = 1 << 5;
|
|
||||||
CPDragOperationEvery = -1;
|
|
||||||
+205
-121
@@ -20,24 +20,165 @@
|
|||||||
* 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 "CPEvent_Constants.j"
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPObjJRuntime.j>
|
|
||||||
@import <Foundation/CPString.j>
|
|
||||||
|
|
||||||
@import "CPCompatibility.j"
|
|
||||||
@import "CGGeometry.j"
|
|
||||||
@import "CPText.j"
|
@import "CPText.j"
|
||||||
|
|
||||||
@class CPTextField
|
|
||||||
|
|
||||||
@global CPApp
|
CPLeftMouseDown = 1;
|
||||||
|
CPLeftMouseUp = 2;
|
||||||
|
CPRightMouseDown = 3;
|
||||||
|
CPRightMouseUp = 4;
|
||||||
|
CPMouseMoved = 5;
|
||||||
|
CPLeftMouseDragged = 6;
|
||||||
|
CPRightMouseDragged = 7;
|
||||||
|
CPMouseEntered = 8;
|
||||||
|
CPMouseExited = 9;
|
||||||
|
CPKeyDown = 10;
|
||||||
|
CPKeyUp = 11;
|
||||||
|
CPFlagsChanged = 12;
|
||||||
|
CPAppKitDefined = 13;
|
||||||
|
CPSystemDefined = 14;
|
||||||
|
CPApplicationDefined = 15;
|
||||||
|
CPPeriodic = 16;
|
||||||
|
CPCursorUpdate = 17;
|
||||||
|
CPScrollWheel = 22;
|
||||||
|
CPOtherMouseDown = 25;
|
||||||
|
CPOtherMouseUp = 26;
|
||||||
|
CPOtherMouseDragged = 27;
|
||||||
|
|
||||||
|
// iPhone Event Types
|
||||||
|
CPTouchStart = 28;
|
||||||
|
CPTouchMove = 29;
|
||||||
|
CPTouchEnd = 30;
|
||||||
|
CPTouchCancel = 31;
|
||||||
|
|
||||||
|
CPAlphaShiftKeyMask = 1 << 16;
|
||||||
|
CPShiftKeyMask = 1 << 17;
|
||||||
|
CPControlKeyMask = 1 << 18;
|
||||||
|
CPAlternateKeyMask = 1 << 19;
|
||||||
|
CPCommandKeyMask = 1 << 20;
|
||||||
|
CPNumericPadKeyMask = 1 << 21;
|
||||||
|
CPHelpKeyMask = 1 << 22;
|
||||||
|
CPFunctionKeyMask = 1 << 23;
|
||||||
|
CPDeviceIndependentModifierFlagsMask = 0xffff0000;
|
||||||
|
|
||||||
|
CPLeftMouseDownMask = 1 << CPLeftMouseDown;
|
||||||
|
CPLeftMouseUpMask = 1 << CPLeftMouseUp;
|
||||||
|
CPRightMouseDownMask = 1 << CPRightMouseDown;
|
||||||
|
CPRightMouseUpMask = 1 << CPRightMouseUp;
|
||||||
|
CPOtherMouseDownMask = 1 << CPOtherMouseDown;
|
||||||
|
CPOtherMouseUpMask = 1 << CPOtherMouseUp;
|
||||||
|
CPMouseMovedMask = 1 << CPMouseMoved;
|
||||||
|
CPLeftMouseDraggedMask = 1 << CPLeftMouseDragged;
|
||||||
|
CPRightMouseDraggedMask = 1 << CPRightMouseDragged;
|
||||||
|
CPOtherMouseDragged = 1 << CPOtherMouseDragged;
|
||||||
|
CPMouseEnteredMask = 1 << CPMouseEntered;
|
||||||
|
CPMouseExitedMask = 1 << CPMouseExited;
|
||||||
|
CPCursorUpdateMask = 1 << CPCursorUpdate;
|
||||||
|
CPKeyDownMask = 1 << CPKeyDown;
|
||||||
|
CPKeyUpMask = 1 << CPKeyUp;
|
||||||
|
CPFlagsChangedMask = 1 << CPFlagsChanged;
|
||||||
|
CPAppKitDefinedMask = 1 << CPAppKitDefined;
|
||||||
|
CPSystemDefinedMask = 1 << CPSystemDefined;
|
||||||
|
CPApplicationDefinedMask = 1 << CPApplicationDefined;
|
||||||
|
CPPeriodicMask = 1 << CPPeriodic;
|
||||||
|
CPScrollWheelMask = 1 << CPScrollWheel;
|
||||||
|
CPAnyEventMask = 0xffffffff;
|
||||||
|
|
||||||
|
CPUpArrowFunctionKey = "\uF700";
|
||||||
|
CPDownArrowFunctionKey = "\uF701";
|
||||||
|
CPLeftArrowFunctionKey = "\uF702";
|
||||||
|
CPRightArrowFunctionKey = "\uF703";
|
||||||
|
CPF1FunctionKey = "\uF704";
|
||||||
|
CPF2FunctionKey = "\uF705";
|
||||||
|
CPF3FunctionKey = "\uF706";
|
||||||
|
CPF4FunctionKey = "\uF707";
|
||||||
|
CPF5FunctionKey = "\uF708";
|
||||||
|
CPF6FunctionKey = "\uF709";
|
||||||
|
CPF7FunctionKey = "\uF70A";
|
||||||
|
CPF8FunctionKey = "\uF70B";
|
||||||
|
CPF9FunctionKey = "\uF70C";
|
||||||
|
CPF10FunctionKey = "\uF70D";
|
||||||
|
CPF11FunctionKey = "\uF70E";
|
||||||
|
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]"),
|
_CPEventUpperCaseRegex = new RegExp("[A-Z]");
|
||||||
_CPEventStartupMilliseconds = new Date().getTime();
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -47,7 +188,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
@implementation CPEvent : CPObject
|
@implementation CPEvent : CPObject
|
||||||
{
|
{
|
||||||
CPEventType _type;
|
CPEventType _type;
|
||||||
CGPoint _location;
|
CPPoint _location;
|
||||||
unsigned _modifierFlags;
|
unsigned _modifierFlags;
|
||||||
CPTimeInterval _timestamp;
|
CPTimeInterval _timestamp;
|
||||||
CPGraphicsContext _context;
|
CPGraphicsContext _context;
|
||||||
@@ -61,26 +202,14 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
BOOL _isARepeat;
|
BOOL _isARepeat;
|
||||||
unsigned _keyCode;
|
unsigned _keyCode;
|
||||||
DOMEvent _DOMEvent;
|
DOMEvent _DOMEvent;
|
||||||
int _data1;
|
|
||||||
int _data2;
|
|
||||||
short _subtype;
|
|
||||||
|
|
||||||
float _deltaX;
|
float _deltaX;
|
||||||
float _deltaY;
|
float _deltaY;
|
||||||
float _deltaZ;
|
float _deltaZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the current time in fractional seconds since startup.
|
|
||||||
*/
|
|
||||||
+ (CPTimeInterval)currentTimestamp
|
|
||||||
{
|
|
||||||
return (new Date().getTime() - _CPEventStartupMilliseconds) / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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 \c aWindowNumber
|
||||||
@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
|
||||||
@@ -105,8 +234,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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 \c aWindowNumber
|
||||||
@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
|
||||||
@@ -128,8 +256,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 \c aWindowNumber
|
||||||
@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
|
||||||
@@ -150,29 +277,17 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext subtype:aSubtype data1:aData1 data2:aData2];
|
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext subtype:aSubtype data1:aData1 data2:aData2];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)_initWithType:(CPEventType)anEventType
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_type = anEventType;
|
|
||||||
|
|
||||||
// Make sure these are 0 rather than nil.
|
|
||||||
_deltaX = 0;
|
|
||||||
_deltaY = 0;
|
|
||||||
_deltaZ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CGPoint)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
|
||||||
{
|
{
|
||||||
if (self = [self _initWithType:anEventType])
|
self = [super init];
|
||||||
|
|
||||||
|
if (self)
|
||||||
{
|
{
|
||||||
_location = CGPointCreateCopy(aPoint);
|
_type = anEventType;
|
||||||
|
_location = CPPointCreateCopy(aPoint);
|
||||||
_modifierFlags = modifierFlags;
|
_modifierFlags = modifierFlags;
|
||||||
_timestamp = aTimestamp;
|
_timestamp = aTimestamp;
|
||||||
_context = aGraphicsContext;
|
_context = aGraphicsContext;
|
||||||
@@ -186,13 +301,16 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (id)_initKeyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
|
- (id)_initKeyEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned int)modifierFlags
|
||||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||||
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
|
||||||
{
|
{
|
||||||
if (self = [self _initWithType:anEventType])
|
self = [super init];
|
||||||
|
|
||||||
|
if (self)
|
||||||
{
|
{
|
||||||
_location = CGPointCreateCopy(aPoint);
|
_type = anEventType;
|
||||||
|
_location = CPPointCreateCopy(aPoint);
|
||||||
_modifierFlags = modifierFlags;
|
_modifierFlags = modifierFlags;
|
||||||
_timestamp = aTimestamp;
|
_timestamp = aTimestamp;
|
||||||
_context = aGraphicsContext;
|
_context = aGraphicsContext;
|
||||||
@@ -211,9 +329,12 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||||
subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2
|
subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2
|
||||||
{
|
{
|
||||||
if (self = [self _initWithType:anEventType])
|
self = [super init];
|
||||||
|
|
||||||
|
if (self)
|
||||||
{
|
{
|
||||||
_location = CGPointCreateCopy(aPoint);
|
_type = anEventType;
|
||||||
|
_location = CPPointCreateCopy(aPoint);
|
||||||
_modifierFlags = modifierFlags;
|
_modifierFlags = modifierFlags;
|
||||||
_timestamp = aTimestamp;
|
_timestamp = aTimestamp;
|
||||||
_context = aGraphicsContext;
|
_context = aGraphicsContext;
|
||||||
@@ -227,15 +348,15 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the location of the mouse (for mouse events).
|
Returns the location of the mouse (for mouse events).
|
||||||
If the receiver is not a mouse event, it returns \c nil.
|
If this is not a mouse event, it returns \c nil.
|
||||||
If \c window returns \c nil, then the mouse coordinates will be based on the screen coordinates.
|
If \c window returns \c nil, then
|
||||||
|
the mouse coordinates will be based on the screen coordinates.
|
||||||
Otherwise, the coordinates are relative to the window's coordinates.
|
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 \c nil for non-mouse events.
|
||||||
*/
|
*/
|
||||||
- (CGPoint)locationInWindow
|
- (CGPoint)locationInWindow
|
||||||
{
|
{
|
||||||
return CGPointMakeCopy(_location);
|
return _CGPointMakeCopy(_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGPoint)globalLocation
|
- (CGPoint)globalLocation
|
||||||
@@ -250,7 +371,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns event information as a bit mask.
|
Returns event information as a bit mask
|
||||||
*/
|
*/
|
||||||
- (unsigned)modifierFlags
|
- (unsigned)modifierFlags
|
||||||
{
|
{
|
||||||
@@ -258,7 +379,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the time the event occurred in seconds since startup.
|
Returns the time the event occurred
|
||||||
*/
|
*/
|
||||||
- (CPTimeInterval)timestamp
|
- (CPTimeInterval)timestamp
|
||||||
{
|
{
|
||||||
@@ -274,7 +395,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the event's associated window.
|
Returns the event's associated window
|
||||||
*/
|
*/
|
||||||
- (CPWindow)window
|
- (CPWindow)window
|
||||||
{
|
{
|
||||||
@@ -305,7 +426,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the number of clicks that caused this event (mouse only).
|
Returns the number of clicks that caused this event. (mouse only)
|
||||||
*/
|
*/
|
||||||
- (int)clickCount
|
- (int)clickCount
|
||||||
{
|
{
|
||||||
@@ -313,8 +434,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the characters associated with this event (keyboard only).
|
Returns the characters associated with this event (keyboard only)
|
||||||
|
|
||||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||||
*/
|
*/
|
||||||
- (CPString)characters
|
- (CPString)characters
|
||||||
@@ -324,7 +444,6 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the character ignoring any modifiers (except shift).
|
Returns the character ignoring any modifiers (except shift).
|
||||||
|
|
||||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||||
*/
|
*/
|
||||||
- (CPString)charactersIgnoringModifiers
|
- (CPString)charactersIgnoringModifiers
|
||||||
@@ -334,7 +453,6 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the keyboard event was caused by the key being held down.
|
Returns \c YES if the keyboard event was caused by the key being held down.
|
||||||
|
|
||||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||||
*/
|
*/
|
||||||
- (BOOL)isARepeat
|
- (BOOL)isARepeat
|
||||||
@@ -344,7 +462,6 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the key's key code.
|
Returns the key's key code.
|
||||||
|
|
||||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||||
*/
|
*/
|
||||||
- (unsigned short)keyCode
|
- (unsigned short)keyCode
|
||||||
@@ -377,17 +494,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
return _DOMEvent;
|
return _DOMEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)data1
|
// Getting Scroll Wheel Event Infomration
|
||||||
{
|
|
||||||
return _data1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)data2
|
|
||||||
{
|
|
||||||
return _data2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getting Scroll Wheel Event Information
|
|
||||||
/*!
|
/*!
|
||||||
Returns the change in the x-axis for a mouse event.
|
Returns the change in the x-axis for a mouse event.
|
||||||
*/
|
*/
|
||||||
@@ -420,8 +527,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
if (_CPEventUpperCaseRegex.test(aKeyEquivalent))
|
if (_CPEventUpperCaseRegex.test(aKeyEquivalent))
|
||||||
aKeyEquivalentModifierMask |= CPShiftKeyMask;
|
aKeyEquivalentModifierMask |= CPShiftKeyMask;
|
||||||
|
|
||||||
// Windows and Linux don't have command keys, so just switch it to ctrl.
|
if (CPBrowserIsOperatingSystem(CPWindowsOperatingSystem) && (aKeyEquivalentModifierMask & CPCommandKeyMask))
|
||||||
if (!CPBrowserIsOperatingSystem(CPMacOperatingSystem) && (aKeyEquivalentModifierMask & CPCommandKeyMask))
|
|
||||||
{
|
{
|
||||||
aKeyEquivalentModifierMask |= CPControlKeyMask;
|
aKeyEquivalentModifierMask |= CPControlKeyMask;
|
||||||
aKeyEquivalentModifierMask &= ~CPCommandKeyMask;
|
aKeyEquivalentModifierMask &= ~CPCommandKeyMask;
|
||||||
@@ -450,32 +556,32 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
if (_modifierFlags & (CPCommandKeyMask | CPControlKeyMask))
|
if (_modifierFlags & (CPCommandKeyMask | CPControlKeyMask))
|
||||||
return YES;
|
return YES;
|
||||||
|
|
||||||
// Cocoa allows almost any key as a key equivalent unless the first responder is a
|
for(var i=0; i<characterCount; i++)
|
||||||
// text field (presumably a subclass of NSText.)
|
|
||||||
var firstResponderIsText = [[_window firstResponder] isKindOfClass:[CPTextField class]];
|
|
||||||
|
|
||||||
// Some keys are accepted as key equivalents even if the first responder is a text
|
|
||||||
// field.
|
|
||||||
for (var i = 0; i < characterCount; i++)
|
|
||||||
{
|
{
|
||||||
var c = _characters.charAt(i);
|
switch(_characters.charAt(i))
|
||||||
|
|
||||||
if ((c >= CPUpArrowFunctionKey && c <= CPModeSwitchFunctionKey) ||
|
|
||||||
c === CPEnterCharacter ||
|
|
||||||
c === CPNewlineCharacter ||
|
|
||||||
c === CPCarriageReturnCharacter ||
|
|
||||||
c === CPEscapeFunctionKey)
|
|
||||||
{
|
{
|
||||||
return YES;
|
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 !firstResponderIsText;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Generates periodic events every \c aPeriod seconds.
|
Gene rates periodic events every \c aPeriod 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
|
||||||
*/
|
*/
|
||||||
@@ -488,7 +594,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Stops the periodic events from being generated.
|
Stops the periodic events from being generated
|
||||||
*/
|
*/
|
||||||
+ (void)stopPeriodicEvents
|
+ (void)stopPeriodicEvents
|
||||||
{
|
{
|
||||||
@@ -500,34 +606,11 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
_CPEventPeriodicEventTimer = nil;
|
_CPEventPeriodicEventTimer = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
switch (_type)
|
|
||||||
{
|
|
||||||
case CPKeyDown:
|
|
||||||
case CPKeyUp:
|
|
||||||
case CPFlagsChanged:
|
|
||||||
return [CPString stringWithFormat:@"CPEvent: type=%d loc=%@ time=%.1f flags=0x%X win=%@ winNum=%d ctxt=%@ chars=\"%@\" unmodchars=\"%@\" repeat=%d keyCode=%d", _type, CGStringFromPoint(_location), _timestamp, _modifierFlags, _window, _windowNumber, _context, _characters, _charactersIgnoringModifiers, _isARepeat, _keyCode];
|
|
||||||
case CPLeftMouseDown:
|
|
||||||
case CPLeftMouseUp:
|
|
||||||
case CPRightMouseDown:
|
|
||||||
case CPRightMouseUp:
|
|
||||||
case CPMouseMoved:
|
|
||||||
case CPLeftMouseDragged:
|
|
||||||
case CPRightMouseDragged:
|
|
||||||
case CPMouseEntered:
|
|
||||||
case CPMouseExited:
|
|
||||||
return [CPString stringWithFormat:@"CPEvent: type=%d loc=%@ time=%.1f flags=0x%X win=%@ winNum=%d ctxt=%@ evNum=%d click=%d buttonNumber=%d pressure=%f", _type, CGStringFromPoint(_location), _timestamp, _modifierFlags, _window, _windowNumber, _context, _eventNumber, _clickCount, [self buttonNumber], _pressure];
|
|
||||||
default:
|
|
||||||
return [CPString stringWithFormat:@"CPEvent: type=%d loc=%@ time=%.1f flags=0x%X win=%@ winNum=%d ctxt=%@ subtype=%d data1=%d data2=%d", _type, CGStringFromPoint(_location), _timestamp, _modifierFlags, _window, _windowNumber, _context, _subtype, _data1, _data2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
function _CPEventFirePeriodEvent()
|
function _CPEventFirePeriodEvent()
|
||||||
{
|
{
|
||||||
[CPApp sendEvent:[CPEvent otherEventWithType:CPPeriodic location:CGPointMakeZero() modifierFlags:0 timestamp:[CPEvent currentTimestamp] windowNumber:0 context:nil subtype:0 data1:0 data2:0]];
|
[CPApp sendEvent:[CPEvent otherEventWithType:CPPeriodic location:_CGPointMakeZero() modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
var CPEventClass = [CPEvent class];
|
var CPEventClass = [CPEvent class];
|
||||||
@@ -549,3 +632,4 @@ function _CPEventFromNativeMouseEvent(aNativeEvent, anEventType, aPoint, modifie
|
|||||||
|
|
||||||
return aNativeEvent;
|
return aNativeEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPEvent_Constants.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
|
|
||||||
*/
|
|
||||||
|
|
||||||
CPLeftMouseDown = 1;
|
|
||||||
CPLeftMouseUp = 2;
|
|
||||||
CPRightMouseDown = 3;
|
|
||||||
CPRightMouseUp = 4;
|
|
||||||
CPMouseMoved = 5;
|
|
||||||
CPLeftMouseDragged = 6;
|
|
||||||
CPRightMouseDragged = 7;
|
|
||||||
CPMouseEntered = 8;
|
|
||||||
CPMouseExited = 9;
|
|
||||||
CPKeyDown = 10;
|
|
||||||
CPKeyUp = 11;
|
|
||||||
CPFlagsChanged = 12;
|
|
||||||
CPAppKitDefined = 13;
|
|
||||||
CPSystemDefined = 14;
|
|
||||||
CPApplicationDefined = 15;
|
|
||||||
CPPeriodic = 16;
|
|
||||||
CPCursorUpdate = 17;
|
|
||||||
CPScrollWheel = 22;
|
|
||||||
CPOtherMouseDown = 25;
|
|
||||||
CPOtherMouseUp = 26;
|
|
||||||
CPOtherMouseDragged = 27;
|
|
||||||
|
|
||||||
// iPhone Event Types
|
|
||||||
CPTouchStart = 28;
|
|
||||||
CPTouchMove = 29;
|
|
||||||
CPTouchEnd = 30;
|
|
||||||
CPTouchCancel = 31;
|
|
||||||
|
|
||||||
CPAlphaShiftKeyMask = 1 << 16;
|
|
||||||
CPShiftKeyMask = 1 << 17;
|
|
||||||
CPControlKeyMask = 1 << 18;
|
|
||||||
CPAlternateKeyMask = 1 << 19;
|
|
||||||
CPCommandKeyMask = 1 << 20;
|
|
||||||
CPNumericPadKeyMask = 1 << 21;
|
|
||||||
CPHelpKeyMask = 1 << 22;
|
|
||||||
CPFunctionKeyMask = 1 << 23;
|
|
||||||
CPDeviceIndependentModifierFlagsMask = 0xffff0000;
|
|
||||||
|
|
||||||
CPLeftMouseDownMask = 1 << CPLeftMouseDown;
|
|
||||||
CPLeftMouseUpMask = 1 << CPLeftMouseUp;
|
|
||||||
CPRightMouseDownMask = 1 << CPRightMouseDown;
|
|
||||||
CPRightMouseUpMask = 1 << CPRightMouseUp;
|
|
||||||
CPOtherMouseDownMask = 1 << CPOtherMouseDown;
|
|
||||||
CPOtherMouseUpMask = 1 << CPOtherMouseUp;
|
|
||||||
CPMouseMovedMask = 1 << CPMouseMoved;
|
|
||||||
CPLeftMouseDraggedMask = 1 << CPLeftMouseDragged;
|
|
||||||
CPRightMouseDraggedMask = 1 << CPRightMouseDragged;
|
|
||||||
CPOtherMouseDragged = 1 << CPOtherMouseDragged;
|
|
||||||
CPMouseEnteredMask = 1 << CPMouseEntered;
|
|
||||||
CPMouseExitedMask = 1 << CPMouseExited;
|
|
||||||
CPCursorUpdateMask = 1 << CPCursorUpdate;
|
|
||||||
CPKeyDownMask = 1 << CPKeyDown;
|
|
||||||
CPKeyUpMask = 1 << CPKeyUp;
|
|
||||||
CPFlagsChangedMask = 1 << CPFlagsChanged;
|
|
||||||
CPAppKitDefinedMask = 1 << CPAppKitDefined;
|
|
||||||
CPSystemDefinedMask = 1 << CPSystemDefined;
|
|
||||||
CPApplicationDefinedMask = 1 << CPApplicationDefined;
|
|
||||||
CPPeriodicMask = 1 << CPPeriodic;
|
|
||||||
CPScrollWheelMask = 1 << CPScrollWheel;
|
|
||||||
CPAnyEventMask = 0xffffffff;
|
|
||||||
|
|
||||||
CPUpArrowFunctionKey = "\uF700";
|
|
||||||
CPDownArrowFunctionKey = "\uF701";
|
|
||||||
CPLeftArrowFunctionKey = "\uF702";
|
|
||||||
CPRightArrowFunctionKey = "\uF703";
|
|
||||||
CPF1FunctionKey = "\uF704";
|
|
||||||
CPF2FunctionKey = "\uF705";
|
|
||||||
CPF3FunctionKey = "\uF706";
|
|
||||||
CPF4FunctionKey = "\uF707";
|
|
||||||
CPF5FunctionKey = "\uF708";
|
|
||||||
CPF6FunctionKey = "\uF709";
|
|
||||||
CPF7FunctionKey = "\uF70A";
|
|
||||||
CPF8FunctionKey = "\uF70B";
|
|
||||||
CPF9FunctionKey = "\uF70C";
|
|
||||||
CPF10FunctionKey = "\uF70D";
|
|
||||||
CPF11FunctionKey = "\uF70E";
|
|
||||||
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";
|
|
||||||
CPSpaceFunctionKey = "\u0020";
|
|
||||||
|
|
||||||
|
|
||||||
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";
|
|
||||||
+17
-26
@@ -38,6 +38,7 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
DOMElement _DOMParamElement;
|
DOMElement _DOMParamElement;
|
||||||
DOMElement _DOMObjectElement;
|
DOMElement _DOMObjectElement;
|
||||||
|
DOMElement _DOMInnerObjectElement;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,16 +52,22 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||||
{
|
{
|
||||||
_DOMObjectElement = document.createElement(@"object");
|
_DOMObjectElement = document.createElement(@"object");
|
||||||
_DOMObjectElement.id = [self elementID];
|
|
||||||
_DOMObjectElement.width = @"100%";
|
_DOMObjectElement.width = @"100%";
|
||||||
_DOMObjectElement.height = @"100%";
|
_DOMObjectElement.height = @"100%";
|
||||||
_DOMObjectElement.style.top = @"0px";
|
_DOMObjectElement.style.top = @"0px";
|
||||||
_DOMObjectElement.style.left = @"0px";
|
_DOMObjectElement.style.left = @"0px";
|
||||||
_DOMObjectElement.type = @"application/x-shockwave-flash";
|
_DOMObjectElement.type = @"application/x-shockwave-flash";
|
||||||
|
_DOMObjectElement.setAttribute(@"classid", IEFlashCLSID);
|
||||||
|
|
||||||
_DOMParamElement = document.createElement(@"param");
|
_DOMParamElement = document.createElement(@"param");
|
||||||
_DOMParamElement.name = @"movie";
|
_DOMParamElement.name = @"movie";
|
||||||
|
|
||||||
|
_DOMInnerObjectElement = document.createElement(@"object");
|
||||||
|
_DOMInnerObjectElement.width = @"100%";
|
||||||
|
_DOMInnerObjectElement.height = @"100%";
|
||||||
|
|
||||||
_DOMObjectElement.appendChild(_DOMParamElement);
|
_DOMObjectElement.appendChild(_DOMParamElement);
|
||||||
|
_DOMObjectElement.appendChild(_DOMInnerObjectElement);
|
||||||
|
|
||||||
_DOMElement.appendChild(_DOMObjectElement);
|
_DOMElement.appendChild(_DOMObjectElement);
|
||||||
}
|
}
|
||||||
@@ -82,7 +89,7 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||||
{
|
{
|
||||||
_DOMParamElement.value = [aFlashMovie filename];
|
_DOMParamElement.value = [aFlashMovie filename];
|
||||||
_DOMObjectElement.data = [aFlashMovie filename];
|
_DOMInnerObjectElement.data = [aFlashMovie filename];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[self _rebuildIEObjects];
|
[self _rebuildIEObjects];
|
||||||
@@ -97,17 +104,14 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
- (void)setFlashVars:(CPDictionary)aDictionary
|
- (void)setFlashVars:(CPDictionary)aDictionary
|
||||||
{
|
{
|
||||||
var varString = @"",
|
var varString = @"",
|
||||||
enumerator = [aDictionary keyEnumerator],
|
enumerator = [aDictionary keyEnumerator];
|
||||||
key;
|
|
||||||
|
|
||||||
if (key = [enumerator nextObject])
|
|
||||||
varString = [varString stringByAppendingFormat:@"%@=%@", key, [aDictionary objectForKey:key]];
|
|
||||||
|
|
||||||
|
var key;
|
||||||
while (key = [enumerator nextObject])
|
while (key = [enumerator nextObject])
|
||||||
varString = [varString stringByAppendingFormat:@"&%@=%@", key, [aDictionary objectForKey:key]];
|
varString = [varString stringByAppendingFormat:@"&%@=%@", key, [aDictionary objectForKey:key]];
|
||||||
|
|
||||||
if (!_params)
|
if (!_params)
|
||||||
_params = @{};
|
_params = [CPDictionary dictionary];
|
||||||
|
|
||||||
[_params setObject:varString forKey:@"flashvars"];
|
[_params setObject:varString forKey:@"flashvars"];
|
||||||
[self setParameters:_params];
|
[self setParameters:_params];
|
||||||
@@ -130,19 +134,16 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
_DOMObjectElement.removeChild([elements objectAtIndex:i]);
|
_DOMObjectElement.removeChild([elements objectAtIndex:i]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!_params)
|
_params = aDictionary;
|
||||||
_params = aDictionary;
|
|
||||||
else
|
|
||||||
[_params addEntriesFromDictionary:aDictionary];
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||||
{
|
{
|
||||||
_paramElements = @{};
|
_paramElements = [CPDictionary dictionary];
|
||||||
|
|
||||||
var enumerator = [_params keyEnumerator],
|
var enumerator = [_params keyEnumerator],
|
||||||
key;
|
key;
|
||||||
|
|
||||||
while (_DOMObjectElement && (key = [enumerator nextObject]) !== nil)
|
while (key = [enumerator nextObject] && _DOMObjectElement)
|
||||||
{
|
{
|
||||||
var param = document.createElement(@"param");
|
var param = document.createElement(@"param");
|
||||||
param.name = key;
|
param.name = key;
|
||||||
@@ -174,26 +175,16 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
paramEnumerator = [_params keyEnumerator],
|
paramEnumerator = [_params keyEnumerator],
|
||||||
key;
|
key;
|
||||||
|
|
||||||
while ((key = [paramEnumerator nextObject]) !== nil)
|
while (key = [paramEnumerator nextObject])
|
||||||
paramString = [paramString stringByAppendingFormat:@"<param name='%@' value='%@' />", key, [_params objectForKey:key]];
|
paramString = [paramString stringByAppendingFormat:@"<param name='%@' value='%@' />", key, [_params objectForKey:key]];
|
||||||
|
|
||||||
_DOMObjectElement = document.createElement(@"object");
|
_DOMObjectElement = document.createElement(@"object");
|
||||||
_DOMElement.appendChild(_DOMObjectElement);
|
_DOMElement.appendChild(_DOMObjectElement);
|
||||||
|
|
||||||
_DOMObjectElement.outerHTML = [CPString stringWithFormat:@"<object id=%@ classid=%@ width=%@ height=%@>%@</object>", [self elementID], IEFlashCLSID, CGRectGetWidth([self bounds]), CGRectGetHeight([self bounds]), paramString];
|
_DOMObjectElement.outerHTML = [CPString stringWithFormat:@"<object classid=%@ width=%@ height=%@>%@</object>", IEFlashCLSID, CGRectGetWidth([self bounds]), CGRectGetHeight([self bounds]), paramString];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
- (CPString)elementID
|
|
||||||
{
|
|
||||||
return @"CPFV_" + [self UID];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseMoved:(id)sommit
|
|
||||||
{
|
|
||||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDragged:(CPEvent)anEvent
|
- (void)mouseDragged:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||||
|
|||||||
+53
-330
@@ -25,88 +25,20 @@
|
|||||||
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
CPFontDefaultSystemFontFace = @"Arial, sans-serif";
|
|
||||||
CPFontDefaultSystemFontSize = 12;
|
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)]
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
To create a font of a size that will dynamically reflect the current
|
@ingroup appkit
|
||||||
system font size, use this for the size argument.
|
@class CPFont
|
||||||
*/
|
|
||||||
CPFontCurrentSystemSize = -1;
|
|
||||||
|
|
||||||
// For internal use only by this class and subclasses
|
The CPFont class allows control of the fonts used for displaying text anywhere on the screen. The primary method for getting a particular font is through one of the class methods that take a name and/or size as arguments, and return the appropriate CPFont.
|
||||||
_CPFontSystemFacePlaceholder = "_CPFontSystemFacePlaceholder";
|
|
||||||
|
|
||||||
var _CPFontCache = {},
|
|
||||||
_CPSystemFontCache = {},
|
|
||||||
_CPFontSystemFontFace = CPFontDefaultSystemFontFace,
|
|
||||||
_CPFontSystemFontSize = 12,
|
|
||||||
_CPFontFallbackFaces = CPFontDefaultSystemFontFace.split(", "),
|
|
||||||
_CPFontStripRegExp = new RegExp("(^\\s*[\"']?|[\"']?\\s*$)", "g");
|
|
||||||
|
|
||||||
|
|
||||||
#define _CPRealFontSize(aSize) (aSize <= 0 ? _CPFontSystemFontSize : aSize)
|
|
||||||
#define _CPFontNormalizedNames(aName) _CPFontNormalizedNameArray(aName).join(", ")
|
|
||||||
#define _CPCachedFont(aName, aSize, isBold, isItalic) _CPFontCache[_CPFontCreateCSSString(_CPFontNormalizedNames(aName), aSize, isBold, isItalic)]
|
|
||||||
#define _CPUserFont(aName, aSize, isBold, isItalic) _CPCachedFont(aName, aSize, isBold, isItalic) || [[CPFont alloc] _initWithName:aName size:aSize bold:isBold italic:isItalic system:NO]
|
|
||||||
|
|
||||||
#define _CPSystemFontCacheKey(aSize, isBold) (String(aSize) + (isBold ? "b" : ""))
|
|
||||||
#define _CPCachedSystemFont(aSize, isBold) _CPSystemFontCache[_CPSystemFontCacheKey(aSize, isBold)]
|
|
||||||
#define _CPSystemFont(aSize, isBold) (_CPCachedSystemFont(aSize, isBold) || [[CPFont alloc] _initWithName:_CPFontSystemFacePlaceholder size:aSize bold:isBold italic:NO system:YES])
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
@class CPFont
|
|
||||||
|
|
||||||
The CPFont class allows control of the fonts used for displaying text anywhere on the screen.
|
|
||||||
The primary method for getting a particular font is through one of the class methods that take
|
|
||||||
a name and/or size as arguments, and return the appropriate CPFont.
|
|
||||||
|
|
||||||
### System fonts
|
|
||||||
When you create a font using \c -systemFontOfSize: or \c -boldSystemFontOfSize:, a proxy font is
|
|
||||||
created that always refers to the current system font face and size. By default the system
|
|
||||||
font face/size is Arial 12px, with a fallback to sans-serif 12px. You may configure this at
|
|
||||||
runtime in two ways:
|
|
||||||
|
|
||||||
- By sending [CPFont setSystemFontFace:<face>] and/or [CPFont setSystemFontSize:<size>].
|
|
||||||
Note that if you change the system font face or size during runtime, the next time
|
|
||||||
any view using a system font is redrawn, it will show the new font.
|
|
||||||
- By configuring Info.plist for your application or for AppKit. You can set the font face
|
|
||||||
by adding a CPSystemFontFace string item to the Info.plist, and you can set the font size
|
|
||||||
by adding a CPSystemFontSize integer item to the Info.plist.
|
|
||||||
|
|
||||||
Note that in either case, you can specify a comma-delimited list of fonts as the font face.
|
|
||||||
Do not quote enclose font faces that contain spaces, that is done automatically when a CSS
|
|
||||||
representation of a font is requested.
|
|
||||||
|
|
||||||
The browser will use the first available font in the list you supply. CPFont always ensures
|
|
||||||
that Arial and sans-serif are in the CSS representation of a font as a fallback, so there is
|
|
||||||
no need to add them to the end of your font list.
|
|
||||||
|
|
||||||
### nib2cib conversion
|
|
||||||
Fonts are converted by nib2cib according to the following algorithm:
|
|
||||||
|
|
||||||
- If the font family is Lucida Grande, then a system font will be created.
|
|
||||||
- If the font is Lucida Grande 13 (plain), the "default" font will be used at runtime.
|
|
||||||
The default font is taken from the "font" theme attribute if one exists,
|
|
||||||
otherwise from the current system font face and size.
|
|
||||||
- Lucida Grande of any size other than 13 will retain its size.
|
|
||||||
- Fonts using any family other than Lucida Grande will be used as is, including the size.
|
|
||||||
|
|
||||||
### Using custom web fonts
|
|
||||||
The configurability of CPFont makes it easy to use a custom web font as the system font.
|
|
||||||
For example, if you want to use the google font Asap as the system font, you would do the
|
|
||||||
following:
|
|
||||||
|
|
||||||
- Add a <link> to the <head> of index-debug.html and index.html:
|
|
||||||
@code
|
|
||||||
<link href='http://fonts.googleapis.com/css?family=Asap:400,700' rel='stylesheet' type='text/css'>
|
|
||||||
@endcode
|
|
||||||
- Specify Asap as the system font in Info.plist by adding the following item:
|
|
||||||
@code
|
|
||||||
<key>CPSystemFontFace</key>
|
|
||||||
<string>Asap</string>
|
|
||||||
@endcode
|
|
||||||
*/
|
*/
|
||||||
@implementation CPFont : CPObject
|
@implementation CPFont : CPObject
|
||||||
{
|
{
|
||||||
@@ -115,217 +47,80 @@ following:
|
|||||||
float _ascender;
|
float _ascender;
|
||||||
float _descender;
|
float _descender;
|
||||||
float _lineHeight;
|
float _lineHeight;
|
||||||
BOOL _isBold @accessors(readonly, getter=isBold);
|
BOOL _isBold;
|
||||||
BOOL _isItalic @accessors(readonly, getter=isItalic);
|
|
||||||
BOOL _isSystem @accessors(readonly, getter=isSystem);
|
|
||||||
|
|
||||||
CPString _cssString;
|
CPString _cssString;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self !== [CPFont class])
|
var systemFont = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:"CPSystemFontFace"];
|
||||||
return;
|
|
||||||
|
|
||||||
var systemFontFace = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontFace"];
|
if (systemFont)
|
||||||
|
_CPFontSystemFontFace = systemFont;
|
||||||
if (!systemFontFace)
|
|
||||||
systemFontFace = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontFace"];
|
|
||||||
|
|
||||||
if (systemFontFace)
|
|
||||||
_CPFontSystemFontFace = _CPFontNormalizedNames(systemFontFace);
|
|
||||||
|
|
||||||
var systemFontSize = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontSize"];
|
|
||||||
|
|
||||||
if (!systemFontSize)
|
|
||||||
systemFontSize = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontSize"];
|
|
||||||
|
|
||||||
if (systemFontSize)
|
|
||||||
_CPFontSystemFontSize = systemFontSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the default system font face, which may consist of several comma-separated family names.
|
|
||||||
*/
|
|
||||||
+ (CPString)systemFontFace
|
|
||||||
{
|
|
||||||
return _CPFontSystemFontFace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the default system font face, which may consist of several comma-separated family names.
|
|
||||||
*/
|
|
||||||
+ (CPString)setSystemFontFace:(CPString)aFace
|
|
||||||
{
|
|
||||||
var normalizedFaces = _CPFontNormalizedNames(aFace);
|
|
||||||
|
|
||||||
if (normalizedFaces === _CPFontSystemFontFace)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self _invalidateSystemFontCache]
|
|
||||||
_CPFontSystemFontFace = aFace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the default system font size.
|
|
||||||
*/
|
|
||||||
+ (float)systemFontSize
|
|
||||||
{
|
|
||||||
return _CPFontSystemFontSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (float)systemFontSizeForControlSize:(CPControlSize)aSize
|
|
||||||
{
|
|
||||||
// TODO These sizes should be themable or made less arbitrary in some other way.
|
|
||||||
switch (aSize)
|
|
||||||
{
|
|
||||||
case CPSmallControlSize:
|
|
||||||
return _CPFontSystemFontSize - 1;
|
|
||||||
|
|
||||||
case CPMiniControlSize:
|
|
||||||
return _CPFontSystemFontSize - 2;
|
|
||||||
|
|
||||||
case CPRegularControlSize:
|
|
||||||
default:
|
|
||||||
return _CPFontSystemFontSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the default system font size.
|
|
||||||
*/
|
|
||||||
+ (float)setSystemFontSize:(float)size
|
|
||||||
{
|
|
||||||
if (size > 0 && size !== _CPFontSystemFontSize)
|
|
||||||
{
|
|
||||||
[self _invalidateSystemFontCache];
|
|
||||||
_CPFontSystemFontSize = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)_invalidateSystemFontCache
|
|
||||||
{
|
|
||||||
var systemSize = String(_CPFontSystemFontSize),
|
|
||||||
currentSize = String(CPFontCurrentSystemSize);
|
|
||||||
|
|
||||||
for (key in _CPSystemFontCache)
|
|
||||||
{
|
|
||||||
if (_CPSystemFontCache.hasOwnProperty(key) &&
|
|
||||||
(key.indexOf(systemSize) === 0 || key.indexOf(currentSize) === 0))
|
|
||||||
{
|
|
||||||
delete _CPSystemFontCache[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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
|
||||||
@param aSize the size of the font (in px). 0 or negative will create a font
|
@param aSize the size of the font (in points)
|
||||||
in the current system font size.
|
|
||||||
@return the requested font
|
@return the requested font
|
||||||
*/
|
*/
|
||||||
+ (CPFont)fontWithName:(CPString)aName size:(float)aSize
|
+ (CPFont)fontWithName:(CPString)aName size:(float)aSize
|
||||||
{
|
{
|
||||||
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, NO, NO);
|
return _CPCachedFont(aName, aSize, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:NO];
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a font with the specified name, size and style.
|
|
||||||
@param aName the name of the font
|
|
||||||
@param aSize the size of the font (in px). 0 or negative will create a font
|
|
||||||
in the current system font size.
|
|
||||||
@param italic whether the font should be italicized
|
|
||||||
@return the requested font
|
|
||||||
*/
|
|
||||||
+ (CPFont)fontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic
|
|
||||||
{
|
|
||||||
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, NO, italic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a bold font with the specified name and size.
|
Returns a bold font with the specified name and size.
|
||||||
@param aName the name of the font
|
@param aName the name of the font
|
||||||
@param aSize the size of the font (in px). 0 or negative will create a font
|
@param aSize the size of the font (in points)
|
||||||
in the current system font size.
|
|
||||||
@return the requested bold font
|
@return the requested bold font
|
||||||
*/
|
*/
|
||||||
+ (CPFont)boldFontWithName:(CPString)aName size:(float)aSize
|
+ (CPFont)boldFontWithName:(CPString)aName size:(float)aSize
|
||||||
{
|
{
|
||||||
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, YES, NO);
|
return _CPCachedFont(aName, aSize, YES) || [[CPFont alloc] _initWithName:aName size:aSize bold:YES];
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a bold font with the specified name, size and style.
|
|
||||||
@param aName the name of the font
|
|
||||||
@param aSize the size of the font (in px). 0 or negative will create a font
|
|
||||||
in the current system font size.
|
|
||||||
@param italic whether the font should be italicized
|
|
||||||
@return the requested font
|
|
||||||
*/
|
|
||||||
+ (CPFont)boldFontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic
|
|
||||||
{
|
|
||||||
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, YES, italic);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Internal font getter like fontWithName:size:italic: with a bold selector.
|
|
||||||
*/
|
|
||||||
+ (CPFont)_fontWithName:(CPString)aName size:(float)aSize bold:(BOOL)bold italic:(BOOL)italic
|
|
||||||
{
|
|
||||||
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, bold, italic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the system font scaled to the specified size
|
Returns the system font scaled to the specified size
|
||||||
@param aSize the size of the font (in px). 0 creates a static font
|
@param aSize the size of the font (in points)
|
||||||
in the current system font size. Negative creates a font
|
|
||||||
that dynamically tracks the current system font size.
|
|
||||||
@return the requested system font
|
@return the requested system font
|
||||||
*/
|
*/
|
||||||
+ (CPFont)systemFontOfSize:(CGSize)aSize
|
+ (CPFont)systemFontOfSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
return _CPSystemFont(aSize === 0 ? _CPFontSystemFontSize : aSize, NO);
|
return _CPCachedFont(_CPFontSystemFontFace, aSize, NO) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the bold system font scaled to the specified size
|
Returns the bold system font scaled to the specified size
|
||||||
@param aSize the size of the font (in px). 0 creates a static font
|
@param aSize the size of the font (in points)
|
||||||
in the current system font size. Negative creates a font
|
|
||||||
that dynamically tracks the current system font size.
|
|
||||||
@return the requested bold system font
|
@return the requested bold system font
|
||||||
*/
|
*/
|
||||||
+ (CPFont)boldSystemFontOfSize:(CGSize)aSize
|
+ (CPFont)boldSystemFontOfSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
return _CPSystemFont(aSize === 0 ? _CPFontSystemFontSize : aSize, YES);
|
return _CPCachedFont(_CPFontSystemFontFace, aSize, YES) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold italic:(BOOL)isItalic system:(BOOL)isSystem
|
/* FIXME Font Descriptor
|
||||||
|
@ignore
|
||||||
|
*/
|
||||||
|
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
|
_name = aName;
|
||||||
_size = aSize;
|
_size = aSize;
|
||||||
_ascender = 0;
|
_ascender = 0;
|
||||||
_descender = 0;
|
_descender = 0;
|
||||||
_lineHeight = 0;
|
_lineHeight = 0;
|
||||||
_isBold = isBold;
|
_isBold = isBold;
|
||||||
_isItalic = isItalic;
|
|
||||||
_isSystem = isSystem;
|
|
||||||
|
|
||||||
if (isSystem)
|
_cssString = _CPCreateCSSString(_name, _size, _isBold);
|
||||||
{
|
|
||||||
_name = aName;
|
_CPFonts[_cssString] = self;
|
||||||
_cssString = _CPFontCreateCSSString(_CPFontSystemFontFace, _size, _isBold, _isItalic);
|
|
||||||
_CPSystemFontCache[_CPSystemFontCacheKey(_size, _isBold)] = self;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_name = _CPFontNormalizedNames(aName);
|
|
||||||
_cssString = _CPFontCreateCSSString(_name, _size, _isBold, _isItalic);
|
|
||||||
_CPFontCache[_cssString] = self;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -336,26 +131,22 @@ following:
|
|||||||
*/
|
*/
|
||||||
- (float)ascender
|
- (float)ascender
|
||||||
{
|
{
|
||||||
var font = _isSystem ? _CPSystemFont(_size, _isBold) : self;
|
if (!_ascender)
|
||||||
|
[self _getMetrics];
|
||||||
|
|
||||||
if (!font._ascender)
|
return _ascender;
|
||||||
[font _getMetrics];
|
|
||||||
|
|
||||||
return font._ascender;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the bottom y coordinate (in CSS px), offset from the baseline, of the receiver's longest descender.
|
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 px below the baseline, descender will return –2.
|
Thus, if the longest descender extends 2 points below the baseline, descender will return –2.
|
||||||
*/
|
*/
|
||||||
- (float)descender
|
- (float)descender
|
||||||
{
|
{
|
||||||
var font = _isSystem ? _CPSystemFont(_size, _isBold) : self;
|
if (!_descender)
|
||||||
|
[self _getMetrics];
|
||||||
|
|
||||||
if (!font._descender)
|
return _descender;
|
||||||
[font _getMetrics];
|
|
||||||
|
|
||||||
return font._descender;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -365,12 +156,10 @@ following:
|
|||||||
*/
|
*/
|
||||||
- (float)defaultLineHeightForFont
|
- (float)defaultLineHeightForFont
|
||||||
{
|
{
|
||||||
var font = _isSystem ? _CPSystemFont(_size, _isBold) : self;
|
if (!_lineHeight)
|
||||||
|
[self _getMetrics];
|
||||||
|
|
||||||
if (!font._lineHeight)
|
return _lineHeight;
|
||||||
[font _getMetrics];
|
|
||||||
|
|
||||||
return font._lineHeight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -378,7 +167,7 @@ following:
|
|||||||
*/
|
*/
|
||||||
- (float)size
|
- (float)size
|
||||||
{
|
{
|
||||||
return _CPRealFontSize(_size);
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -386,9 +175,7 @@ following:
|
|||||||
*/
|
*/
|
||||||
- (CPString)cssString
|
- (CPString)cssString
|
||||||
{
|
{
|
||||||
var font = _isSystem ? _CPSystemFont(_size, _isBold) : self;
|
return _cssString;
|
||||||
|
|
||||||
return font._cssString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -396,30 +183,17 @@ following:
|
|||||||
*/
|
*/
|
||||||
- (CPString)familyName
|
- (CPString)familyName
|
||||||
{
|
{
|
||||||
if (_isSystem)
|
|
||||||
return _CPFontSystemFontFace;
|
|
||||||
|
|
||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isSystemSize
|
|
||||||
{
|
|
||||||
return _size <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)anObject
|
- (BOOL)isEqual:(id)anObject
|
||||||
{
|
{
|
||||||
return [anObject isKindOfClass:[CPFont class]] && [anObject cssString] === _cssString;
|
return [anObject isKindOfClass:[CPFont class]] && [anObject cssString] === [self cssString];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPString)description
|
- (CPString)description
|
||||||
{
|
{
|
||||||
return [CPString stringWithFormat:@"%@ %@", [super description], [self cssString]];
|
return [CPString stringWithFormat:@"%@ %@ %f pt.", [super description], [self familyName], [self size]];
|
||||||
}
|
|
||||||
|
|
||||||
- (id)copy
|
|
||||||
{
|
|
||||||
return [[CPFont alloc] _initWithName:_name size:_size bold:_isBold italic:_isItalic system:_isSystem];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_getMetrics
|
- (void)_getMetrics
|
||||||
@@ -433,11 +207,9 @@ following:
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPFontNameKey = @"CPFontNameKey",
|
var CPFontNameKey = @"CPFontNameKey",
|
||||||
CPFontSizeKey = @"CPFontSizeKey",
|
CPFontSizeKey = @"CPFontSizeKey",
|
||||||
CPFontIsBoldKey = @"CPFontIsBoldKey",
|
CPFontIsBoldKey = @"CPFontIsBoldKey";
|
||||||
CPFontIsItalicKey = @"CPFontIsItalicKey",
|
|
||||||
CPFontIsSystemKey = @"CPFontIsSystemKey";
|
|
||||||
|
|
||||||
@implementation CPFont (CPCoding)
|
@implementation CPFont (CPCoding)
|
||||||
|
|
||||||
@@ -448,13 +220,9 @@ var CPFontNameKey = @"CPFontNameKey",
|
|||||||
*/
|
*/
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
var fontName = [aCoder decodeObjectForKey:CPFontNameKey],
|
return [self _initWithName:[aCoder decodeObjectForKey:CPFontNameKey]
|
||||||
size = [aCoder decodeFloatForKey:CPFontSizeKey],
|
size:[aCoder decodeFloatForKey:CPFontSizeKey]
|
||||||
isBold = [aCoder decodeBoolForKey:CPFontIsBoldKey],
|
bold:[aCoder decodeBoolForKey:CPFontIsBoldKey]];
|
||||||
isItalic = [aCoder decodeBoolForKey:CPFontIsItalicKey],
|
|
||||||
isSystem = [aCoder decodeBoolForKey:CPFontIsSystemKey];
|
|
||||||
|
|
||||||
return [self _initWithName:fontName size:size bold:isBold italic:isItalic system:isSystem];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -466,51 +234,6 @@ var CPFontNameKey = @"CPFontNameKey",
|
|||||||
[aCoder encodeObject:_name forKey:CPFontNameKey];
|
[aCoder encodeObject:_name forKey:CPFontNameKey];
|
||||||
[aCoder encodeFloat:_size forKey:CPFontSizeKey];
|
[aCoder encodeFloat:_size forKey:CPFontSizeKey];
|
||||||
[aCoder encodeBool:_isBold forKey:CPFontIsBoldKey];
|
[aCoder encodeBool:_isBold forKey:CPFontIsBoldKey];
|
||||||
[aCoder encodeBool:_isItalic forKey:CPFontIsItalicKey];
|
|
||||||
[aCoder encodeBool:_isSystem forKey:CPFontIsSystemKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
// aName must be normalized
|
|
||||||
var _CPFontCreateCSSString = function(aName, aSize, isBold, isItalic)
|
|
||||||
{
|
|
||||||
var properties = (isItalic ? "italic " : "") + (isBold ? "bold " : "") + _CPRealFontSize(aSize) + "px ";
|
|
||||||
|
|
||||||
return properties + _CPFontConcatNameWithFallback(aName);
|
|
||||||
};
|
|
||||||
|
|
||||||
var _CPFontConcatNameWithFallback = function(aName)
|
|
||||||
{
|
|
||||||
var names = _CPFontNormalizedNameArray(aName),
|
|
||||||
fallbackFaces = _CPFontFallbackFaces.slice(0);
|
|
||||||
|
|
||||||
// Remove the fallback names used in the names passed in
|
|
||||||
for (var i = 0; i < names.length; ++i)
|
|
||||||
{
|
|
||||||
for (var j = 0; j < fallbackFaces.length; ++j)
|
|
||||||
{
|
|
||||||
if (names[i].toLowerCase() === fallbackFaces[j].toLowerCase())
|
|
||||||
{
|
|
||||||
fallbackFaces.splice(j, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (names[i].indexOf(" ") > 0)
|
|
||||||
names[i] = '"' + names[i] + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
return names.concat(fallbackFaces).join(", ");
|
|
||||||
};
|
|
||||||
|
|
||||||
var _CPFontNormalizedNameArray = function(aName)
|
|
||||||
{
|
|
||||||
var names = aName.split(",");
|
|
||||||
|
|
||||||
for (var i = 0; i < names.length; ++i)
|
|
||||||
names[i] = names[i].replace(_CPFontStripRegExp, "");
|
|
||||||
|
|
||||||
return names;
|
|
||||||
};
|
|
||||||
|
|||||||
+53
-181
@@ -24,21 +24,6 @@
|
|||||||
|
|
||||||
@import "CPFont.j"
|
@import "CPFont.j"
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
CPItalicFontMask = 1 << 0;
|
|
||||||
CPBoldFontMask = 1 << 1;
|
|
||||||
CPUnboldFontMask = 1 << 2;
|
|
||||||
CPNonStandardCharacterSetFontMask = 1 << 3;
|
|
||||||
CPNarrowFontMask = 1 << 4;
|
|
||||||
CPExpandedFontMask = 1 << 5;
|
|
||||||
CPCondensedFontMask = 1 << 6;
|
|
||||||
CPSmallCapsFontMask = 1 << 7;
|
|
||||||
CPPosterFontMask = 1 << 8;
|
|
||||||
CPCompressedFontMask = 1 << 9;
|
|
||||||
CPFixedPitchFontMask = 1 << 10;
|
|
||||||
CPUnitalicFontMask = 1 << 24;
|
|
||||||
|
|
||||||
|
|
||||||
var CPSharedFontManager = nil,
|
var CPSharedFontManager = nil,
|
||||||
CPFontManagerFactory = Nil;
|
CPFontManagerFactory = Nil;
|
||||||
@@ -48,17 +33,7 @@ var CPSharedFontManager = nil,
|
|||||||
*/
|
*/
|
||||||
@implementation CPFontManager : CPObject
|
@implementation CPFontManager : CPObject
|
||||||
{
|
{
|
||||||
CPArray _availableFonts;
|
CPArray _availableFonts;
|
||||||
|
|
||||||
id _target @accessors(property=target);
|
|
||||||
SEL _action @accessors(property=action);
|
|
||||||
|
|
||||||
id _delegate @accessors(property=delegate);
|
|
||||||
|
|
||||||
CPFont _selectedFont;
|
|
||||||
BOOL _multiple @accessors(getter=isMultiple, setter=setMultiple:);
|
|
||||||
|
|
||||||
CPDictionary _activeChange;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getting the Shared Font Manager
|
// Getting the Shared Font Manager
|
||||||
@@ -84,16 +59,6 @@ var CPSharedFontManager = nil,
|
|||||||
CPFontManagerFactory = aClass;
|
CPFontManagerFactory = aClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_action = @selector(changeFont:);
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns an array of the available fonts
|
Returns an array of the available fonts
|
||||||
*/
|
*/
|
||||||
@@ -101,9 +66,6 @@ var CPSharedFontManager = nil,
|
|||||||
{
|
{
|
||||||
if (!_availableFonts)
|
if (!_availableFonts)
|
||||||
{
|
{
|
||||||
_availableFonts = [];
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
_CPFontDetectSpan = document.createElement("span");
|
_CPFontDetectSpan = document.createElement("span");
|
||||||
_CPFontDetectSpan.fontSize = "24px";
|
_CPFontDetectSpan.fontSize = "24px";
|
||||||
_CPFontDetectSpan.appendChild(document.createTextNode("mmmmmmmmmml"));
|
_CPFontDetectSpan.appendChild(document.createTextNode("mmmmmmmmmml"));
|
||||||
@@ -113,18 +75,14 @@ var CPSharedFontManager = nil,
|
|||||||
div.appendChild(_CPFontDetectSpan);
|
div.appendChild(_CPFontDetectSpan);
|
||||||
document.getElementsByTagName("body")[0].appendChild(div);
|
document.getElementsByTagName("body")[0].appendChild(div);
|
||||||
|
|
||||||
_CPFontDetectReferenceFonts = _CPFontDetectPickTwoDifferentFonts(["monospace", "serif", "sans-serif", "cursive"]);
|
_CPFontDetectReferenceFonts = _CPFontDetectPickTwoDifferentFonts(["monospace", "serif", "sans-serif", "cursive"]);
|
||||||
|
|
||||||
for (var i = 0; i < _CPFontDetectAllFonts.length; i++)
|
_availableFonts = [];
|
||||||
{
|
for (var i = 0; i < _CPFontDetectAllFonts.length; i++) {
|
||||||
var available = _CPFontDetectFontAvailable(_CPFontDetectAllFonts[i]);
|
var available = _CPFontDetectFontAvailable(_CPFontDetectAllFonts[i]);
|
||||||
if (available)
|
if (available)
|
||||||
_availableFonts.push(_CPFontDetectAllFonts[i]);
|
_availableFonts.push(_CPFontDetectAllFonts[i]);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
// If there's no font detection, just assume all fonts are available.
|
|
||||||
_availableFonts = _CPFontDetectAllFonts;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return _availableFonts;
|
return _availableFonts;
|
||||||
}
|
}
|
||||||
@@ -138,165 +96,79 @@ var CPSharedFontManager = nil,
|
|||||||
return _CPFontDetectFontAvailable(aFontName);
|
return _CPFontDetectFontAvailable(aFontName);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSelectedFont:(CPFont)aFont isMultiple:(BOOL)aFlag
|
|
||||||
{
|
|
||||||
_selectedFont = aFont;
|
|
||||||
_multiple = aFlag;
|
|
||||||
|
|
||||||
// TODO Notify CPFontPanel when it exists.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFont)selectedFont
|
|
||||||
{
|
|
||||||
return _selectedFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)weightOfFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
// TODO Weight 5 is a normal of book weight and 9 and above is bold, but it would be nice to be more
|
|
||||||
// precise than that.
|
|
||||||
return [aFont isBold] ? 9 : 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFontTraitMask)traitsOfFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
return ([aFont isBold] ? CPBoldFontMask : 0) | ([aFont isItalic] ? CPItalicFontMask : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFont)convertFont:(CPFont)aFont
|
|
||||||
{
|
|
||||||
if (!_activeChange)
|
|
||||||
return aFont;
|
|
||||||
|
|
||||||
var addTraits = [_activeChange valueForKey:@"addTraits"];
|
|
||||||
|
|
||||||
if (addTraits)
|
|
||||||
aFont = [self convertFont:aFont toHaveTrait:addTraits];
|
|
||||||
|
|
||||||
return aFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFont)convertFont:(CPFont)aFont toHaveTrait:(CPFontTraitMask)addTraits
|
|
||||||
{
|
|
||||||
if (!aFont)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
var shouldBeBold = ([aFont isBold] || (addTraits & CPBoldFontMask)) && !(addTraits & CPUnboldFontMask),
|
|
||||||
shouldBeItalic = ([aFont isItalic] || (addTraits & CPItalicFontMask)) && !(addTraits & CPUnitalicFontMask),
|
|
||||||
shouldBeSize = [aFont size];
|
|
||||||
|
|
||||||
// XXX On the current platform there will always be a bold/italic version of each font, but still leave
|
|
||||||
// || aFont in here for future platforms.
|
|
||||||
aFont = [CPFont _fontWithName:[aFont familyName] size:shouldBeSize bold:shouldBeBold italic:shouldBeItalic] || aFont;
|
|
||||||
|
|
||||||
return aFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFont)convertFont:(CPFont)aFont toFace:(CPString)aTypeface
|
|
||||||
{
|
|
||||||
if (!aFont)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
var shouldBeBold = [aFont isBold],
|
|
||||||
shouldBeItalic = [aFont isItalic],
|
|
||||||
shouldBeSize = [aFont size];
|
|
||||||
|
|
||||||
aFont = [CPFont _fontWithName:aTypeface size:shouldBeSize bold:shouldBeBold italic:shouldBeItalic] || aFont;
|
|
||||||
|
|
||||||
return aFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (@action)addFontTrait:(id)sender
|
|
||||||
{
|
|
||||||
var tag = [sender tag];
|
|
||||||
_activeChange = tag === nil ? @{} : @{ @"addTraits": tag };
|
|
||||||
|
|
||||||
[self sendAction];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)sendAction
|
|
||||||
{
|
|
||||||
return [CPApp sendAction:_action to:_target from:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var _CPFontDetectSpan,
|
var _CPFontDetectSpan,
|
||||||
_CPFontDetectReferenceFonts,
|
_CPFontDetectReferenceFonts,
|
||||||
_CPFontDetectAllFonts = [
|
_CPFontDetectAllFonts = [
|
||||||
/* "04b_21", "A Charming Font", "Abadi MT Condensed", "Abadi MT Condensed Extra Bold", "Abadi MT Condensed Light", "Academy Engraved LET", "Agency FB", "Alba", "Alba Matter", "Alba Super", "Algerian",*/
|
/* "04b_21","A Charming Font","Abadi MT Condensed","Abadi MT Condensed Extra Bold","Abadi MT Condensed Light","Academy Engraved LET","Agency FB","Alba","Alba Matter","Alba Super","Algerian",*/
|
||||||
"American Typewriter",
|
"American Typewriter",
|
||||||
/* "Andale Mono", "Andale Mono IPA", "Andy", */
|
/* "Andale Mono","Andale Mono IPA","Andy", */
|
||||||
"Apple Chancery", "Arial", "Arial Black", "Arial Narrow", "Arial Rounded MT Bold", "Arial Unicode MS",
|
"Apple Chancery","Arial","Arial Black","Arial Narrow","Arial Rounded MT Bold","Arial Unicode MS",
|
||||||
/* "Avant Garde", "Avantgarde", "Baby Kruffy", "Base 02", "Baskerville", "Baskerville Old Face", "Bauhaus 93", "Beesknees ITC", "Bell MT", "Berlin Sans FB", "Berlin Sans FB Demi", "Bernard MT Condensed", "Bickley Script",*/
|
/* "Avant Garde","Avantgarde","Baby Kruffy","Base 02","Baskerville","Baskerville Old Face","Bauhaus 93","Beesknees ITC","Bell MT","Berlin Sans FB","Berlin Sans FB Demi","Bernard MT Condensed","Bickley Script",*/
|
||||||
"Big Caslon", "Bitstream Vera Sans", "Bitstream Vera Sans Mono", "Bitstream Vera Serif",
|
"Big Caslon","Bitstream Vera Sans","Bitstream Vera Sans Mono","Bitstream Vera Serif",
|
||||||
/* "Blackadder ITC", "Blackletter686 BT", "Bodoni MT", "Bodoni MT Black", "Bodoni MT Condensed", "Bodoni MT Poster Compressed", "Book Antiqua", "Bookman", "Bookman Old Style", "Bradley Hand ITC", "Braggadocio", "Britannic Bold", "Broadway", "Broadway BT",*/
|
/* "Blackadder ITC","Blackletter686 BT","Bodoni MT","Bodoni MT Black","Bodoni MT Condensed","Bodoni MT Poster Compressed","Book Antiqua","Bookman","Bookman Old Style","Bradley Hand ITC","Braggadocio","Britannic Bold","Broadway","Broadway BT",*/
|
||||||
"Brush Script MT",
|
"Brush Script MT",
|
||||||
/* "BudHand", "CAMPBELL", "Calibri", "Californian FB", "Calisto MT", "Calligraph421 BT",*/
|
/* "BudHand","CAMPBELL","Calibri","Californian FB","Calisto MT","Calligraph421 BT",*/
|
||||||
"Cambria",
|
"Cambria",
|
||||||
/* "Candara", "Capitals",*/
|
/* "Candara","Capitals",*/
|
||||||
"Caslon", "Castellar", "Cataneo BT", "Centaur", "Century Gothic", "Century Schoolbook", "Century Schoolbook L",
|
"Caslon","Castellar","Cataneo BT","Centaur","Century Gothic","Century Schoolbook","Century Schoolbook L",
|
||||||
/* "Champignon", "Charcoal", "Charter", "Charter BT", "Chicago", "Chick", "Chiller", "ClearlyU", "Colonna MT",*/
|
/* "Champignon","Charcoal","Charter","Charter BT","Chicago","Chick","Chiller","ClearlyU","Colonna MT",*/
|
||||||
"Comic Sans", "Comic Sans MS", "Consolas", "Constantia", "Cooper Black", "Copperplate", "Copperplate Gothic Bold", "Copperplate Gothic Light", "Corbel", "Courier", "Courier New",
|
"Comic Sans", "Comic Sans MS","Consolas","Constantia","Cooper Black","Copperplate","Copperplate Gothic Bold","Copperplate Gothic Light","Corbel","Courier","Courier New",
|
||||||
/* "Croobie", "Curlz MT", "Desdemona", "Didot", "DomBold BT", "Edwardian Script ITC", "Engravers MT", "Eras Bold ITC", "Eras Demi ITC", "Eras Light ITC", "Eras Medium ITC", "Eurostile", "FIRSTHOME", "Fat", "Felix Titling", "Fine Hand", "Fixed", "Footlight MT Light", "Forte", "Franklin Gothic Book", "Franklin Gothic Demi", "Franklin Gothic Demi Cond", "Franklin Gothic Heavy", "Franklin Gothic Medium", "Franklin Gothic Medium Cond", "Freestyle Script", "French Script MT", "Freshbot", "Frosty",*/
|
/* "Croobie","Curlz MT","Desdemona","Didot","DomBold BT","Edwardian Script ITC","Engravers MT","Eras Bold ITC","Eras Demi ITC","Eras Light ITC","Eras Medium ITC","Eurostile","FIRSTHOME","Fat","Felix Titling","Fine Hand","Fixed","Footlight MT Light","Forte","Franklin Gothic Book","Franklin Gothic Demi","Franklin Gothic Demi Cond","Franklin Gothic Heavy","Franklin Gothic Medium","Franklin Gothic Medium Cond","Freestyle Script","French Script MT","Freshbot","Frosty",*/
|
||||||
"Futura",
|
"Futura",
|
||||||
/* "GENUINE", "Gadget", "Garamond",*/
|
/* "GENUINE","Gadget","Garamond",*/
|
||||||
"Geneva", "Georgia", "Georgia Ref", "Geeza Pro", "Gigi", "Gill Sans", "Gill Sans MT", "Gill Sans MT Condensed", "Gill Sans MT Ext Condensed Bold", "Gill Sans Ultra Bold", "Gill Sans Ultra Bold Condensed",
|
"Geneva","Georgia","Georgia Ref", "Geeza Pro", "Gigi","Gill Sans","Gill Sans MT","Gill Sans MT Condensed","Gill Sans MT Ext Condensed Bold","Gill Sans Ultra Bold","Gill Sans Ultra Bold Condensed",
|
||||||
/* "GlooGun", "Gloucester MT Extra Condensed", "Goudy Old Style", "Goudy Stout", "Haettenschweiler", "Harlow Solid Italic", "Harrington",*/
|
/* "GlooGun","Gloucester MT Extra Condensed","Goudy Old Style","Goudy Stout","Haettenschweiler","Harlow Solid Italic","Harrington",*/
|
||||||
"Helvetica", "Helvetica Narrow", "Helvetica Neue", "Herculanum", "High Tower Text", "Highlight LET", "Hoefler Text", "Impact", "Imprint MT Shadow",
|
"Helvetica","Helvetica Narrow","Helvetica Neue","Herculanum","High Tower Text","Highlight LET","Hoefler Text","Impact","Imprint MT Shadow",
|
||||||
/* "Informal Roman", "Jenkins v2.0", "John Handy LET", "Jokerman", "Jokerman LET", "Jokewood", "Juice ITC", "Kabel Ult BT", "Kartika", "Kino MT", "Kristen ITC", "Kunstler Script", "La Bamba LET", */
|
/* "Informal Roman","Jenkins v2.0","John Handy LET","Jokerman","Jokerman LET","Jokewood","Juice ITC","Kabel Ult BT","Kartika","Kino MT","Kristen ITC","Kunstler Script","La Bamba LET", */
|
||||||
"Lucida", "Lucida Bright", "Lucida Calligraphy", "Lucida Console", "Lucida Fax", "Lucida Grande", "Lucida Handwriting", "Lucida Sans", "Lucida Sans Typewriter", "Lucida Sans Unicode",
|
"Lucida","Lucida Bright","Lucida Calligraphy","Lucida Console","Lucida Fax","Lucida Grande","Lucida Handwriting","Lucida Sans","Lucida Sans Typewriter","Lucida Sans Unicode",
|
||||||
/* "Luxi Mono", "Luxi Sans", "Luxi Serif", "MARKETPRO", "MS Reference Sans Serif", "MS Reference Serif", "Magneto", "Maiandra GD", */
|
/* "Luxi Mono","Luxi Sans","Luxi Serif","MARKETPRO","MS Reference Sans Serif","MS Reference Serif","Magneto","Maiandra GD", */
|
||||||
"Marker Felt",
|
"Marker Felt",
|
||||||
/* "Matisse ITC", "Matura MT Script Capitals", "Mead Bold", "Mekanik LET", "Mercurius Script MT Bold", */
|
/* "Matisse ITC","Matura MT Script Capitals","Mead Bold","Mekanik LET","Mercurius Script MT Bold", */
|
||||||
"Microsoft Sans Serif", "Milano LET", "Minion Web", "MisterEarl BT", "Mistral", "Monaco", "Monotype Corsiva", "Monotype.com", "New Century Schoolbook", "New York", "News Gothic MT",
|
"Microsoft Sans Serif","Milano LET","Minion Web","MisterEarl BT","Mistral","Monaco","Monotype Corsiva","Monotype.com","New Century Schoolbook","New York","News Gothic MT",
|
||||||
/* "Niagara Engraved", "Niagara Solid", "Nimbus Mono L", "Nimbus Roman No9 L", "OCR A Extended", "OCRB", "Odessa LET", "Old English Text MT", "OldDreadfulNo7 BT", "One Stroke Script LET", "Onyx", "Optima", "Orange LET", "Palace Script MT", "Palatino", "Palatino Linotype", */
|
/* "Niagara Engraved","Niagara Solid","Nimbus Mono L","Nimbus Roman No9 L","OCR A Extended","OCRB","Odessa LET","Old English Text MT","OldDreadfulNo7 BT","One Stroke Script LET","Onyx","Optima","Orange LET","Palace Script MT","Palatino","Palatino Linotype", */
|
||||||
"Papyrus",
|
"Papyrus",
|
||||||
/* "ParkAvenue BT", "Pepita MT", "Perpetua", "Perpetua Titling MT", "Placard Condensed", "Playbill", "Poornut", "Pristina", "Pump Demi Bold LET", "Pussycat", "Quixley LET", "Rage Italic", "Rage Italic LET", "Ravie", "Rockwell", "Rockwell Condensed", "Rockwell Extra Bold", "Ruach LET", "Runic MT Condensed", "Sand", "Script MT Bold", "Scruff LET", "Segoe UI", "Showcard Gothic", "Skia", "Smudger LET", "Snap ITC", "Square721 BT", "Staccato222 BT", "Stencil", "Sylfaen", */
|
/* "ParkAvenue BT","Pepita MT","Perpetua","Perpetua Titling MT","Placard Condensed","Playbill","Poornut","Pristina","Pump Demi Bold LET","Pussycat","Quixley LET","Rage Italic","Rage Italic LET","Ravie","Rockwell","Rockwell Condensed","Rockwell Extra Bold","Ruach LET","Runic MT Condensed","Sand","Script MT Bold","Scruff LET","Segoe UI","Showcard Gothic","Skia","Smudger LET","Snap ITC","Square721 BT","Staccato222 BT","Stencil","Sylfaen", */
|
||||||
"Tahoma", "Techno", "Tempus Sans ITC", "Terminal", "Textile", "Times", "Times New Roman", "Tiranti Solid LET", "Trebuchet MS",
|
"Tahoma","Techno","Tempus Sans ITC","Terminal","Textile","Times","Times New Roman","Tiranti Solid LET","Trebuchet MS",
|
||||||
/* "Tw Cen MT", "Tw Cen MT Condensed", "Tw Cen MT Condensed Extra Bold", "URW Antiqua T", "URW Bookman L", "URW Chancery L", "URW Gothic L", "URW Palladio L", "Univers", "University Roman LET", "Utopia", */
|
/* "Tw Cen MT","Tw Cen MT Condensed","Tw Cen MT Condensed Extra Bold","URW Antiqua T","URW Bookman L","URW Chancery L","URW Gothic L","URW Palladio L","Univers","University Roman LET","Utopia", */
|
||||||
"Verdana", "Verdana Ref", /* "Victorian LET", "Viner Hand ITC", "Vivaldi", "Vladimir Script", "Vrinda", "Weltron Urban", "Westwood LET", "Wide Latin", "Zapf Chancery", */
|
"Verdana","Verdana Ref", /* "Victorian LET","Viner Hand ITC","Vivaldi","Vladimir Script","Vrinda","Weltron Urban","Westwood LET","Wide Latin","Zapf Chancery", */
|
||||||
"Zapfino"];
|
"Zapfino"];
|
||||||
|
|
||||||
// Compare against the reference fonts. Return true if it produces a different size than at least one of them.
|
// Compare against the reference fonts. Return true if it produces a different size than at least one of them.
|
||||||
var _CPFontDetectFontAvailable = function(font)
|
var _CPFontDetectFontAvailable = function(font) {
|
||||||
{
|
for (var i = 0; i < _CPFontDetectReferenceFonts.length; i++)
|
||||||
for (var i = 0; i < _CPFontDetectReferenceFonts.length; i++)
|
if (_CPFontDetectCompareFonts(_CPFontDetectReferenceFonts[i], font))
|
||||||
if (_CPFontDetectCompareFonts(_CPFontDetectReferenceFonts[i], font))
|
return true;
|
||||||
return true;
|
return false;
|
||||||
return false;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
var _CPFontDetectCache = {};
|
var _CPFontDetectCache = {};
|
||||||
|
|
||||||
// Compares two given fonts. Returns true if they produce different sizes (i.e. fontA didn't fallback to fontB)
|
// Compares two given fonts. Returns true if they produce different sizes (i.e. fontA didn't fallback to fontB)
|
||||||
var _CPFontDetectCompareFonts = function(fontA, fontB)
|
var _CPFontDetectCompareFonts = function(fontA, fontB) {
|
||||||
{
|
|
||||||
var a;
|
var a;
|
||||||
if (_CPFontDetectCache[fontA])
|
if (_CPFontDetectCache[fontA]) {
|
||||||
a = _CPFontDetectCache[fontA];
|
a = _CPFontDetectCache[fontA];
|
||||||
|
} else {
|
||||||
else
|
_CPFontDetectSpan.style.fontFamily = '"' + fontA + '"';
|
||||||
{
|
|
||||||
_CPFontDetectSpan.style.fontFamily = '"' + fontA + '"';
|
|
||||||
_CPFontDetectCache[fontA] = a = { w: _CPFontDetectSpan.offsetWidth, h: _CPFontDetectSpan.offsetHeight };
|
_CPFontDetectCache[fontA] = a = { w: _CPFontDetectSpan.offsetWidth, h: _CPFontDetectSpan.offsetHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
_CPFontDetectSpan.style.fontFamily = '"' + fontB + '", "' + fontA + '"';
|
_CPFontDetectSpan.style.fontFamily= '"' + fontB + '", "' + fontA + '"';
|
||||||
var bWidth = _CPFontDetectSpan.offsetWidth,
|
var bWidth = _CPFontDetectSpan.offsetWidth;
|
||||||
bHeight = _CPFontDetectSpan.offsetHeight;
|
var bHeight = _CPFontDetectSpan.offsetHeight;
|
||||||
|
|
||||||
return (a.w != bWidth || a.h != bHeight);
|
return (a.w != bWidth || a.h != bHeight);
|
||||||
};
|
}
|
||||||
|
|
||||||
// Test the candidate fonts pairwise until we find two that are different. Otherwise return the first.
|
// Test the candidate fonts pairwise until we find two that are different. Otherwise return the first.
|
||||||
var _CPFontDetectPickTwoDifferentFonts = function(candidates)
|
var _CPFontDetectPickTwoDifferentFonts = function(candidates) {
|
||||||
{
|
for (var i = 0; i < candidates.length; i++)
|
||||||
for (var i = 0; i < candidates.length; i++)
|
for (var j = 0; j < i; j++)
|
||||||
for (var j = 0; j < i; j++)
|
if (_CPFontDetectCompareFonts(candidates[i], candidates[j]))
|
||||||
if (_CPFontDetectCompareFonts(candidates[i], candidates[j]))
|
return [candidates[i], candidates[j]];
|
||||||
return [candidates[i], candidates[j]];
|
return [candidates[0]];
|
||||||
return [candidates[0]];
|
}
|
||||||
};
|
|
||||||
|
|
||||||
[CPFontManager setFontManagerFactory:[CPFontManager class]];
|
[CPFontManager setFontManagerFactory:[CPFontManager class]];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* CPGeometry.j
|
* CPGeometry.j
|
||||||
* Foundation
|
* AppKit
|
||||||
*
|
*
|
||||||
* Created by Francisco Tolmasky.
|
* Created by Francisco Tolmasky.
|
||||||
* Copyright 2008, 280 North, Inc.
|
* Copyright 2008, 280 North, Inc.
|
||||||
@@ -20,108 +20,34 @@
|
|||||||
* 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 "CGGeometry.j"
|
||||||
|
|
||||||
|
|
||||||
CPMinXEdge = 0;
|
CPMinXEdge = 0;
|
||||||
CPMinYEdge = 1;
|
CPMinYEdge = 1;
|
||||||
CPMaxXEdge = 2;
|
CPMaxXEdge = 2;
|
||||||
CPMaxYEdge = 3;
|
CPMaxYEdge = 3;
|
||||||
|
|
||||||
/*!
|
// FIXME: the rest!
|
||||||
Makes a CGPoint object out of two numbers provided as arguments
|
|
||||||
@group CGPoint
|
|
||||||
@param x the x-coordinate of the CGPoint
|
|
||||||
@param y the y-coordinate of the CGPoint
|
|
||||||
@return CGPoint a CGPoint with an X and Y coordinate equal to the function arguments
|
|
||||||
*/
|
|
||||||
CPMakePoint = CGPointMake;
|
CPMakePoint = CGPointMake;
|
||||||
|
|
||||||
/*!
|
|
||||||
Creates and returns a new CGSize object from the provided dimensions.
|
|
||||||
@group CGSize
|
|
||||||
@param width the width for the new CGSize
|
|
||||||
@param height the height for the new CGSize
|
|
||||||
@return CGSize the new CGSize
|
|
||||||
*/
|
|
||||||
CPMakeSize = CGSizeMake;
|
CPMakeSize = CGSizeMake;
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a CGRect made of the specified arguments
|
|
||||||
@group CGRect
|
|
||||||
@param x the x-coordinate of the rectangle's origin
|
|
||||||
@param y the y-coordinate of the rectangle's origin
|
|
||||||
@param width the width of the new rectangle
|
|
||||||
@param height the height of the new rectangle
|
|
||||||
@return CGRect the new rectangle
|
|
||||||
*/
|
|
||||||
CPMakeRect = CGRectMake;
|
CPMakeRect = CGRectMake;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a copy of a specified point and returns the copy
|
@addtogroup appkit
|
||||||
@group CGPoint
|
@{
|
||||||
@param the point to be copied
|
|
||||||
@return CGPoint the copy of the provided CGPoint
|
|
||||||
*/
|
*/
|
||||||
CPPointCreateCopy = CGPointMakeCopy;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Tests whether the two CGPoints are equal to each other by comparing their
|
Creates a copy of a specified point and returns the copy
|
||||||
\c x and \c y members.
|
@group CGPoint
|
||||||
@group @CGPoint
|
@param the point to be copied
|
||||||
@param lhsPoint the first CGPoint to check
|
@return CGPoint the copy of the provided CGPoint
|
||||||
@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.
|
function CPPointCreateCopy(aPoint)
|
||||||
*/
|
|
||||||
CPPointEqualToPoint = CGPointEqualToPoint;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Tests whether the CGPoint is contained by the CGRect.
|
|
||||||
@group CGPoint
|
|
||||||
@param aPoint the CGPoint to check
|
|
||||||
@param aRect the CGRect to check
|
|
||||||
@return BOOL \c YES if the rect contains the point.
|
|
||||||
*/
|
|
||||||
CPPointInRect = function(aPoint, aRect)
|
|
||||||
{
|
{
|
||||||
return CGRectContainsPoint(aRect, aPoint)
|
return { x: aPoint.x, y: aPoint.y };
|
||||||
};
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Test whether the two CGRects have the same origin and size
|
|
||||||
@group CGRect
|
|
||||||
@param lhsRect the first 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.
|
|
||||||
*/
|
|
||||||
CPRectEqualToRect = CGRectEqualToRect;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c YES if the CGRect has no area.
|
|
||||||
The test is performed by checking if the width and height are both zero.
|
|
||||||
@group CGRect
|
|
||||||
@param aRect the CGRect to test
|
|
||||||
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
|
||||||
*/
|
|
||||||
CPRectIsEmpty = CGRectIsEmpty;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a \c BOOL indicating whether CGRect \c possibleOuter
|
|
||||||
contains CGRect \c possibleInner.
|
|
||||||
@group CGRect
|
|
||||||
@param possibleOuter the CGRect to test if \c possibleInner is inside of
|
|
||||||
@param possibleInner the CGRect to test if it fits inside \c possibleOuter.
|
|
||||||
@return BOOL \c YES if \c possibleInner fits inside \c possibleOuter.
|
|
||||||
*/
|
|
||||||
CPRectContainsRect = CGRectContainsRect;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the intersection of the two provided rectangles as a new rectangle
|
|
||||||
@group CGRect
|
|
||||||
@param lhsRect the first rectangle used for calculation
|
|
||||||
@param rhsRect the second rectangle used for calculation
|
|
||||||
@return CGRect the intersection of the two rectangles
|
|
||||||
*/
|
|
||||||
CPRectIntersection = CGRectIntersection;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Makes a CGPoint object out of two numbers provided as arguments
|
Makes a CGPoint object out of two numbers provided as arguments
|
||||||
@@ -129,8 +55,11 @@ CPRectIntersection = CGRectIntersection;
|
|||||||
@param x the x-coordinate of the CGPoint
|
@param x the x-coordinate of the CGPoint
|
||||||
@param y the y-coordinate of the CGPoint
|
@param y the y-coordinate of the CGPoint
|
||||||
@return CGPoint a CGPoint with an X and Y coordinate equal to the function arguments
|
@return CGPoint a CGPoint with an X and Y coordinate equal to the function arguments
|
||||||
*/
|
*/
|
||||||
CPPointMake = CGPointMake;
|
function CPPointMake(x, y)
|
||||||
|
{
|
||||||
|
return { x: x, y: 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 \c aRect less the \c dX/dY insets specified.
|
||||||
@@ -139,7 +68,11 @@ CPPointMake = CGPointMake;
|
|||||||
@group CGRect
|
@group CGRect
|
||||||
@return CGRect a rectangle like \c aRect with an inset
|
@return CGRect a rectangle like \c aRect with an inset
|
||||||
*/
|
*/
|
||||||
CPRectInset = CGRectInset;
|
function CPRectInset(aRect, dX, dY)
|
||||||
|
{
|
||||||
|
return CPRectMake( aRect.origin.x + dX, aRect.origin.y + dY,
|
||||||
|
aRect.size.width - 2 * dX, aRect.size.height - 2*dY);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@@ -147,7 +80,31 @@ CPRectInset = CGRectInset;
|
|||||||
@return void
|
@return void
|
||||||
@deprecated
|
@deprecated
|
||||||
*/
|
*/
|
||||||
CPRectIntegral = CGRectIntegral;
|
function CPRectIntegral(aRect)
|
||||||
|
{
|
||||||
|
// FIXME!!!
|
||||||
|
alert("CPRectIntegral unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the intersection of the two provided rectangles as a new rectangle
|
||||||
|
@group CGRect
|
||||||
|
@param lhsRect the first rectangle used for calculation
|
||||||
|
@param rhsRect the second rectangle used for calculation
|
||||||
|
@return CGRect the intersection of the two rectangles
|
||||||
|
*/
|
||||||
|
function CPRectIntersection(lhsRect, rhsRect)
|
||||||
|
{
|
||||||
|
var intersection = CPRectMake(
|
||||||
|
Math.max(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)),
|
||||||
|
Math.max(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
|
||||||
|
0, 0);
|
||||||
|
|
||||||
|
intersection.size.width = Math.min(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)) - CPRectGetMinX(intersection);
|
||||||
|
intersection.size.height = Math.min(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect)) - CPRectGetMinY(intersection);
|
||||||
|
|
||||||
|
return CPRectIsEmpty(intersection) ? CPRectMakeZero() : intersection;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a copy of the provided rectangle
|
Creates a copy of the provided rectangle
|
||||||
@@ -155,7 +112,10 @@ CPRectIntegral = CGRectIntegral;
|
|||||||
@param aRect the CGRect that will be copied
|
@param aRect the CGRect that will be copied
|
||||||
@return CGRect the rectangle copy
|
@return CGRect the rectangle copy
|
||||||
*/
|
*/
|
||||||
CPRectCreateCopy = CGRectCreateCopy;
|
function CPRectCreateCopy(aRect)
|
||||||
|
{
|
||||||
|
return { origin: CPPointCreateCopy(aRect.origin), size: CPSizeCreateCopy(aRect.size) };
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a CGRect made of the specified arguments
|
Returns a CGRect made of the specified arguments
|
||||||
@@ -166,7 +126,10 @@ CPRectCreateCopy = CGRectCreateCopy;
|
|||||||
@param height the height of the new rectangle
|
@param height the height of the new rectangle
|
||||||
@return CGRect the new rectangle
|
@return CGRect the new rectangle
|
||||||
*/
|
*/
|
||||||
CPRectMake = CGRectMake;
|
function CPRectMake(x, y, width, height)
|
||||||
|
{
|
||||||
|
return { origin: CPPointMake(x, y), size: CPSizeMake(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 \c dX and \c dY.
|
||||||
@@ -176,14 +139,36 @@ CPRectMake = CGRectMake;
|
|||||||
@param dY the amount added to the y-size of the new rectangle
|
@param dY the amount added to the y-size of the new rectangle
|
||||||
@return CGRect the new rectangle with modified size
|
@return CGRect the new rectangle with modified size
|
||||||
*/
|
*/
|
||||||
CPRectOffset = CGRectOffset;
|
function CPRectOffset(aRect, dX, dY)
|
||||||
|
{
|
||||||
|
return CPRectMake(aRect.origin.x + dX, aRect.origin.y + dY, aRect.size.width, aRect.size.height);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect a CGRect
|
@param aRect a CGRect
|
||||||
@return CGRect
|
@return CGRect
|
||||||
*/
|
*/
|
||||||
CPRectStandardize = CGRectStandardize;
|
function CPRectStandardize(aRect)
|
||||||
|
{
|
||||||
|
var width = CPRectGetWidth(aRect),
|
||||||
|
height = CPRectGetHeight(aRect),
|
||||||
|
standardized = CPRectCreateCopy(aRect);
|
||||||
|
|
||||||
|
if (width < 0.0)
|
||||||
|
{
|
||||||
|
standardized.origin.x += width;
|
||||||
|
standardized.size.width = -width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height < 0.0)
|
||||||
|
{
|
||||||
|
standardized.origin.y += height;
|
||||||
|
standardized.size.height = -height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return standardized;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the smallest rectangle that can contain the two argument \c CGRects.
|
Returns the smallest rectangle that can contain the two argument \c CGRects.
|
||||||
@@ -192,7 +177,15 @@ CPRectStandardize = CGRectStandardize;
|
|||||||
@param rhsRect the second CGRect to use for the union calculation
|
@param rhsRect the second CGRect to use for the union calculation
|
||||||
@return CGRect the union rectangle
|
@return CGRect the union rectangle
|
||||||
*/
|
*/
|
||||||
CPRectUnion = CGRectUnion;
|
function CPRectUnion(lhsRect, rhsRect)
|
||||||
|
{
|
||||||
|
var minX = Math.min(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)),
|
||||||
|
minY = Math.min(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
|
||||||
|
maxX = Math.max(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)),
|
||||||
|
maxY = Math.max(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect));
|
||||||
|
|
||||||
|
return CPRectMake(minX, minY, maxX - minX, maxY - minY);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates and returns a copy of the provided CGSize
|
Creates and returns a copy of the provided CGSize
|
||||||
@@ -200,7 +193,10 @@ CPRectUnion = CGRectUnion;
|
|||||||
@param aSize the CGSize to copy
|
@param aSize the CGSize to copy
|
||||||
@return CGSize the copy of the CGSize
|
@return CGSize the copy of the CGSize
|
||||||
*/
|
*/
|
||||||
CPSizeCreateCopy = CGSizeCreateCopy;
|
function CPSizeCreateCopy(aSize)
|
||||||
|
{
|
||||||
|
return { width: aSize.width, height: aSize.height };
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates and returns a new CGSize object from the provided dimensions.
|
Creates and returns a new CGSize object from the provided dimensions.
|
||||||
@@ -209,7 +205,10 @@ CPSizeCreateCopy = CGSizeCreateCopy;
|
|||||||
@param height the height for the new CGSize
|
@param height the height for the new CGSize
|
||||||
@return CGSize the new CGSize
|
@return CGSize the new CGSize
|
||||||
*/
|
*/
|
||||||
CPSizeMake = CGSizeMake;
|
function CPSizeMake(width, height)
|
||||||
|
{
|
||||||
|
return { width: width, height: height };
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the CGRect, \c aRect, contains
|
Returns \c YES if the CGRect, \c aRect, contains
|
||||||
@@ -219,63 +218,132 @@ CPSizeMake = CGSizeMake;
|
|||||||
@group CGRect
|
@group CGRect
|
||||||
@return BOOL \c YES if the rectangle contains the point, \c NO otherwise.
|
@return BOOL \c YES if the rectangle contains the point, \c NO otherwise.
|
||||||
*/
|
*/
|
||||||
CPRectContainsPoint = CGRectContainsPoint;
|
function CPRectContainsPoint(aRect, aPoint)
|
||||||
|
{
|
||||||
|
return aPoint.x >= CPRectGetMinX(aRect) &&
|
||||||
|
aPoint.y >= CPRectGetMinY(aRect) &&
|
||||||
|
aPoint.x < CPRectGetMaxX(aRect) &&
|
||||||
|
aPoint.y < CPRectGetMaxY(aRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns a \c BOOL indicating whether CGRect \c possibleOuter
|
||||||
|
contains CGRect \c possibleInner.
|
||||||
|
@group CGRect
|
||||||
|
@param possibleOuter the CGRect to test if \c possibleInner is inside of
|
||||||
|
@param possibleInner the CGRect to test if it fits inside \c possibleOuter.
|
||||||
|
@return BOOL \c YES if \c possibleInner fits inside \c possibleOuter.
|
||||||
|
*/
|
||||||
|
function CPRectContainsRect(possibleOuter, possibleInner)
|
||||||
|
{
|
||||||
|
return CGRectContainsRect(possibleOuter, possibleInner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Tests whether the two CGPoints are equal to each other by comparing their
|
||||||
|
\c x and \c y members.
|
||||||
|
@group @CGPoint
|
||||||
|
@param lhsPoint the first 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.
|
||||||
|
*/
|
||||||
|
function CPPointEqualToPoint(lhsPoint, rhsPoint)
|
||||||
|
{
|
||||||
|
return lhsPoint.x == rhsPoint.x && lhsPoint.y == rhsPoint.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Test whether the two CGRects have the same origin and size
|
||||||
|
@group CGRect
|
||||||
|
@param lhsRect the first 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.
|
||||||
|
*/
|
||||||
|
function CPRectEqualToRect(lhsRect, rhsRect)
|
||||||
|
{
|
||||||
|
return CPPointEqualToPoint(lhsRect.origin, rhsRect.origin) &&
|
||||||
|
CPSizeEqualToSize(lhsRect.size, rhsRect.size);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect a CGRect
|
@param aRect a CGRect
|
||||||
@return int
|
@return int
|
||||||
*/
|
*/
|
||||||
CPRectGetHeight = CGRectGetHeight;
|
function CPRectGetHeight(aRect)
|
||||||
|
{
|
||||||
|
return aRect.size.height;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect a CGRect
|
@param aRect a CGRect
|
||||||
@return int
|
@return int
|
||||||
*/
|
*/
|
||||||
CPRectGetMaxX = CGRectGetMaxX;
|
function CPRectGetMaxX(aRect)
|
||||||
|
{
|
||||||
|
return aRect.origin.x + aRect.size.width;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect a CGRect
|
@param aRect a CGRect
|
||||||
@return int
|
@return int
|
||||||
*/
|
*/
|
||||||
CPRectGetMaxY = CGRectGetMaxY;
|
function CPRectGetMaxY(aRect)
|
||||||
|
{
|
||||||
|
return aRect.origin.y + aRect.size.height;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect a CGRect
|
@param aRect a CGRect
|
||||||
@return float
|
@return float
|
||||||
*/
|
*/
|
||||||
CPRectGetMidX = CGRectGetMidX;
|
function CPRectGetMidX(aRect)
|
||||||
|
{
|
||||||
|
return aRect.origin.x + (aRect.size.width) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect a CGRect
|
@param aRect a CGRect
|
||||||
@return float
|
@return float
|
||||||
*/
|
*/
|
||||||
CPRectGetMidY = CGRectGetMidY;
|
function CPRectGetMidY(aRect)
|
||||||
|
{
|
||||||
|
return aRect.origin.y + (aRect.size.height) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect a CGRect
|
@param aRect a CGRect
|
||||||
@return int
|
@return int
|
||||||
*/
|
*/
|
||||||
CPRectGetMinX = CGRectGetMinX;
|
function CPRectGetMinX(aRect)
|
||||||
|
{
|
||||||
|
return aRect.origin.x;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect a CGRect
|
@param aRect a CGRect
|
||||||
@return int
|
@return int
|
||||||
*/
|
*/
|
||||||
CPRectGetMinY = CGRectGetMinY;
|
function CPRectGetMinY(aRect)
|
||||||
|
{
|
||||||
|
return aRect.origin.y;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@param aRect a CGRect
|
@param aRect a CGRect
|
||||||
@return int
|
@return int
|
||||||
*/
|
*/
|
||||||
CPRectGetWidth = CGRectGetWidth;
|
function CPRectGetWidth(aRect)
|
||||||
|
{
|
||||||
|
return aRect.size.width;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the two rectangles intersect
|
Returns \c YES if the two rectangles intersect
|
||||||
@@ -284,7 +352,22 @@ CPRectGetWidth = CGRectGetWidth;
|
|||||||
@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 \c YES if the two rectangles have any common spaces, and \c NO, otherwise.
|
||||||
*/
|
*/
|
||||||
CPRectIntersectsRect = CGRectIntersectsRect;
|
function CPRectIntersectsRect(lhsRect, rhsRect)
|
||||||
|
{
|
||||||
|
return !CPRectIsEmpty(CPRectIntersection(lhsRect, rhsRect));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns \c YES if the CGRect has no area.
|
||||||
|
The test is performed by checking if the width and height are both zero.
|
||||||
|
@group CGRect
|
||||||
|
@param aRect the CGRect to test
|
||||||
|
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
||||||
|
*/
|
||||||
|
function CPRectIsEmpty(aRect)
|
||||||
|
{
|
||||||
|
return aRect.size.width <= 0.0 || aRect.size.height <= 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the CGRect has no area.
|
Returns \c YES if the CGRect has no area.
|
||||||
@@ -292,7 +375,10 @@ CPRectIntersectsRect = CGRectIntersectsRect;
|
|||||||
@group CGRect
|
@group CGRect
|
||||||
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
||||||
*/
|
*/
|
||||||
CPRectIsNull = CGRectIsNull;
|
function CPRectIsNull(aRect)
|
||||||
|
{
|
||||||
|
return aRect.size.width <= 0.0 || aRect.size.height <= 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates two rectangles -- slice and rem -- from inRect, by dividing inRect
|
Creates two rectangles -- slice and rem -- from inRect, by dividing inRect
|
||||||
@@ -303,7 +389,10 @@ CPRectIsNull = CGRectIsNull;
|
|||||||
|
|
||||||
@group CGRect
|
@group CGRect
|
||||||
*/
|
*/
|
||||||
CPDivideRect = CGRectDivide;
|
function CPDivideRect(inRect, slice, rem, amount, edge)
|
||||||
|
{
|
||||||
|
CGRectDivide(inRect, slice, rem, amount, edge);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c YES if the two CGSizes are identical.
|
Returns \c YES if the two CGSizes are identical.
|
||||||
@@ -312,7 +401,10 @@ CPDivideRect = CGRectDivide;
|
|||||||
@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 \c YES if the two sizes are identical. \c NO, otherwise.
|
||||||
*/
|
*/
|
||||||
CPSizeEqualToSize = CGSizeEqualToSize;
|
function CPSizeEqualToSize(lhsSize, rhsSize)
|
||||||
|
{
|
||||||
|
return lhsSize.width == rhsSize.width && lhsSize.height == rhsSize.height;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a human readable string of the provided CGPoint.
|
Returns a human readable string of the provided CGPoint.
|
||||||
@@ -320,7 +412,10 @@ CPSizeEqualToSize = CGSizeEqualToSize;
|
|||||||
@param aPoint the point to represent
|
@param aPoint the point to represent
|
||||||
@return CGPoint a string representation of the CGPoint
|
@return CGPoint a string representation of the CGPoint
|
||||||
*/
|
*/
|
||||||
CPStringFromPoint = CGStringFromPoint;
|
function CPStringFromPoint(aPoint)
|
||||||
|
{
|
||||||
|
return "{" + aPoint.x + ", " + aPoint.y + "}";
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a human readable string of the provided CGSize.
|
Returns a human readable string of the provided CGSize.
|
||||||
@@ -328,7 +423,10 @@ CPStringFromPoint = CGStringFromPoint;
|
|||||||
@param aSize the size to represent
|
@param aSize the size to represent
|
||||||
@return CGSize a string representation of the CGSize
|
@return CGSize a string representation of the CGSize
|
||||||
*/
|
*/
|
||||||
CPStringFromSize = CGStringFromSize;
|
function CPStringFromSize(aSize)
|
||||||
|
{
|
||||||
|
return "{" + aSize.width + ", " + aSize.height + "}";
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a human readable string of the provided CGRect.
|
Returns a human readable string of the provided CGRect.
|
||||||
@@ -336,7 +434,10 @@ CPStringFromSize = CGStringFromSize;
|
|||||||
@param aRect the rectangle to represent
|
@param aRect the rectangle to represent
|
||||||
@return CGString the string representation of the rectangle
|
@return CGString the string representation of the rectangle
|
||||||
*/
|
*/
|
||||||
CPStringFromRect = CGStringFromRect;
|
function CPStringFromRect(aRect)
|
||||||
|
{
|
||||||
|
return "{" + CPStringFromPoint(aRect.origin) + ", " + CPStringFromSize(aRect.size) + "}";
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a CGPoint from a string with a comma separated pair of integers.
|
Returns a CGPoint from a string with a comma separated pair of integers.
|
||||||
@@ -344,7 +445,12 @@ CPStringFromRect = CGStringFromRect;
|
|||||||
@param aString a string containing two comma separated integers
|
@param aString a string containing two comma separated integers
|
||||||
@return CGPoint the point object created from the string
|
@return CGPoint the point object created from the string
|
||||||
*/
|
*/
|
||||||
CPPointFromString = CGPointFromString;
|
function CPPointFromString(aString)
|
||||||
|
{
|
||||||
|
var comma = aString.indexOf(',');
|
||||||
|
|
||||||
|
return { x:parseFloat(aString.substr(1, comma - 1), 10), y:parseFloat(aString.substring(comma + 1, aString.length), 10) };
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a CGSize from a string containing a pair of comma separated integers.
|
Returns a CGSize from a string containing a pair of comma separated integers.
|
||||||
@@ -352,7 +458,12 @@ CPPointFromString = CGPointFromString;
|
|||||||
@param aString a string containing two comma separated integers
|
@param aString a string containing two comma separated integers
|
||||||
@return CGSize the size object created from the string
|
@return CGSize the size object created from the string
|
||||||
*/
|
*/
|
||||||
CPSizeFromString = CGSizeFromString;
|
function CPSizeFromString(aString)
|
||||||
|
{
|
||||||
|
var comma = aString.indexOf(',');
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -360,32 +471,53 @@ CPSizeFromString = CGSizeFromString;
|
|||||||
@param aString a string in the form generated by \c CPStringFromRect
|
@param aString a string in the form generated by \c CPStringFromRect
|
||||||
@return CGRect the rectangle created from the string
|
@return CGRect the rectangle created from the string
|
||||||
*/
|
*/
|
||||||
CPRectFromString = CGRectFromString;
|
function CPRectFromString(aString)
|
||||||
|
{
|
||||||
|
var comma = aString.indexOf(',', aString.indexOf(',') + 1);
|
||||||
|
|
||||||
|
return { origin:CPPointFromString(aString.substr(1, comma - 1)), size:CPSizeFromString(aString.substring(comma + 2, aString.length)) };
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@group CGPoint
|
@group CGPoint
|
||||||
@param anEvent
|
@param anEvent
|
||||||
@return CGPoint
|
@return CGPoint
|
||||||
*/
|
*/
|
||||||
CPPointFromEvent = CGPointFromEvent;
|
function CPPointFromEvent(anEvent)
|
||||||
|
{
|
||||||
|
return CPPointMake(anEvent.clientX, anEvent.clientY, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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 \c width and \c height
|
||||||
*/
|
*/
|
||||||
CPSizeMakeZero = CGSizeMakeZero;
|
function CPSizeMakeZero()
|
||||||
|
{
|
||||||
|
return CPSizeMake(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a rectangle at origin \c (0,0) and size of \c (0,0).
|
Returns a rectangle at origin \c (0,0) and size of \c (0,0).
|
||||||
@group CGRect
|
@group CGRect
|
||||||
@return CGRect a zeroed out CGRect
|
@return CGRect a zeroed out CGRect
|
||||||
*/
|
*/
|
||||||
CPRectMakeZero = CGRectMakeZero;
|
function CPRectMakeZero()
|
||||||
|
{
|
||||||
|
return CPRectMake(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a point located at \c (0, 0).
|
Returns a point located at \c (0, 0).
|
||||||
@group CGPoint
|
@group CGPoint
|
||||||
@return CGPoint a point located at \c (0, 0)
|
@return CGPoint a point located at \c (0, 0)
|
||||||
*/
|
*/
|
||||||
CPPointMakeZero = CGPointMakeZero;
|
function CPPointMakeZero()
|
||||||
|
{
|
||||||
|
return CPPointMake(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@}
|
||||||
|
*/
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPGradient.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Alexander Ljungberg.
|
|
||||||
* Copyright 2012, SlevenBits Ltd.
|
|
||||||
*
|
|
||||||
* 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/CPException.j>
|
|
||||||
@import <Foundation/CPMutableArray.j>
|
|
||||||
|
|
||||||
@import "CGColor.j"
|
|
||||||
@import "CGColorSpace.j"
|
|
||||||
@import "CGGradient.j"
|
|
||||||
@import "CPBezierPath.j"
|
|
||||||
@import "CPGraphicsContext.j"
|
|
||||||
|
|
||||||
CPGradientDrawsBeforeStartingLocation = kCGGradientDrawsBeforeStartLocation;
|
|
||||||
CPGradientDrawsAfterEndingLocation = kCGGradientDrawsAfterEndLocation;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
A class for drawing linear and radial gradients with a convenient API.
|
|
||||||
*/
|
|
||||||
@implementation CPGradient : CPObject
|
|
||||||
{
|
|
||||||
CGGradient _gradient;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithStartingColor:(CPColor)startingColor endingColor:(CPColor)endingColor
|
|
||||||
{
|
|
||||||
return [self initWithColors:[startingColor, endingColor]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithColors:(CPArray)someColors
|
|
||||||
{
|
|
||||||
var count = [someColors count];
|
|
||||||
|
|
||||||
if (count < 2)
|
|
||||||
[CPException raise:CPInvalidArgumentException reason:@"at least 2 colors required"];
|
|
||||||
|
|
||||||
var distance = 1.0 / (count - 1),
|
|
||||||
locations = [CPMutableArray array],
|
|
||||||
location = 0.0;
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
[locations addObject:location];
|
|
||||||
location += distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [self initWithColors:someColors atLocations:locations colorSpace:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithColors:(CPArray)someColors atLocations:(CPArray)someLocations colorSpace:(CPColorSpace)aColorSpace
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
var cgColors = [],
|
|
||||||
count = [someColors count],
|
|
||||||
colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB;
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
cgColors.push(CGColorCreate(colorSpace, [someColors[i] components]));
|
|
||||||
_gradient = CGGradientCreateWithColors(colorSpace, cgColors, someLocations);
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawInRect:(CGRect)rect angle:(float)angle
|
|
||||||
{
|
|
||||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
CGContextSaveGState(ctx);
|
|
||||||
CGContextClipToRect(ctx, rect);
|
|
||||||
CGContextAddRect(ctx, rect);
|
|
||||||
|
|
||||||
[self _drawInRect:rect atAngle:angle];
|
|
||||||
|
|
||||||
CGContextRestoreGState(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore like draw in rect but apply no clipping.
|
|
||||||
*/
|
|
||||||
- (void)_drawInRect:(CGRect)rect atAngle:(float)angle
|
|
||||||
{
|
|
||||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
|
|
||||||
startPoint,
|
|
||||||
endPoint;
|
|
||||||
|
|
||||||
// Modulo of negative values doesn't work as expected in JS.
|
|
||||||
angle = ((angle % 360.0) + 360.0) % 360.0;
|
|
||||||
|
|
||||||
if (angle < 90.0)
|
|
||||||
startPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
|
|
||||||
else if (angle < 180.0)
|
|
||||||
startPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
|
|
||||||
else if (angle < 270.0)
|
|
||||||
startPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
|
|
||||||
else
|
|
||||||
startPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
|
|
||||||
|
|
||||||
// A line segment comes out of the starting point at the given angle, with the first colour
|
|
||||||
// at the starting point and the last at the end. To do what drawInRect: is supposed to do
|
|
||||||
// we want the opposite corner of the starting corner to just reach the final colour stop.
|
|
||||||
// So when the angle isn't a right angle, the segment has to extend beyond the edge of the
|
|
||||||
// rectangle just far enough. This is hard to describe without a picture but if we place
|
|
||||||
// another line through the opposite corner at -90 degrees, it'll help form some triangles
|
|
||||||
// from which we can derive this formula:
|
|
||||||
//
|
|
||||||
// length = cos(angle) * (rectWidth + rectHeight * tan(angle))
|
|
||||||
//
|
|
||||||
// This simplifies down to (in the first quadrant) rectWidth * cos(a) + rectHeight * sin(a).
|
|
||||||
|
|
||||||
var radians = PI * angle / 180.0,
|
|
||||||
length = ABS(CGRectGetWidth(rect) * COS(radians)) + ABS(CGRectGetHeight(rect) * SIN(radians));
|
|
||||||
|
|
||||||
endPoint = CGPointMake(startPoint.x + length * COS(radians),
|
|
||||||
startPoint.y + length * SIN(radians));
|
|
||||||
|
|
||||||
[self drawFromPoint:startPoint toPoint:endPoint options:CPGradientDrawsBeforeStartingLocation | CPGradientDrawsAfterEndingLocation];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawInBezierPath:(CPBezierPath)aPath angle:(float)anAngle
|
|
||||||
{
|
|
||||||
[CPGraphicsContext saveGraphicsState];
|
|
||||||
|
|
||||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
// Nail down the path which CGContextDrawLinearGradient will cause to be filled.
|
|
||||||
// Note we don't do this by clipping to the path and then calling drawInRect:atAngle:
|
|
||||||
// as this would cut off any antialias of the drawing, plus any active context shadow.
|
|
||||||
CGContextBeginPath(ctx);
|
|
||||||
CGContextAddPath(ctx, aPath._path);
|
|
||||||
CGContextSetLineWidth(ctx, [aPath lineWidth]);
|
|
||||||
CGContextClosePath(ctx);
|
|
||||||
|
|
||||||
[self _drawInRect:[aPath bounds] atAngle:anAngle];
|
|
||||||
|
|
||||||
[CPGraphicsContext restoreGraphicsState];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawFromPoint:(NSPoint)startingPoint toPoint:(NSPoint)endingPoint options:(NSGradientDrawingOptions)options
|
|
||||||
{
|
|
||||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
// TODO kCGGradientDrawsBeforeStartLocation and kCGGradientDrawsAfterEndLocation are not actually supported
|
|
||||||
// by CGContextDrawLinearGradient yet.
|
|
||||||
CGContextDrawLinearGradient(ctx, _gradient, startingPoint, endingPoint, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+13
-26
@@ -23,16 +23,6 @@
|
|||||||
@import "CPColor.j"
|
@import "CPColor.j"
|
||||||
@import "CPGraphicsContext.j"
|
@import "CPGraphicsContext.j"
|
||||||
|
|
||||||
CPCalibratedWhiteColorSpace = @"CalibratedWhiteColorSpace";
|
|
||||||
CPCalibratedBlackColorSpace = @"CalibratedBlackColorSpace";
|
|
||||||
CPCalibratedRGBColorSpace = @"CalibratedRGBColorSpace";
|
|
||||||
CPDeviceWhiteColorSpace = @"DeviceWhiteColorSpace";
|
|
||||||
CPDeviceBlackColorSpace = @"DeviceBlackColorSpace";
|
|
||||||
CPDeviceRGBColorSpace = @"DeviceRGBColorSpace";
|
|
||||||
CPDeviceCMYKColorSpace = @"DeviceCMYKColorSpace";
|
|
||||||
CPNamedColorSpace = @"NamedColorSpace";
|
|
||||||
CPPatternColorSpace = @"PatternColorSpace";
|
|
||||||
CPCustomColorSpace = @"CustomColorSpace";
|
|
||||||
|
|
||||||
function CPDrawTiledRects(
|
function CPDrawTiledRects(
|
||||||
/* CGRect */ boundsRect,
|
/* CGRect */ boundsRect,
|
||||||
@@ -60,9 +50,9 @@ function CPDrawColorTiledRects(
|
|||||||
if (sides.length != colors.length)
|
if (sides.length != colors.length)
|
||||||
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and colors (length: " + colors.length + ") must have the same length."];
|
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and colors (length: " + colors.length + ") must have the same length."];
|
||||||
|
|
||||||
var resultRect = CGRectMakeCopy(boundsRect),
|
var resultRect = _CGRectMakeCopy(boundsRect),
|
||||||
slice = CGRectMakeZero(),
|
slice = _CGRectMakeZero(),
|
||||||
remainder = CGRectMakeZero(),
|
remainder = _CGRectMakeZero(),
|
||||||
context = [[CPGraphicsContext currentContext] graphicsPort];
|
context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||||
|
|
||||||
CGContextSaveGState(context);
|
CGContextSaveGState(context);
|
||||||
@@ -77,34 +67,31 @@ function CPDrawColorTiledRects(
|
|||||||
slice = CGRectIntersection(slice, clipRect);
|
slice = CGRectIntersection(slice, clipRect);
|
||||||
|
|
||||||
// Cocoa docs say that only slices that are within the clipRect are actually drawn
|
// Cocoa docs say that only slices that are within the clipRect are actually drawn
|
||||||
if (CGRectIsEmpty(slice))
|
if (_CGRectIsEmpty(slice))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var minX,
|
var minX, maxX, minY, maxY;
|
||||||
maxX,
|
|
||||||
minY,
|
|
||||||
maxY;
|
|
||||||
|
|
||||||
if (side == CPMinXEdge || side == CPMaxXEdge)
|
if (side == CPMinXEdge || side == CPMaxXEdge)
|
||||||
{
|
{
|
||||||
// Make sure we have at least 1 pixel to draw a line
|
// Make sure we have at least 1 pixel to draw a line
|
||||||
if (CGRectGetWidth(slice) < 1.0)
|
if (_CGRectGetWidth(slice) < 1.0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
minX = CGRectGetMinX(slice) + 0.5;
|
minX = _CGRectGetMinX(slice) + 0.5;
|
||||||
maxX = minX;
|
maxX = minX;
|
||||||
minY = CGRectGetMinY(slice);
|
minY = _CGRectGetMinY(slice);
|
||||||
maxY = CGRectGetMaxY(slice);
|
maxY = _CGRectGetMaxY(slice);
|
||||||
}
|
}
|
||||||
else // CPMinYEdge || CPMaxYEdge
|
else // CPMinYEdge || CPMaxYEdge
|
||||||
{
|
{
|
||||||
// Make sure we have at least 1 pixel to draw a line
|
// Make sure we have at least 1 pixel to draw a line
|
||||||
if (CGRectGetHeight(slice) < 1.0)
|
if (_CGRectGetHeight(slice) < 1.0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
minX = CGRectGetMinX(slice);
|
minX = _CGRectGetMinX(slice);
|
||||||
maxX = CGRectGetMaxX(slice);
|
maxX = _CGRectGetMaxX(slice);
|
||||||
minY = CGRectGetMinY(slice) + 0.5;
|
minY = _CGRectGetMinY(slice) + 0.5;
|
||||||
maxY = minY;
|
maxY = minY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,7 @@
|
|||||||
@import "CGContext.j"
|
@import "CGContext.j"
|
||||||
|
|
||||||
|
|
||||||
var CPGraphicsContextCurrent = nil,
|
var CPGraphicsContextCurrent = nil;
|
||||||
CPGraphicsContextThreadStack = nil;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -34,7 +33,7 @@ var CPGraphicsContextCurrent = nil,
|
|||||||
*/
|
*/
|
||||||
@implementation CPGraphicsContext : CPObject
|
@implementation CPGraphicsContext : CPObject
|
||||||
{
|
{
|
||||||
CGContext _graphicsPort;
|
CPContext _graphicsPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -53,29 +52,6 @@ var CPGraphicsContextCurrent = nil,
|
|||||||
CPGraphicsContextCurrent = aGraphicsContext;
|
CPGraphicsContextCurrent = aGraphicsContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)saveGraphicsState
|
|
||||||
{
|
|
||||||
if (!CPGraphicsContextCurrent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!CPGraphicsContextThreadStack)
|
|
||||||
CPGraphicsContextThreadStack = [CPMutableArray array];
|
|
||||||
|
|
||||||
[CPGraphicsContextThreadStack addObject:CPGraphicsContextCurrent];
|
|
||||||
[CPGraphicsContextCurrent saveGraphicsState];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)restoreGraphicsState
|
|
||||||
{
|
|
||||||
var lastContext = [CPGraphicsContextThreadStack lastObject];
|
|
||||||
|
|
||||||
if (lastContext)
|
|
||||||
{
|
|
||||||
[lastContext restoreGraphicsState];
|
|
||||||
[CPGraphicsContextThreadStack removeLastObject];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a graphics context with a provided port.
|
Creates a graphics context with a provided port.
|
||||||
@param aContext the context to initialize with
|
@param aContext the context to initialize with
|
||||||
@@ -110,27 +86,4 @@ var CPGraphicsContextCurrent = nil,
|
|||||||
return _graphicsPort;
|
return _graphicsPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
(Not implemented. Always returns Cappuccino's default, `YES`.)
|
|
||||||
|
|
||||||
Return `isFlipped` of the receiver's focused view, unless no view is focused in which case the receiver's
|
|
||||||
`flipped` flag, as set by `graphicsContextWithGraphicsPort:flipped:`, is returned.
|
|
||||||
|
|
||||||
Note: Cappuccino is flipped by default.
|
|
||||||
*/
|
|
||||||
- (BOOL)isFlipped
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)saveGraphicsState
|
|
||||||
{
|
|
||||||
CGContextSaveGState(_graphicsPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)restoreGraphicsState
|
|
||||||
{
|
|
||||||
CGContextRestoreGState(_graphicsPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+13
-130
@@ -25,10 +25,9 @@
|
|||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPRunLoop.j>
|
@import <Foundation/CPRunLoop.j>
|
||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
@import <Foundation/CPData.j>
|
|
||||||
|
|
||||||
@import "CGGeometry.j"
|
@import "CPGeometry.j"
|
||||||
@import "CPCompatibility.j"
|
|
||||||
|
|
||||||
CPImageLoadStatusInitialized = 0;
|
CPImageLoadStatusInitialized = 0;
|
||||||
CPImageLoadStatusLoading = 1;
|
CPImageLoadStatusLoading = 1;
|
||||||
@@ -45,50 +44,20 @@ CPImageNameColorPanel = @"CPImageNameColorPanel";
|
|||||||
CPImageNameColorPanelHighlighted = @"CPImageNameColorPanelHighlighted";
|
CPImageNameColorPanelHighlighted = @"CPImageNameColorPanelHighlighted";
|
||||||
|
|
||||||
var imagesForNames = { },
|
var imagesForNames = { },
|
||||||
AppKitImageForNames = { },
|
AppKitImageForNames = { };
|
||||||
ImageDescriptionFormat = "%s {\n filename: \"%s\",\n size: { width:%f, height:%f }\n}";
|
|
||||||
|
|
||||||
AppKitImageForNames[CPImageNameColorPanel] = CGSizeMake(26.0, 29.0);
|
AppKitImageForNames[CPImageNameColorPanel] = CGSizeMake(26.0, 29.0);
|
||||||
AppKitImageForNames[CPImageNameColorPanelHighlighted] = CGSizeMake(26.0, 29.0);
|
AppKitImageForNames[CPImageNameColorPanelHighlighted] = CGSizeMake(26.0, 29.0);
|
||||||
|
|
||||||
/*!
|
function CPImageInBundle(aFilename, aSize, aBundle)
|
||||||
Returns a resource image with a relative path and size
|
|
||||||
in a bundle.
|
|
||||||
|
|
||||||
@param filename A filename or relative path to a resource image.
|
|
||||||
@param width Width of the image. May be omitted.
|
|
||||||
@param height Height of the image. May be omitted if width is omitted.
|
|
||||||
@param size Instead of passing width/height, a CGSize may be passed.
|
|
||||||
@param bundle Bundle in which the image resource can be found.
|
|
||||||
If omitted, defaults to the main bundle.
|
|
||||||
@return CPImage
|
|
||||||
*/
|
|
||||||
function CPImageInBundle()
|
|
||||||
{
|
{
|
||||||
var filename = arguments[0],
|
if (!aBundle)
|
||||||
size = nil,
|
aBundle = [CPBundle mainBundle];
|
||||||
bundle = nil;
|
|
||||||
|
|
||||||
if (typeof(arguments[1]) === "number")
|
if (aSize)
|
||||||
{
|
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename] size:aSize];
|
||||||
if (arguments[1] !== nil && arguments[1] !== undefined)
|
|
||||||
size = CGSizeMake(arguments[1], arguments[2]);
|
|
||||||
|
|
||||||
bundle = arguments[3];
|
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename]];
|
||||||
}
|
|
||||||
else if (typeof(arguments[1]) === "object")
|
|
||||||
{
|
|
||||||
size = arguments[1];
|
|
||||||
bundle = arguments[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bundle)
|
|
||||||
bundle = [CPBundle mainBundle];
|
|
||||||
|
|
||||||
if (size)
|
|
||||||
return [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:filename] size:size];
|
|
||||||
|
|
||||||
return [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:filename]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function CPAppKitImage(aFilename, aSize)
|
function CPAppKitImage(aFilename, aSize)
|
||||||
@@ -148,7 +117,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_size = CGSizeMakeCopy(aSize);
|
_size = CPSizeCreateCopy(aSize);
|
||||||
_filename = aFilename;
|
_filename = aFilename;
|
||||||
_loadStatus = CPImageLoadStatusInitialized;
|
_loadStatus = CPImageLoadStatusInitialized;
|
||||||
}
|
}
|
||||||
@@ -188,20 +157,6 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes the receiver with the specified data. The method loads the data into memory.
|
|
||||||
@param someData the CPData object representing the image
|
|
||||||
@return the initialized image
|
|
||||||
*/
|
|
||||||
- (id)initWithData:(CPData)someData
|
|
||||||
{
|
|
||||||
var base64 = [someData base64],
|
|
||||||
type = [base64 hasPrefix:@"/9j/4AAQSkZJRgABAQEASABIAAD/"] ? @"jpg" : @"png",
|
|
||||||
dataURL = "data:image/" + type + ";base64," + base64;
|
|
||||||
|
|
||||||
return [self initWithContentsOfFile:dataURL];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the path of the file associated with this image.
|
Returns the path of the file associated with this image.
|
||||||
*/
|
*/
|
||||||
@@ -210,37 +165,6 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
return _filename;
|
return _filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the data associated with this image.
|
|
||||||
@discussion Returns nil if the reciever was not initialized with -initWithData: and the browser does not support the canvas feature;
|
|
||||||
*/
|
|
||||||
- (CPData)data
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var dataURL;
|
|
||||||
|
|
||||||
if ([_filename hasPrefix:@"data:image"])
|
|
||||||
dataURL = _filename;
|
|
||||||
else if (CPFeatureIsCompatible(CPHTMLCanvasFeature))
|
|
||||||
{
|
|
||||||
var canvas = document.createElement("canvas"),
|
|
||||||
ctx = canvas.getContext("2d");
|
|
||||||
|
|
||||||
canvas.width = _image.width,
|
|
||||||
canvas.height = _image.height;
|
|
||||||
|
|
||||||
ctx.drawImage(_image, 0, 0);
|
|
||||||
|
|
||||||
dataURL = canvas.toDataURL("image/png");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
var base64 = dataURL.replace(/^data:image\/png;base64,/, "");
|
|
||||||
return [CPData dataWithBase64:base64];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the size of the image.
|
Sets the size of the image.
|
||||||
@param the size of the image
|
@param the size of the image
|
||||||
@@ -302,14 +226,6 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the underlying Image element for a single image.
|
|
||||||
*/
|
|
||||||
- (Image)image
|
|
||||||
{
|
|
||||||
return _image;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the receiver's delegate.
|
Sets the receiver's delegate.
|
||||||
@param the delegate
|
@param the delegate
|
||||||
@@ -364,7 +280,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||||
}
|
}
|
||||||
[self _derefFromImage];
|
[self _derefFromImage];
|
||||||
};
|
}
|
||||||
|
|
||||||
_image.onerror = function ()
|
_image.onerror = function ()
|
||||||
{
|
{
|
||||||
@@ -376,7 +292,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||||
}
|
}
|
||||||
[self _derefFromImage];
|
[self _derefFromImage];
|
||||||
};
|
}
|
||||||
|
|
||||||
_image.onabort = function ()
|
_image.onabort = function ()
|
||||||
{
|
{
|
||||||
@@ -388,7 +304,7 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||||
}
|
}
|
||||||
[self _derefFromImage];
|
[self _derefFromImage];
|
||||||
};
|
}
|
||||||
|
|
||||||
_image.src = _filename;
|
_image.src = _filename;
|
||||||
|
|
||||||
@@ -398,11 +314,6 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isSingleImage
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isThreePartImage
|
- (BOOL)isThreePartImage
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
@@ -413,24 +324,6 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
var filename = [self filename],
|
|
||||||
size = [self size];
|
|
||||||
|
|
||||||
if (filename.indexOf("data:") === 0)
|
|
||||||
{
|
|
||||||
var index = filename.indexOf(",");
|
|
||||||
|
|
||||||
if (index > 0)
|
|
||||||
filename = [CPString stringWithFormat:@"%s,%s...%s", filename.substr(0, index), filename.substr(index + 1, 10), filename.substr(filename.length - 10)];
|
|
||||||
else
|
|
||||||
filename = "data:<unknown type>";
|
|
||||||
}
|
|
||||||
|
|
||||||
return [CPString stringWithFormat:ImageDescriptionFormat, [super description], filename, size.width, size.height];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (void)_derefFromImage
|
- (void)_derefFromImage
|
||||||
{
|
{
|
||||||
@@ -534,11 +427,6 @@ function CPAppKitImage(aFilename, aSize)
|
|||||||
return _isVertical;
|
return _isVertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isSingleImage
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isThreePartImage
|
- (BOOL)isThreePartImage
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
@@ -603,11 +491,6 @@ var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey",
|
|||||||
return _imageSlices;
|
return _imageSlices;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isSingleImage
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isThreePartImage
|
- (BOOL)isThreePartImage
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
|
|||||||
+54
-121
@@ -26,8 +26,10 @@
|
|||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
@import "CPShadowView.j"
|
@import "CPShadowView.j"
|
||||||
|
|
||||||
@global CPImagesPboardType
|
|
||||||
@global appkit_tag_dom_elements
|
CPScaleProportionally = 0;
|
||||||
|
CPScaleToFit = 1;
|
||||||
|
CPScaleNone = 2;
|
||||||
|
|
||||||
CPImageAlignCenter = 0;
|
CPImageAlignCenter = 0;
|
||||||
CPImageAlignTop = 1;
|
CPImageAlignTop = 1;
|
||||||
@@ -39,7 +41,15 @@ CPImageAlignBottomLeft = 6;
|
|||||||
CPImageAlignBottomRight = 7;
|
CPImageAlignBottomRight = 7;
|
||||||
CPImageAlignRight = 8;
|
CPImageAlignRight = 8;
|
||||||
|
|
||||||
var CPImageViewEmptyPlaceholderImage = nil;
|
var CPImageViewShadowBackgroundColor = nil,
|
||||||
|
CPImageViewEmptyPlaceholderImage = nil;
|
||||||
|
|
||||||
|
var LEFT_SHADOW_INSET = 3.0,
|
||||||
|
RIGHT_SHADOW_INSET = 3.0,
|
||||||
|
TOP_SHADOW_INSET = 3.0,
|
||||||
|
BOTTOM_SHADOW_INSET = 5.0,
|
||||||
|
VERTICAL_SHADOW_INSET = TOP_SHADOW_INSET + BOTTOM_SHADOW_INSET,
|
||||||
|
HORIZONTAL_SHADOW_INSET = LEFT_SHADOW_INSET + RIGHT_SHADOW_INSET;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -62,24 +72,11 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
|||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self !== [CPImageView class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var bundle = [CPBundle bundleForClass:[CPView class]];
|
var bundle = [CPBundle bundleForClass:[CPView class]];
|
||||||
|
|
||||||
CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]];
|
CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding === CPValueBinding || aBinding === CPValueURLBinding || aBinding === CPValuePathBinding || aBinding === CPDataBinding)
|
|
||||||
return [CPImageViewValueBinder class];
|
|
||||||
else if ([aBinding hasPrefix:CPEditableBinding])
|
|
||||||
return [CPMultipleValueAndBinding class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
@@ -243,7 +240,7 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
|||||||
[super setImageScaling:anImageScaling];
|
[super setImageScaling:anImageScaling];
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
if ([self currentValueForThemeAttribute:@"image-scaling"] === CPImageScaleAxesIndependently)
|
if ([self currentValueForThemeAttribute:@"image-scaling"] === CPScaleToFit)
|
||||||
{
|
{
|
||||||
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, 0.0, 0.0);
|
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
@@ -300,19 +297,19 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
|||||||
imageScaling = [self currentValueForThemeAttribute:@"image-scaling"],
|
imageScaling = [self currentValueForThemeAttribute:@"image-scaling"],
|
||||||
x = 0.0,
|
x = 0.0,
|
||||||
y = 0.0,
|
y = 0.0,
|
||||||
insetWidth = (_hasShadow ? [_shadowView horizontalInset] : 0.0),
|
insetWidth = (_hasShadow ? HORIZONTAL_SHADOW_INSET : 0.0),
|
||||||
insetHeight = (_hasShadow ? [_shadowView verticalInset] : 0.0),
|
insetHeight = (_hasShadow ? VERTICAL_SHADOW_INSET : 0.0),
|
||||||
boundsWidth = CGRectGetWidth(bounds),
|
boundsWidth = _CGRectGetWidth(bounds),
|
||||||
boundsHeight = CGRectGetHeight(bounds),
|
boundsHeight = _CGRectGetHeight(bounds),
|
||||||
width = boundsWidth - insetWidth,
|
width = boundsWidth - insetWidth,
|
||||||
height = boundsHeight - insetHeight;
|
height = boundsHeight - insetHeight;
|
||||||
|
|
||||||
if (imageScaling === CPImageScaleAxesIndependently)
|
if (imageScaling === CPScaleToFit)
|
||||||
{
|
{
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
_DOMImageElement.width = ROUND(width);
|
_DOMImageElement.width = ROUND(width);
|
||||||
_DOMImageElement.height = ROUND(height);
|
_DOMImageElement.height = ROUND(height);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -321,19 +318,17 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
|||||||
if (size.width == -1 && size.height == -1)
|
if (size.width == -1 && size.height == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (imageScaling)
|
if (imageScaling === CPScaleProportionally)
|
||||||
{
|
{
|
||||||
case CPImageScaleProportionallyDown:
|
// The max size it can be is size.width x size.height, so only
|
||||||
if (width >= size.width && height >= size.height)
|
// only proportion otherwise.
|
||||||
{
|
if (width >= size.width && height >= size.height)
|
||||||
width = size.width;
|
{
|
||||||
height = size.height;
|
width = size.width;
|
||||||
break;
|
height = size.height;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// intentionally fall through to the next case
|
{
|
||||||
|
|
||||||
case CPImageScaleProportionallyUpOrDown:
|
|
||||||
var imageRatio = size.width / size.height,
|
var imageRatio = size.width / size.height,
|
||||||
viewRatio = width / height;
|
viewRatio = width / height;
|
||||||
|
|
||||||
@@ -341,22 +336,28 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
|||||||
width = height * imageRatio;
|
width = height * imageRatio;
|
||||||
else
|
else
|
||||||
height = width / imageRatio;
|
height = width / imageRatio;
|
||||||
break;
|
}
|
||||||
|
|
||||||
case CPImageScaleAxesIndependently:
|
#if PLATFORM(DOM)
|
||||||
case CPImageScaleNone:
|
|
||||||
width = size.width;
|
|
||||||
height = size.height;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
_DOMImageElement.width = ROUND(width);
|
_DOMImageElement.width = ROUND(width);
|
||||||
_DOMImageElement.height = ROUND(height);
|
_DOMImageElement.height = ROUND(height);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
width = size.width;
|
||||||
|
height = size.height;
|
||||||
|
}
|
||||||
|
|
||||||
var x,
|
if (imageScaling == CPScaleNone)
|
||||||
y;
|
{
|
||||||
|
#if PLATFORM(DOM)
|
||||||
|
_DOMImageElement.width = ROUND(size.width);
|
||||||
|
_DOMImageElement.height = ROUND(size.height);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
var x, y;
|
||||||
|
|
||||||
switch (_imageAlignment)
|
switch (_imageAlignment)
|
||||||
{
|
{
|
||||||
@@ -401,10 +402,10 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
_imageRect = CGRectMake(x, y, width, height);
|
_imageRect = _CGRectMake(x, y, width, height);
|
||||||
|
|
||||||
if (_hasShadow)
|
if (_hasShadow)
|
||||||
[_shadowView setFrame:CGRectMake(x - [_shadowView leftInset], y - [_shadowView topInset], width + insetWidth, height + insetHeight)];
|
[_shadowView setFrame:_CGRectMake(x - LEFT_SHADOW_INSET, y - TOP_SHADOW_INSET, width + insetWidth, height + insetHeight)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
@@ -455,53 +456,6 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPImageViewValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
|
||||||
{
|
|
||||||
[self _setPlaceholder:nil forMarker:CPMultipleValuesMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:nil forMarker:CPNoSelectionMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:nil forMarker:CPNotApplicableMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:nil forMarker:CPNullMarker isDefault:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[_source setImage:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var image;
|
|
||||||
|
|
||||||
if (aValue == nil)
|
|
||||||
image = nil;
|
|
||||||
else if (aBinding === CPDataBinding)
|
|
||||||
image = [[CPImage alloc] initWithData:aValue];
|
|
||||||
else if (aBinding === CPValueURLBinding || aBinding === CPValuePathBinding)
|
|
||||||
image = [CPImage cachedImageWithContentsOfFile:aValue];
|
|
||||||
else if (aBinding === CPValueBinding)
|
|
||||||
image = aValue;
|
|
||||||
|
|
||||||
[_source setImage:image];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)valueForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var image = [_source image];
|
|
||||||
|
|
||||||
if (aBinding === CPDataBinding)
|
|
||||||
return [image data];
|
|
||||||
else if (aBinding === CPValueURLBinding || aBinding === CPValuePathBinding)
|
|
||||||
return [image filename];
|
|
||||||
else if (aBinding === CPValueBinding)
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPImageViewImageKey = @"CPImageViewImageKey",
|
var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||||
CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
|
CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
|
||||||
CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey",
|
CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey",
|
||||||
@@ -528,9 +482,6 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
|||||||
_DOMImageElement.setAttribute("draggable", "true");
|
_DOMImageElement.setAttribute("draggable", "true");
|
||||||
_DOMImageElement.style["-khtml-user-drag"] = "element";
|
_DOMImageElement.style["-khtml-user-drag"] = "element";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof(appkit_tag_dom_elements) !== "undefined" && !!appkit_tag_dom_elements)
|
|
||||||
_DOMImageElement.setAttribute("data-cappuccino-view", [self className]);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
self = [super initWithCoder:aCoder];
|
self = [super initWithCoder:aCoder];
|
||||||
@@ -544,7 +495,7 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
|||||||
[self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
|
[self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
|
||||||
[self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]];
|
[self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]];
|
||||||
|
|
||||||
if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey])
|
if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO)
|
||||||
[self setEditable:YES];
|
[self setEditable:YES];
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
@@ -584,21 +535,3 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPImage (CachedImage)
|
|
||||||
|
|
||||||
+ (CPImage)cachedImageWithContentsOfFile:(CPString)aFile
|
|
||||||
{
|
|
||||||
var cached_name = [CPString stringWithFormat:@"%@_%d", [self class], [aFile hash]],
|
|
||||||
image = [CPImage imageNamed:cached_name];
|
|
||||||
|
|
||||||
if (!image)
|
|
||||||
{
|
|
||||||
image = [[CPImage alloc] initWithContentsOfFile:aFile];
|
|
||||||
[image setName:cached_name];
|
|
||||||
}
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|||||||
@@ -21,15 +21,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPArray.j>
|
|
||||||
|
|
||||||
@import "CPEvent_Constants.j"
|
@import "CPEvent.j"
|
||||||
@import "CPText.j"
|
|
||||||
|
|
||||||
CPStandardKeyBindings = {
|
CPStandardKeyBindings = {
|
||||||
@"@.": @"cancelOperation:",
|
@"@.": @"cancelOperation:",
|
||||||
|
|
||||||
@"@a": @"selectAll:",
|
|
||||||
@"^a": @"moveToBeginningOfParagraph:",
|
@"^a": @"moveToBeginningOfParagraph:",
|
||||||
@"^$a": @"moveToBeginningOfParagraphAndModifySelection:",
|
@"^$a": @"moveToBeginningOfParagraphAndModifySelection:",
|
||||||
@"^b": @"moveBackward:",
|
@"^b": @"moveBackward:",
|
||||||
@@ -154,7 +152,7 @@ var CPKeyBindingCache = {};
|
|||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self !== [CPKeyBinding class])
|
if ([self class] !== CPKeyBinding)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self createKeyBindingsFromJSObject:CPStandardKeyBindings];
|
[self createKeyBindingsFromJSObject:CPStandardKeyBindings];
|
||||||
|
|||||||
+157
-600
@@ -26,35 +26,29 @@
|
|||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPArray.j>
|
@import <Foundation/CPArray.j>
|
||||||
@import <Foundation/CPDictionary.j>
|
@import <Foundation/CPDictionary.j>
|
||||||
@import <Foundation/CPInvocation.j>
|
|
||||||
@import <Foundation/CPValueTransformer.j>
|
@import <Foundation/CPValueTransformer.j>
|
||||||
@import <Foundation/CPKeyValueObserving.j>
|
|
||||||
|
|
||||||
@class CPButton
|
|
||||||
|
|
||||||
var exposedBindingsMap = @{},
|
var exposedBindingsMap = [CPDictionary new],
|
||||||
bindingsMap = @{};
|
bindingsMap = [CPDictionary new];
|
||||||
|
|
||||||
var CPBindingOperationAnd = 0,
|
var CPBindingOperationAnd = 0,
|
||||||
CPBindingOperationOr = 1;
|
CPBindingOperationOr = 1;
|
||||||
|
|
||||||
@implementation CPBinder : CPObject
|
@implementation CPKeyValueBinding : CPObject
|
||||||
{
|
{
|
||||||
CPDictionary _info;
|
CPDictionary _info;
|
||||||
id _source @accessors(getter=source);
|
id _source;
|
||||||
|
|
||||||
JSObject _suppressedNotifications;
|
|
||||||
JSObject _placeholderForMarker;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)exposeBinding:(CPString)aBinding forClass:(Class)aClass
|
+ (void)exposeBinding:(CPString)aBinding forClass:(Class)aClass
|
||||||
{
|
{
|
||||||
var bindings = [exposedBindingsMap objectForKey:[aClass UID]];
|
var bindings = [exposedBindingsMap objectForKey:[aClass hash]];
|
||||||
|
|
||||||
if (!bindings)
|
if (!bindings)
|
||||||
{
|
{
|
||||||
bindings = [];
|
bindings = [];
|
||||||
[exposedBindingsMap setObject:bindings forKey:[aClass UID]];
|
[exposedBindingsMap setObject:bindings forKey:[aClass hash]];
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings.push(aBinding);
|
bindings.push(aBinding);
|
||||||
@@ -62,12 +56,12 @@ var CPBindingOperationAnd = 0,
|
|||||||
|
|
||||||
+ (CPArray)exposedBindingsForClass:(Class)aClass
|
+ (CPArray)exposedBindingsForClass:(Class)aClass
|
||||||
{
|
{
|
||||||
return [[exposedBindingsMap objectForKey:[aClass UID]] copy];
|
return [[exposedBindingsMap objectForKey:[aClass hash]] copy];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPBinder)getBinding:(CPString)aBinding forObject:(id)anObject
|
+ (CPKeyValueBinding)getBinding:(CPString)aBinding forObject:(id)anObject
|
||||||
{
|
{
|
||||||
return [[bindingsMap objectForKey:[anObject UID]] objectForKey:aBinding];
|
return [[bindingsMap objectForKey:[anObject hash]] objectForKey:aBinding];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPDictionary)infoForBinding:(CPString)aBinding forObject:(id)anObject
|
+ (CPDictionary)infoForBinding:(CPString)aBinding forObject:(id)anObject
|
||||||
@@ -82,12 +76,12 @@ var CPBindingOperationAnd = 0,
|
|||||||
|
|
||||||
+ (CPDictionary)allBindingsForObject:(id)anObject
|
+ (CPDictionary)allBindingsForObject:(id)anObject
|
||||||
{
|
{
|
||||||
return [bindingsMap objectForKey:[anObject UID]];
|
return [bindingsMap objectForKey:[anObject hash]];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)unbind:(CPString)aBinding forObject:(id)anObject
|
+ (void)unbind:(CPString)aBinding forObject:(id)anObject
|
||||||
{
|
{
|
||||||
var bindings = [bindingsMap objectForKey:[anObject UID]];
|
var bindings = [bindingsMap objectForKey:[anObject hash]];
|
||||||
|
|
||||||
if (!bindings)
|
if (!bindings)
|
||||||
return;
|
return;
|
||||||
@@ -97,9 +91,9 @@ var CPBindingOperationAnd = 0,
|
|||||||
if (!theBinding)
|
if (!theBinding)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var info = theBinding._info,
|
var infoDictionary = theBinding._info,
|
||||||
observedObject = [info objectForKey:CPObservedObjectKey],
|
observedObject = [infoDictionary objectForKey:CPObservedObjectKey],
|
||||||
keyPath = [info objectForKey:CPObservedKeyPathKey];
|
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey];
|
||||||
|
|
||||||
[observedObject removeObserver:theBinding forKeyPath:keyPath];
|
[observedObject removeObserver:theBinding forKeyPath:keyPath];
|
||||||
[bindings removeObjectForKey:aBinding];
|
[bindings removeObjectForKey:aBinding];
|
||||||
@@ -107,8 +101,7 @@ var CPBindingOperationAnd = 0,
|
|||||||
|
|
||||||
+ (void)unbindAllForObject:(id)anObject
|
+ (void)unbindAllForObject:(id)anObject
|
||||||
{
|
{
|
||||||
var bindings = [bindingsMap objectForKey:[anObject UID]];
|
var bindings = [bindingsMap objectForKey:[anObject hash]];
|
||||||
|
|
||||||
if (!bindings)
|
if (!bindings)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -118,38 +111,28 @@ var CPBindingOperationAnd = 0,
|
|||||||
while (count--)
|
while (count--)
|
||||||
[anObject unbind:[bindings objectForKey:allKeys[count]]];
|
[anObject unbind:[bindings objectForKey:allKeys[count]]];
|
||||||
|
|
||||||
[bindingsMap removeObjectForKey:[anObject UID]];
|
[bindingsMap removeObjectForKey:[anObject hash]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
|
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
|
||||||
{
|
{
|
||||||
// We use [self init] here because subclasses override init. We can't override this method
|
self = [super init];
|
||||||
// because their initialization has to occur before this method in this class is executed.
|
|
||||||
self = [self init];
|
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_source = aSource;
|
_source = aSource;
|
||||||
_info = @{
|
_info = [CPDictionary dictionaryWithObjects:[aDestination, aKeyPath] forKeys:[CPObservedObjectKey, CPObservedKeyPathKey]];
|
||||||
CPObservedObjectKey: aDestination,
|
|
||||||
CPObservedKeyPathKey: aKeyPath,
|
|
||||||
};
|
|
||||||
_suppressedNotifications = {};
|
|
||||||
_placeholderForMarker = {};
|
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
[_info setObject:options forKey:CPOptionsKey];
|
[_info setObject:options forKey:CPOptionsKey];
|
||||||
|
|
||||||
[self _updatePlaceholdersWithOptions:options forBinding:aName];
|
|
||||||
|
|
||||||
[aDestination addObserver:self forKeyPath:aKeyPath options:CPKeyValueObservingOptionNew context:aBinding];
|
[aDestination addObserver:self forKeyPath:aKeyPath options:CPKeyValueObservingOptionNew context:aBinding];
|
||||||
|
|
||||||
var bindings = [bindingsMap objectForKey:[_source UID]];
|
var bindings = [bindingsMap objectForKey:[_source hash]];
|
||||||
|
|
||||||
if (!bindings)
|
if (!bindings)
|
||||||
{
|
{
|
||||||
bindings = @{};
|
bindings = [CPDictionary new];
|
||||||
[bindingsMap setObject:bindings forKey:[_source UID]];
|
[bindingsMap setObject:bindings forKey:[_source hash]];
|
||||||
}
|
}
|
||||||
|
|
||||||
[bindings setObject:self forKey:aName];
|
[bindings setObject:self forKey:aName];
|
||||||
@@ -159,44 +142,44 @@ var CPBindingOperationAnd = 0,
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)raiseIfNotApplicable:(id)aValue forKeyPath:(CPString)keyPath options:(CPDictionary)options
|
- (void)setValueFor:(CPString)aBinding
|
||||||
{
|
|
||||||
if (aValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
|
||||||
{
|
|
||||||
[CPException raise:CPGenericException
|
|
||||||
reason:@"Cannot transform non-applicable key on: " + _source + " key path: " + keyPath + " value: " + aValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)theBinding
|
|
||||||
{
|
{
|
||||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
||||||
options = [_info objectForKey:CPOptionsKey],
|
options = [_info objectForKey:CPOptionsKey],
|
||||||
newValue = [destination valueForKeyPath:keyPath];
|
newValue = [destination valueForKeyPath:keyPath],
|
||||||
|
isPlaceholder = CPIsControllerMarker(newValue);
|
||||||
|
|
||||||
if (CPIsControllerMarker(newValue))
|
if (isPlaceholder)
|
||||||
{
|
{
|
||||||
[self raiseIfNotApplicable:newValue forKeyPath:keyPath options:options];
|
switch (newValue)
|
||||||
|
{
|
||||||
|
case CPMultipleValuesMarker:
|
||||||
|
newValue = [options objectForKey:CPMultipleValuesPlaceholderBindingOption] || @"Multiple Values";
|
||||||
|
break;
|
||||||
|
|
||||||
var value = [self _placeholderForMarker:newValue];
|
case CPNoSelectionMarker:
|
||||||
[self setPlaceholderValue:value withMarker:newValue forBinding:theBinding];
|
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
|
else
|
||||||
{
|
{
|
||||||
var value = [self transformValue:newValue withOptions:options];
|
// Only transform the value if the current value is not a placeholder
|
||||||
[self setValue:value forBinding:theBinding];
|
newValue = [self transformValue:newValue withOptions:options];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
[_source setValue:newValue forKey:aBinding];
|
||||||
{
|
|
||||||
[_source setValue:aValue forKey:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
if ([_source respondsToSelector:@selector(_setCurrentValueIsPlaceholder:)])
|
||||||
{
|
[_source _setCurrentValueIsPlaceholder:isPlaceholder];
|
||||||
[_source setValue:aValue forKey:aBinding];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reverseSetValueFor:(CPString)aBinding
|
- (void)reverseSetValueFor:(CPString)aBinding
|
||||||
@@ -204,18 +187,10 @@ var CPBindingOperationAnd = 0,
|
|||||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
||||||
options = [_info objectForKey:CPOptionsKey],
|
options = [_info objectForKey:CPOptionsKey],
|
||||||
newValue = [self valueForBinding:aBinding];
|
newValue = [_source valueForKeyPath:aBinding];
|
||||||
|
|
||||||
newValue = [self reverseTransformValue:newValue withOptions:options];
|
newValue = [self reverseTransformValue:newValue withOptions:options];
|
||||||
|
|
||||||
[self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
|
|
||||||
[destination setValue:newValue forKeyPath:keyPath];
|
[destination setValue:newValue forKeyPath:keyPath];
|
||||||
[self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
return [_source valueForKeyPath:aBinding];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
||||||
@@ -223,16 +198,15 @@ var CPBindingOperationAnd = 0,
|
|||||||
if (!changes)
|
if (!changes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var objectSuppressions = _suppressedNotifications[[anObject UID]];
|
|
||||||
|
|
||||||
if (objectSuppressions && objectSuppressions[aKeyPath])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self setValueFor:context];
|
[self setValueFor:context];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)transformValue:(id)aValue withOptions:(CPDictionary)options
|
- (id)transformValue:(id)aValue withOptions:(CPDictionary)options
|
||||||
{
|
{
|
||||||
|
var valueTransformerName,
|
||||||
|
valueTransformer,
|
||||||
|
placeholder;
|
||||||
|
|
||||||
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
||||||
valueTransformer;
|
valueTransformer;
|
||||||
|
|
||||||
@@ -243,7 +217,6 @@ var CPBindingOperationAnd = 0,
|
|||||||
if (!valueTransformer)
|
if (!valueTransformer)
|
||||||
{
|
{
|
||||||
var valueTransformerClass = CPClassFromString(valueTransformerName);
|
var valueTransformerClass = CPClassFromString(valueTransformerName);
|
||||||
|
|
||||||
if (valueTransformerClass)
|
if (valueTransformerClass)
|
||||||
{
|
{
|
||||||
valueTransformer = [[valueTransformerClass alloc] init];
|
valueTransformer = [[valueTransformerClass alloc] init];
|
||||||
@@ -257,17 +230,14 @@ var CPBindingOperationAnd = 0,
|
|||||||
if (valueTransformer)
|
if (valueTransformer)
|
||||||
aValue = [valueTransformer transformedValue:aValue];
|
aValue = [valueTransformer transformedValue:aValue];
|
||||||
|
|
||||||
// If the value is nil AND the source doesn't respond to setPlaceholderString: then
|
|
||||||
// we set the value to the placeholder. Otherwise, we do not want to short cut the process
|
if (aValue === undefined || aValue === nil)
|
||||||
// of setting the placeholder that is based on the fact that the value is nil.
|
|
||||||
if ((aValue === undefined || aValue === nil || aValue === [CPNull null])
|
|
||||||
&& ![_source respondsToSelector:@selector(setPlaceholderString:)])
|
|
||||||
aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
|
aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
|
||||||
|
|
||||||
return aValue;
|
return aValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)reverseTransformValue:(id)aValue withOptions:(CPDictionary)options
|
- (id)reverseTransformValue:(id)aValue withOptions: (CPDictionary)options
|
||||||
{
|
{
|
||||||
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
||||||
valueTransformer;
|
valueTransformer;
|
||||||
@@ -283,108 +253,13 @@ var CPBindingOperationAnd = 0,
|
|||||||
return aValue;
|
return aValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)continuouslyUpdatesValue
|
|
||||||
{
|
|
||||||
var options = [_info objectForKey:CPOptionsKey];
|
|
||||||
return [[options objectForKey:CPContinuouslyUpdatesValueBindingOption] boolValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)handlesContentAsCompoundValue
|
|
||||||
{
|
|
||||||
var options = [_info objectForKey:CPOptionsKey];
|
|
||||||
return [[options objectForKey:CPHandlesContentAsCompoundValueBindingOption] boolValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Use this to avoid reacting to the notifications coming out of a reverseTransformedValue:.
|
|
||||||
*/
|
|
||||||
- (void)suppressSpecificNotificationFromObject:(id)anObject keyPath:(CPString)aKeyPath
|
|
||||||
{
|
|
||||||
if (!anObject)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var uid = [anObject UID],
|
|
||||||
objectSuppressions = _suppressedNotifications[uid];
|
|
||||||
|
|
||||||
if (!objectSuppressions)
|
|
||||||
_suppressedNotifications[uid] = objectSuppressions = {};
|
|
||||||
|
|
||||||
objectSuppressions[aKeyPath] = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Use this to cancel suppressSpecificNotificationFromObject:keyPath:.
|
|
||||||
*/
|
|
||||||
- (void)unsuppressSpecificNotificationFromObject:(id)anObject keyPath:(CPString)aKeyPath
|
|
||||||
{
|
|
||||||
if (!anObject)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var uid = [anObject UID],
|
|
||||||
objectSuppressions = _suppressedNotifications[uid];
|
|
||||||
|
|
||||||
if (!objectSuppressions)
|
|
||||||
return;
|
|
||||||
|
|
||||||
delete objectSuppressions[aKeyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
|
||||||
{
|
|
||||||
var count = [CPBinderPlaceholderMarkers count];
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
var marker = CPBinderPlaceholderMarkers[count],
|
|
||||||
optionName = CPBinderPlaceholderOptions[count],
|
|
||||||
isExplicit = [options containsKey:optionName],
|
|
||||||
placeholder = isExplicit ? [options objectForKey:optionName] : nil;
|
|
||||||
|
|
||||||
[self _setPlaceholder:placeholder forMarker:marker isDefault:!isExplicit];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[self _updatePlaceholdersWithOptions:options];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (JSObject)_placeholderForMarker:(id)aMarker
|
|
||||||
{
|
|
||||||
var placeholder = _placeholderForMarker[[aMarker UID]];
|
|
||||||
|
|
||||||
if (placeholder)
|
|
||||||
return placeholder.value;
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setPlaceholder:(id)aPlaceholder forMarker:(id)aMarker isDefault:(BOOL)isDefault
|
|
||||||
{
|
|
||||||
if (isDefault)
|
|
||||||
{
|
|
||||||
var existingPlaceholder = _placeholderForMarker[[aMarker UID]];
|
|
||||||
|
|
||||||
// Don't overwrite an explicitly set placeholder with a default.
|
|
||||||
if (existingPlaceholder && !existingPlaceholder.isDefault)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_placeholderForMarker[[aMarker UID]] = { 'isDefault': isDefault, 'value': aPlaceholder };
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPObject (KeyValueBindingCreation)
|
@implementation CPObject (KeyValueBindingCreation)
|
||||||
|
|
||||||
+ (void)exposeBinding:(CPString)aBinding
|
+ (void)exposeBinding:(CPString)aBinding
|
||||||
{
|
{
|
||||||
[CPBinder exposeBinding:aBinding forClass:[self class]];
|
[CPKeyValueBinding exposeBinding:aBinding forClass:[self class]];
|
||||||
}
|
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
return [CPBinder class];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPArray)exposedBindings
|
- (CPArray)exposedBindings
|
||||||
@@ -394,7 +269,7 @@ var CPBindingOperationAnd = 0,
|
|||||||
|
|
||||||
while (theClass)
|
while (theClass)
|
||||||
{
|
{
|
||||||
var temp = [CPBinder exposedBindingsForClass:theClass];
|
var temp = [CPKeyValueBinding exposedBindingsForClass:theClass];
|
||||||
|
|
||||||
if (temp)
|
if (temp)
|
||||||
[exposedBindings addObjectsFromArray:temp];
|
[exposedBindings addObjectsFromArray:temp];
|
||||||
@@ -413,77 +288,44 @@ var CPBindingOperationAnd = 0,
|
|||||||
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
|
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
|
||||||
{
|
{
|
||||||
if (!anObject || !aKeyPath)
|
if (!anObject || !aKeyPath)
|
||||||
return CPLog.error("Invalid object or path on " + self + " for " + aBinding);
|
return CPLog.error("Invalid object or path on "+self+" for "+aBinding);
|
||||||
|
|
||||||
//if (![[self exposedBindings] containsObject:aBinding])
|
//if (![[self exposedBindings] containsObject:aBinding])
|
||||||
// CPLog.warn("No binding exposed on " + self + " for " + aBinding);
|
// CPLog.warn("No binding exposed on "+self+" for "+aBinding);
|
||||||
|
|
||||||
var binderClass = [[self class] _binderClassForBinding:aBinding];
|
|
||||||
|
|
||||||
[self unbind:aBinding];
|
[self unbind:aBinding];
|
||||||
[[binderClass alloc] initWithBinding:[self _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self];
|
[[CPKeyValueBinding alloc] initWithBinding:[self _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPDictionary)infoForBinding:(CPString)aBinding
|
- (CPDictionary)infoForBinding:(CPString)aBinding
|
||||||
{
|
{
|
||||||
return [CPBinder infoForBinding:aBinding forObject:self];
|
return [CPKeyValueBinding infoForBinding:aBinding forObject:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)unbind:(CPString)aBinding
|
- (void)unbind:(CPString)aBinding
|
||||||
{
|
{
|
||||||
var binderClass = [[self class] _binderClassForBinding:aBinding];
|
[CPKeyValueBinding unbind:aBinding forObject:self];
|
||||||
[binderClass unbind:aBinding forObject:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPString)_replacementKeyPathForBinding:(CPString)binding
|
- (id)_replacementKeyPathForBinding:(CPString)binding
|
||||||
{
|
{
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/*!
|
@implementation _CPKeyValueOrBinding : CPKeyValueBinding
|
||||||
@ignore
|
|
||||||
Provides stub implementations that simply calls super for the "objectValue" binding.
|
|
||||||
This class should not be necessary but assures backwards compliance with our old way of doing bindings.
|
|
||||||
|
|
||||||
IMPORTANT!
|
|
||||||
Every class with a value binding should implement a subclass to handle its specific value binding logic.
|
|
||||||
*/
|
|
||||||
@implementation _CPValueBinder : CPBinder
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)theBinding
|
|
||||||
{
|
{
|
||||||
[super setValueFor:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reverseSetValueFor:(CPString)theBinding
|
|
||||||
{
|
|
||||||
[super reverseSetValueFor:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPMultipleValueBooleanBinding : CPBinder
|
|
||||||
{
|
|
||||||
CPBindingOperationKind _operation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)aBinding
|
- (void)setValueFor:(CPString)aBinding
|
||||||
{
|
{
|
||||||
var bindings = [bindingsMap valueForKey:[_source UID]];
|
var bindings = [bindingsMap valueForKey:[_source hash]];
|
||||||
|
|
||||||
if (!bindings)
|
if (!bindings)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var baseBinding = aBinding.replace(/\d$/, "");
|
[_source setValue:resolveMultipleValues(aBinding, bindings, CPBindingOperationOr) forKey:aBinding];
|
||||||
|
|
||||||
[_source setValue:[self resolveMultipleValuesForBinding:baseBinding bindings:bindings booleanOperation:_operation] forKey:baseBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reverseSetValueFor:(CPString)theBinding
|
|
||||||
{
|
|
||||||
// read-only
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
||||||
@@ -491,146 +333,61 @@ var CPBindingOperationAnd = 0,
|
|||||||
[self setValueFor:context];
|
[self setValueFor:context];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)resolveMultipleValuesForBinding:(CPString)aBinding bindings:(CPDictionary)bindings booleanOperation:(CPBindingOperationKind)operation
|
@end
|
||||||
|
|
||||||
|
@implementation _CPKeyValueAndBinding : CPKeyValueBinding
|
||||||
{
|
{
|
||||||
var bindingName = aBinding,
|
}
|
||||||
|
|
||||||
|
- (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,
|
theBinding,
|
||||||
count = 2;
|
count = 1;
|
||||||
|
|
||||||
while (theBinding = [bindings objectForKey:bindingName])
|
while (theBinding = [bindings objectForKey:bindingName])
|
||||||
{
|
{
|
||||||
var info = theBinding._info,
|
var infoDictionary = theBinding._info,
|
||||||
object = [info objectForKey:CPObservedObjectKey],
|
object = [infoDictionary objectForKey:CPObservedObjectKey],
|
||||||
keyPath = [info objectForKey:CPObservedKeyPathKey],
|
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
|
||||||
options = [info objectForKey:CPOptionsKey],
|
options = [infoDictionary objectForKey:CPOptionsKey];
|
||||||
value = [object valueForKeyPath:keyPath];
|
|
||||||
|
|
||||||
if (CPIsControllerMarker(value))
|
var value = [theBinding transformValue:[object valueForKeyPath:keyPath] withOptions:options];
|
||||||
{
|
|
||||||
[self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
|
|
||||||
value = [theBinding _placeholderForMarker:value];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
value = [theBinding transformValue:value withOptions:options];
|
|
||||||
|
|
||||||
if (operation === CPBindingOperationOr)
|
if (value == operation)
|
||||||
{
|
return operation;
|
||||||
// Any true condition means true for OR
|
|
||||||
if (value)
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any false condition means false for AND
|
bindingName = [CPString stringWithFormat:@"%@%i", key, ++count];
|
||||||
else if (!value)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
bindingName = aBinding + (count++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, all OR conditions were false or all AND conditions were true
|
return !operation;
|
||||||
return operation === CPBindingOperationOr ? NO : YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
var invokeAction = function invokeAction(/*CPString*/targetKey, /*CPString*/argumentKey, /*CPDictionary*/bindings)
|
||||||
|
|
||||||
@implementation CPMultipleValueAndBinding : _CPMultipleValueBooleanBinding
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
var theBinding = [bindings objectForKey:targetKey],
|
||||||
_operation = CPBindingOperationAnd;
|
infoDictionary = theBinding._info,
|
||||||
|
|
||||||
return self;
|
object = [infoDictionary objectForKey:CPObservedObjectKey],
|
||||||
}
|
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
|
||||||
|
options = [infoDictionary objectForKey:CPOptionsKey],
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPMultipleValueOrBinding : _CPMultipleValueBooleanBinding
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
_operation = CPBindingOperationOr;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPMultipleValueActionBinding : CPBinder
|
|
||||||
{
|
|
||||||
CPString _argumentBinding;
|
|
||||||
CPString _targetBinding;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)theBinding
|
|
||||||
{
|
|
||||||
// Called when the binding is first created
|
|
||||||
[self checkForNullBinding:theBinding initializing:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reverseSetValueFor:(CPString)theBinding
|
|
||||||
{
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
|
||||||
{
|
|
||||||
// context is the binding name
|
|
||||||
[self checkForNullBinding:context initializing:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
|
|
||||||
When the value of a multiple-value argument binding changes,
|
|
||||||
if the binding is marked not to allow null arguments, we have to check
|
|
||||||
if the binding's value is nil and disable the button if it is.
|
|
||||||
Otherwise the button is enabled.
|
|
||||||
*/
|
|
||||||
- (void)checkForNullBinding:(CPString)theBinding initializing:(BOOL)isInitializing
|
|
||||||
{
|
|
||||||
// Only done for buttons
|
|
||||||
if (![_source isKindOfClass:CPButton])
|
|
||||||
return;
|
|
||||||
|
|
||||||
// We start with the button enabled for the first argument during init,
|
|
||||||
// and subsequent checks can disable it.
|
|
||||||
if (isInitializing && theBinding === CPArgumentBinding)
|
|
||||||
[_source setEnabled:YES];
|
|
||||||
|
|
||||||
var bindings = [bindingsMap valueForKey:[_source UID]],
|
|
||||||
binding = [bindings objectForKey:theBinding],
|
|
||||||
info = binding._info,
|
|
||||||
options = [info objectForKey:CPOptionsKey];
|
|
||||||
|
|
||||||
if (![options valueForKey:CPAllowsNullArgumentBindingOption])
|
|
||||||
{
|
|
||||||
var object = [info objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [info objectForKey:CPObservedKeyPathKey],
|
|
||||||
value = [object valueForKeyPath:keyPath];
|
|
||||||
|
|
||||||
if (value === nil || value === undefined)
|
|
||||||
{
|
|
||||||
[_source setEnabled:NO];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a binding value changed and did not fail the null test, enable the button
|
|
||||||
if (!isInitializing)
|
|
||||||
[_source setEnabled:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invokeAction
|
|
||||||
{
|
|
||||||
var bindings = [bindingsMap valueForKey:[_source UID]],
|
|
||||||
theBinding = [bindings objectForKey:CPTargetBinding],
|
|
||||||
|
|
||||||
info = theBinding._info,
|
|
||||||
object = [info objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [info objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [info objectForKey:CPOptionsKey],
|
|
||||||
|
|
||||||
target = [object valueForKeyPath:keyPath],
|
target = [object valueForKeyPath:keyPath],
|
||||||
selector = [options objectForKey:CPSelectorNameBindingOption];
|
selector = [options objectForKey:CPSelectorNameBindingOption];
|
||||||
@@ -638,195 +395,28 @@ var CPBindingOperationAnd = 0,
|
|||||||
if (!target || !selector)
|
if (!target || !selector)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]],
|
var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]];
|
||||||
bindingName = CPArgumentBinding,
|
[invocation setSelector:selector];
|
||||||
|
|
||||||
|
var bindingName = argumentKey,
|
||||||
count = 1;
|
count = 1;
|
||||||
|
|
||||||
while (theBinding = [bindings objectForKey:bindingName])
|
while (theBinding = [bindings objectForKey:bindingName])
|
||||||
{
|
{
|
||||||
info = theBinding._info;
|
infoDictionary = theBinding._info;
|
||||||
object = [info objectForKey:CPObservedObjectKey];
|
|
||||||
keyPath = [info objectForKey:CPObservedKeyPathKey];
|
|
||||||
|
|
||||||
[invocation setArgument:[object valueForKeyPath:keyPath] atIndex:++count];
|
keyPath = [infoDictionary objectForKey:CPObserverKeyPathKey];
|
||||||
|
object = [[infoDictionary objectForKey:CPObservedObjectKey] valueForKeyPath:keyPath];
|
||||||
|
|
||||||
bindingName = CPArgumentBinding + count;
|
if (object)
|
||||||
|
[invocation setArgument:object atIndex:++count];
|
||||||
|
|
||||||
|
bindingName = [CPString stringWithFormat:@"%@%i", argumentKey, count];
|
||||||
}
|
}
|
||||||
|
|
||||||
[invocation setSelector:selector];
|
[invocation invoke];
|
||||||
[invocation invokeWithTarget:target];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPActionBinding : _CPMultipleValueActionBinding
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_argumentBinding = CPArgumentBinding;
|
|
||||||
_targetBinding = CPTargetBinding;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPDoubleClickActionBinding : _CPMultipleValueActionBinding
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_argumentBinding = CPArgumentBinding;
|
|
||||||
_targetBinding = CPTargetBinding;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Abstract superclass for CPValueWithPatternBinding and CPTitleWithPatternBinding.
|
|
||||||
*/
|
|
||||||
@implementation _CPPatternBinding : CPBinder
|
|
||||||
{
|
|
||||||
CPString _bindingKey;
|
|
||||||
CPString _patternPlaceholder;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var bindings = [bindingsMap valueForKey:[_source UID]];
|
|
||||||
|
|
||||||
if (!bindings)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Strip off any trailing number from the binding name
|
|
||||||
var baseBinding = aBinding.replace(/\d$/, ""),
|
|
||||||
result = [self resolveMultipleValuesForBindings:bindings];
|
|
||||||
|
|
||||||
if (result.isPlaceholder)
|
|
||||||
[self setPlaceholderValue:result.value withMarker:result.marker forBinding:baseBinding];
|
|
||||||
else
|
|
||||||
[self setValue:result.value forBinding:baseBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reverseSetValueFor:(CPString)theBinding
|
|
||||||
{
|
|
||||||
// read-only
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
|
||||||
{
|
|
||||||
[self setValueFor:context];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (JSObject)resolveMultipleValuesForBindings:(CPDictionary)bindings
|
|
||||||
{
|
|
||||||
var theBinding,
|
|
||||||
result = { value:@"", isPlaceholder:NO, marker:nil };
|
|
||||||
|
|
||||||
for (var count = 1; theBinding = [bindings objectForKey:_bindingKey + count]; ++count)
|
|
||||||
{
|
|
||||||
var info = theBinding._info,
|
|
||||||
object = [info objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [info objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [info objectForKey:CPOptionsKey],
|
|
||||||
value = [object valueForKeyPath:keyPath];
|
|
||||||
|
|
||||||
if (count === 1)
|
|
||||||
result.value = [options objectForKey:CPDisplayPatternBindingOption];
|
|
||||||
|
|
||||||
if (CPIsControllerMarker(value))
|
|
||||||
{
|
|
||||||
[self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
|
|
||||||
|
|
||||||
result.isPlaceholder = YES;
|
|
||||||
result.marker = value;
|
|
||||||
|
|
||||||
value = [theBinding _placeholderForMarker:value];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
value = [theBinding transformValue:value withOptions:options];
|
|
||||||
|
|
||||||
if (value === nil || value === undefined)
|
|
||||||
value = @"";
|
|
||||||
|
|
||||||
result.value = result.value.replace("%{" + _patternPlaceholder + count + "}@", [value description]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Users of this class must override setValue:forKey: and if the
|
|
||||||
key is CPDisplayPatternValueBinding, set the appropriate value
|
|
||||||
for the control class. For example, CPTextField uses setObjectValue.
|
|
||||||
*/
|
|
||||||
@implementation CPValueWithPatternBinding : _CPPatternBinding
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_bindingKey = CPDisplayPatternValueBinding;
|
|
||||||
_patternPlaceholder = @"value";
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Users of this class must override setValue:forKey: and if the
|
|
||||||
key is CPDisplayPatternTitleBinding, set the appropriate value
|
|
||||||
for the control class. For example, CPBox uses setTitle.
|
|
||||||
*/
|
|
||||||
@implementation CPTitleWithPatternBinding : _CPPatternBinding
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_bindingKey = CPDisplayPatternTitleBinding;
|
|
||||||
_patternPlaceholder = @"title";
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPStateMarker : CPObject
|
|
||||||
{
|
|
||||||
CPString _name;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithName:(CPString)aName
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
_name = aName
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
return "<" + _name + ">";
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
// Keys in options dictionary
|
// Keys in options dictionary
|
||||||
|
|
||||||
// Keys in dictionary returned by infoForBinding
|
// Keys in dictionary returned by infoForBinding
|
||||||
@@ -835,81 +425,48 @@ CPObservedKeyPathKey = @"CPObservedKeyPathKey";
|
|||||||
CPOptionsKey = @"CPOptionsKey";
|
CPOptionsKey = @"CPOptionsKey";
|
||||||
|
|
||||||
// special markers
|
// special markers
|
||||||
CPNoSelectionMarker = [[_CPStateMarker alloc] initWithName:@"NO SELECTION MARKER"];
|
CPMultipleValuesMarker = @"CPMultipleValuesMarker";
|
||||||
CPMultipleValuesMarker = [[_CPStateMarker alloc] initWithName:@"MULTIPLE VALUES MARKER"];
|
CPNoSelectionMarker = @"CPNoSelectionMarker";
|
||||||
CPNotApplicableMarker = [[_CPStateMarker alloc] initWithName:@"NOT APPLICABLE MARKER"];
|
CPNotApplicableMarker = @"CPNotApplicableMarker";
|
||||||
CPNullMarker = [[_CPStateMarker alloc] initWithName:@"NULL MARKER"];
|
|
||||||
|
|
||||||
// Binding name constants
|
// Binding name constants
|
||||||
CPAlignmentBinding = @"alignment";
|
CPAlignmentBinding = @"CPAlignmentBinding";
|
||||||
CPArgumentBinding = @"argument";
|
CPEditableBinding = @"CPEditableBinding";
|
||||||
CPContentArrayBinding = @"contentArray";
|
CPEnabledBinding = @"CPEnabledBinding";
|
||||||
CPContentBinding = @"content";
|
CPFontBinding = @"CPFontBinding";
|
||||||
CPContentObjectBinding = @"contentObject";
|
CPHiddenBinding = @"CPHiddenBinding";
|
||||||
CPContentObjectsBinding = @"contentObjects";
|
CPSelectedIndexBinding = @"CPSelectedIndexBinding";
|
||||||
CPContentValuesBinding = @"contentValues";
|
CPTextColorBinding = @"CPTextColorBinding";
|
||||||
CPDisplayPatternTitleBinding = @"displayPatternTitle";
|
CPToolTipBinding = @"CPToolTipBinding";
|
||||||
CPDisplayPatternValueBinding = @"displayPatternValue";
|
CPValueBinding = @"value";
|
||||||
CPDoubleClickArgumentBinding = @"doubleClickArgument";
|
|
||||||
CPDoubleClickTargetBinding = @"doubleClickTarget";
|
|
||||||
CPEditableBinding = @"editable";
|
|
||||||
CPEnabledBinding = @"enabled";
|
|
||||||
CPFontBinding = @"font";
|
|
||||||
CPFontNameBinding = @"fontName";
|
|
||||||
CPFontBoldBinding = @"fontBold";
|
|
||||||
CPHiddenBinding = @"hidden";
|
|
||||||
CPFilterPredicateBinding = @"filterPredicate";
|
|
||||||
CPMaxValueBinding = @"maxValue";
|
|
||||||
CPMinValueBinding = @"minValue";
|
|
||||||
CPPredicateBinding = @"predicate";
|
|
||||||
CPSelectedIndexBinding = @"selectedIndex";
|
|
||||||
CPSelectedLabelBinding = @"selectedLabel";
|
|
||||||
CPSelectedObjectBinding = @"selectedObject";
|
|
||||||
CPSelectedObjectsBinding = @"selectedObjects";
|
|
||||||
CPSelectedTagBinding = @"selectedTag";
|
|
||||||
CPSelectedValueBinding = @"selectedValue";
|
|
||||||
CPSelectedValuesBinding = @"selectedValues";
|
|
||||||
CPSelectionIndexesBinding = @"selectionIndexes";
|
|
||||||
CPTargetBinding = @"target";
|
|
||||||
CPTextColorBinding = @"textColor";
|
|
||||||
CPTitleBinding = @"title";
|
|
||||||
CPToolTipBinding = @"toolTip";
|
|
||||||
CPValueBinding = @"value";
|
|
||||||
CPValueURLBinding = @"valueURL";
|
|
||||||
CPValuePathBinding = @"valuePath";
|
|
||||||
CPDataBinding = @"data";
|
|
||||||
|
|
||||||
// Binding options constants
|
//Binding options constants
|
||||||
CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelection";
|
CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelectionBindingOption";
|
||||||
CPAllowsNullArgumentBindingOption = @"CPAllowsNullArgument";
|
CPAllowsNullArgumentBindingOption = @"CPAllowsNullArgumentBindingOption";
|
||||||
CPConditionallySetsEditableBindingOption = @"CPConditionallySetsEditable";
|
CPConditionallySetsEditableBindingOption = @"CPConditionallySetsEditableBindingOption";
|
||||||
CPConditionallySetsEnabledBindingOption = @"CPConditionallySetsEnabled";
|
CPConditionallySetsEnabledBindingOption = @"CPConditionallySetsEnabledBindingOption";
|
||||||
CPConditionallySetsHiddenBindingOption = @"CPConditionallySetsHidden";
|
CPConditionallySetsHiddenBindingOption = @"CPConditionallySetsHiddenBindingOption";
|
||||||
CPContinuouslyUpdatesValueBindingOption = @"CPContinuouslyUpdatesValue";
|
CPContinuouslyUpdatesValueBindingOption = @"CPContinuouslyUpdatesValueBindingOption";
|
||||||
CPCreatesSortDescriptorBindingOption = @"CPCreatesSortDescriptor";
|
CPCreatesSortDescriptorBindingOption = @"CPCreatesSortDescriptorBindingOption";
|
||||||
CPDeletesObjectsOnRemoveBindingsOption = @"CPDeletesObjectsOnRemove";
|
CPDeletesObjectsOnRemoveBindingsOption = @"CPDeletesObjectsOnRemoveBindingsOption";
|
||||||
CPDisplayNameBindingOption = @"CPDisplayName";
|
CPDisplayNameBindingOption = @"CPDisplayNameBindingOption";
|
||||||
CPDisplayPatternBindingOption = @"CPDisplayPattern";
|
CPDisplayPatternBindingOption = @"CPDisplayPatternBindingOption";
|
||||||
CPHandlesContentAsCompoundValueBindingOption = @"CPHandlesContentAsCompoundValue";
|
CPHandlesContentAsCompoundValueBindingOption = @"CPHandlesContentAsCompoundValueBindingOption";
|
||||||
CPInsertsNullPlaceholderBindingOption = @"CPInsertsNullPlaceholder";
|
CPInsertsNullPlaceholderBindingOption = @"CPInsertsNullPlaceholderBindingOption";
|
||||||
CPInvokesSeparatelyWithArrayObjectsBindingOption = @"CPInvokesSeparatelyWithArrayObjects";
|
CPInvokesSeparatelyWithArrayObjectsBindingOption = @"CPInvokesSeparatelyWithArrayObjectsBindingOption";
|
||||||
CPMultipleValuesPlaceholderBindingOption = @"CPMultipleValuesPlaceholder";
|
CPMultipleValuesPlaceholderBindingOption = @"CPMultipleValuesPlaceholderBindingOption";
|
||||||
CPNoSelectionPlaceholderBindingOption = @"CPNoSelectionPlaceholder";
|
CPNoSelectionPlaceholderBindingOption = @"CPNoSelectionPlaceholderBindingOption";
|
||||||
CPNotApplicablePlaceholderBindingOption = @"CPNotApplicablePlaceholder";
|
CPNotApplicablePlaceholderBindingOption = @"CPNotApplicablePlaceholderBindingOption";
|
||||||
CPNullPlaceholderBindingOption = @"CPNullPlaceholder";
|
CPNullPlaceholderBindingOption = @"CPNullPlaceholderBindingOption";
|
||||||
CPPredicateFormatBindingOption = @"CPPredicateFormat";
|
CPPredicateFormatBindingOption = @"CPPredicateFormatBindingOption";
|
||||||
CPRaisesForNotApplicableKeysBindingOption = @"CPRaisesForNotApplicableKeys";
|
CPRaisesForNotApplicableKeysBindingOption = @"CPRaisesForNotApplicableKeysBindingOption";
|
||||||
CPSelectorNameBindingOption = @"CPSelectorName";
|
CPSelectorNameBindingOption = @"CPSelectorNameBindingOption";
|
||||||
CPSelectsAllWhenSettingContentBindingOption = @"CPSelectsAllWhenSettingContent";
|
CPSelectsAllWhenSettingContentBindingOption = @"CPSelectsAllWhenSettingContentBindingOption";
|
||||||
CPValidatesImmediatelyBindingOption = @"CPValidatesImmediately";
|
CPValidatesImmediatelyBindingOption = @"CPValidatesImmediatelyBindingOption";
|
||||||
CPValueTransformerNameBindingOption = @"CPValueTransformerName";
|
CPValueTransformerNameBindingOption = @"CPValueTransformerNameBindingOption";
|
||||||
CPValueTransformerBindingOption = @"CPValueTransformer";
|
CPValueTransformerBindingOption = @"CPValueTransformerBindingOption";
|
||||||
|
|
||||||
CPIsControllerMarker = function(/*id*/anObject)
|
CPIsControllerMarker = function(/*id*/anObject)
|
||||||
{
|
{
|
||||||
return anObject === CPMultipleValuesMarker || anObject === CPNoSelectionMarker || anObject === CPNotApplicableMarker || anObject === CPNullMarker;
|
return anObject === CPMultipleValuesMarker || anObject === CPNoSelectionMarker || anObject === CPNotApplicableMarker;
|
||||||
};
|
}
|
||||||
|
|
||||||
var CPBinderPlaceholderMarkers = [CPMultipleValuesMarker, CPNoSelectionMarker, CPNotApplicableMarker, CPNullMarker],
|
|
||||||
CPBinderPlaceholderOptions = [CPMultipleValuesPlaceholderBindingOption, CPNoSelectionPlaceholderBindingOption, CPNotApplicablePlaceholderBindingOption, CPNullPlaceholderBindingOption];
|
|
||||||
|
|
||||||
@@ -1,367 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPLevelIndicator.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Alexander Ljungberg.
|
|
||||||
* Copyright 2011, WireLoad Inc.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "CPControl.j"
|
|
||||||
@import "CPWindow_Constants.j"
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
CPTickMarkBelow = 0;
|
|
||||||
CPTickMarkAbove = 1;
|
|
||||||
CPTickMarkLeft = CPTickMarkAbove;
|
|
||||||
CPTickMarkRight = CPTickMarkBelow;
|
|
||||||
|
|
||||||
CPRelevancyLevelIndicatorStyle = 0;
|
|
||||||
CPContinuousCapacityLevelIndicatorStyle = 1;
|
|
||||||
CPDiscreteCapacityLevelIndicatorStyle = 2;
|
|
||||||
CPRatingLevelIndicatorStyle = 3;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
@class CPLevelIndicator
|
|
||||||
|
|
||||||
CPLevelIndicator is a control which indicates a value visually on a scale.
|
|
||||||
*/
|
|
||||||
@implementation CPLevelIndicator : CPControl
|
|
||||||
{
|
|
||||||
CPLevelIndicator _levelIndicatorStyle @accessors(property=levelIndicatorStyle);
|
|
||||||
double _minValue @accessors(property=minValue);
|
|
||||||
double _maxValue @accessors(property=maxValue);
|
|
||||||
double _warningValue @accessors(property=warningValue);
|
|
||||||
double _criticalValue @accessors(property=criticalValue);
|
|
||||||
CPTickMarkPosition _tickMarkPosition @accessors(property=tickMarkPosition);
|
|
||||||
int _numberOfTickMarks @accessors(property=numberOfTickMarks);
|
|
||||||
int _numberOfMajorTickMarks @accessors(property=numberOfMajorTickMarks);
|
|
||||||
|
|
||||||
BOOL _isEditable;
|
|
||||||
|
|
||||||
BOOL _isTracking;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return "level-indicator";
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return @{
|
|
||||||
@"bezel-color": [CPNull null],
|
|
||||||
@"color-empty": [CPNull null],
|
|
||||||
@"color-normal": [CPNull null],
|
|
||||||
@"color-warning": [CPNull null],
|
|
||||||
@"color-critical": [CPNull null],
|
|
||||||
@"spacing": 1.0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_levelIndicatorStyle = CPDiscreteCapacityLevelIndicatorStyle;
|
|
||||||
_maxValue = 2;
|
|
||||||
_warningValue = 2;
|
|
||||||
_criticalValue = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
var bezelView = [self layoutEphemeralSubviewNamed:"bezel"
|
|
||||||
positioned:CPWindowBelow
|
|
||||||
relativeToEphemeralSubviewNamed:nil];
|
|
||||||
// TODO Make themable.
|
|
||||||
[bezelView setBackgroundColor:[self valueForThemeAttribute:@"bezel-color"]];
|
|
||||||
|
|
||||||
var segmentCount = _maxValue - _minValue;
|
|
||||||
|
|
||||||
if (segmentCount <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var filledColor = [self valueForThemeAttribute:@"color-normal"],
|
|
||||||
value = [self doubleValue];
|
|
||||||
|
|
||||||
if (value <= _criticalValue)
|
|
||||||
filledColor = [self valueForThemeAttribute:@"color-critical"];
|
|
||||||
else if (value <= _warningValue)
|
|
||||||
filledColor = [self valueForThemeAttribute:@"color-warning"];
|
|
||||||
|
|
||||||
for (var i = 0; i < segmentCount; i++)
|
|
||||||
{
|
|
||||||
var segmentView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
|
|
||||||
positioned:CPWindowAbove
|
|
||||||
relativeToEphemeralSubviewNamed:bezelView];
|
|
||||||
|
|
||||||
[segmentView setBackgroundColor:(_minValue + i) < value ? filledColor : [self valueForThemeAttribute:@"color-empty"]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
|
||||||
{
|
|
||||||
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
|
|
||||||
{
|
|
||||||
// TODO Put into theme attributes.
|
|
||||||
var bezelHeight = 18,
|
|
||||||
segmentHeight = 17,
|
|
||||||
bounds = CGRectCreateCopy([self bounds]);
|
|
||||||
|
|
||||||
if (aViewName == "bezel")
|
|
||||||
{
|
|
||||||
bounds.origin.y = (CGRectGetHeight(bounds) - bezelHeight) / 2.0;
|
|
||||||
bounds.size.height = bezelHeight;
|
|
||||||
return bounds;
|
|
||||||
}
|
|
||||||
else if (aViewName.indexOf("segment-bezel") === 0)
|
|
||||||
{
|
|
||||||
var segment = parseInt(aViewName.substring("segment-bezel-".length), 10),
|
|
||||||
segmentCount = _maxValue - _minValue;
|
|
||||||
|
|
||||||
if (segment >= segmentCount)
|
|
||||||
return CGRectMakeZero();
|
|
||||||
|
|
||||||
var basicSegmentWidth = bounds.size.width / segmentCount,
|
|
||||||
segmentFrame = CGRectCreateCopy([self bounds]),
|
|
||||||
spacing = [self valueForThemeAttribute:@"spacing"];
|
|
||||||
|
|
||||||
segmentFrame.origin.y = (CGRectGetHeight(bounds) - bezelHeight) / 2.0;
|
|
||||||
segmentFrame.origin.x = FLOOR(segment * basicSegmentWidth);
|
|
||||||
segmentFrame.size.width = (segment == segmentCount - 1) ? bounds.size.width - segmentFrame.origin.x : FLOOR(((segment + 1) * basicSegmentWidth)) - FLOOR((segment * basicSegmentWidth)) - spacing;
|
|
||||||
segmentFrame.size.height = segmentHeight;
|
|
||||||
|
|
||||||
return segmentFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CGRectMakeZero();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets whether or not the receiver level indicator can be edited.
|
|
||||||
*/
|
|
||||||
- (void)setEditable:(BOOL)shouldBeEditable
|
|
||||||
{
|
|
||||||
if (_isEditable === shouldBeEditable)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_isEditable = shouldBeEditable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c YES if the textfield is currently editable by the user.
|
|
||||||
*/
|
|
||||||
- (BOOL)isEditable
|
|
||||||
{
|
|
||||||
return _isEditable;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)hitTest:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
// Don't swallow clicks when displayed in a table.
|
|
||||||
if (![self isEditable])
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return [super hitTest:aPoint];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
if (![self isEditable] || ![self isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self _trackMouse:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_trackMouse:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
var type = [anEvent type];
|
|
||||||
|
|
||||||
if (type == CPLeftMouseDown || type == CPLeftMouseDragged)
|
|
||||||
{
|
|
||||||
var segmentCount = _maxValue - _minValue;
|
|
||||||
|
|
||||||
if (segmentCount <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
|
||||||
bounds = [self bounds],
|
|
||||||
oldValue = [self doubleValue],
|
|
||||||
newValue = oldValue;
|
|
||||||
|
|
||||||
// Moving the mouse outside of the widget to the left sets it
|
|
||||||
// to its minimum, and moving outside on the right sets it to
|
|
||||||
// its maximum.
|
|
||||||
if (type == CPLeftMouseDragged && location.x < 0)
|
|
||||||
{
|
|
||||||
newValue = _minValue;
|
|
||||||
}
|
|
||||||
else if (type == CPLeftMouseDragged && location.x > bounds.size.width)
|
|
||||||
{
|
|
||||||
newValue = _maxValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (var i = 0; i < segmentCount; i++)
|
|
||||||
{
|
|
||||||
var rect = [self rectForEphemeralSubviewNamed:"segment-bezel-" + i];
|
|
||||||
|
|
||||||
// Once we're tracking the mouse, we only care about horizontal mouse movement.
|
|
||||||
if (location.x >= CGRectGetMinX(rect) && location.x < CGRectGetMaxX(rect))
|
|
||||||
{
|
|
||||||
newValue = (_minValue + i + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newValue != oldValue)
|
|
||||||
[self setDoubleValue:newValue];
|
|
||||||
|
|
||||||
// Track the mouse to support click and slide value editing.
|
|
||||||
_isTracking = YES;
|
|
||||||
[CPApp setTarget:self selector:@selector(_trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
|
||||||
|
|
||||||
if ([self isContinuous])
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
}
|
|
||||||
else if (_isTracking)
|
|
||||||
{
|
|
||||||
_isTracking = NO;
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
- (CPLevelIndicatorStyle)style;
|
|
||||||
- (void)setLevelIndicatorStyle:(CPLevelIndicatorStyle)style;
|
|
||||||
*/
|
|
||||||
|
|
||||||
- (void)setMinValue:(double)minValue
|
|
||||||
{
|
|
||||||
if (_minValue === minValue)
|
|
||||||
return;
|
|
||||||
_minValue = minValue;
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMaxValue:(double)maxValue
|
|
||||||
{
|
|
||||||
if (_maxValue === maxValue)
|
|
||||||
return;
|
|
||||||
_maxValue = maxValue;
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setWarningValue:(double)warningValue;
|
|
||||||
{
|
|
||||||
if (_warningValue === warningValue)
|
|
||||||
return;
|
|
||||||
_warningValue = warningValue;
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setCriticalValue:(double)criticalValue;
|
|
||||||
{
|
|
||||||
if (_criticalValue === criticalValue)
|
|
||||||
return;
|
|
||||||
_criticalValue = criticalValue;
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
- (CPTickMarkPosition)tickMarkPosition;
|
|
||||||
- (void)setTickMarkPosition:(CPTickMarkPosition)position;
|
|
||||||
|
|
||||||
- (int)numberOfTickMarks;
|
|
||||||
- (void)setNumberOfTickMarks:(int)count;
|
|
||||||
|
|
||||||
- (int)numberOfMajorTickMarks;
|
|
||||||
- (void)setNumberOfMajorTickMarks:(int)count;
|
|
||||||
|
|
||||||
- (double)tickMarkValueAtIndex:(int)index;
|
|
||||||
- (CGRect)rectOfTickMarkAtIndex:(int)index;
|
|
||||||
*/
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPLevelIndicatorStyleKey = "CPLevelIndicatorStyleKey",
|
|
||||||
CPLevelIndicatorMinValueKey = "CPLevelIndicatorMinValueKey",
|
|
||||||
CPLevelIndicatorMaxValueKey = "CPLevelIndicatorMaxValueKey",
|
|
||||||
CPLevelIndicatorWarningValueKey = "CPLevelIndicatorWarningValueKey",
|
|
||||||
CPLevelIndicatorCriticalValueKey = "CPLevelIndicatorCriticalValueKey",
|
|
||||||
CPLevelIndicatorTickMarkPositionKey = "CPLevelIndicatorTickMarkPositionKey",
|
|
||||||
CPLevelIndicatorNumberOfTickMarksKey = "CPLevelIndicatorNumberOfTickMarksKey",
|
|
||||||
CPLevelIndicatorNumberOfMajorTickMarksKey = "CPLevelIndicatorNumberOfMajorTickMarksKey",
|
|
||||||
CPLevelIndicatorIsEditableKey = "CPLevelIndicatorIsEditableKey";
|
|
||||||
|
|
||||||
@implementation CPLevelIndicator (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_levelIndicatorStyle = [aCoder decodeIntForKey:CPLevelIndicatorStyleKey];
|
|
||||||
_minValue = [aCoder decodeDoubleForKey:CPLevelIndicatorMinValueKey];
|
|
||||||
_maxValue = [aCoder decodeDoubleForKey:CPLevelIndicatorMaxValueKey];
|
|
||||||
_warningValue = [aCoder decodeDoubleForKey:CPLevelIndicatorWarningValueKey];
|
|
||||||
_criticalValue = [aCoder decodeDoubleForKey:CPLevelIndicatorCriticalValueKey];
|
|
||||||
_tickMarkPosition = [aCoder decodeIntForKey:CPLevelIndicatorTickMarkPositionKey];
|
|
||||||
_numberOfTickMarks = [aCoder decodeIntForKey:CPLevelIndicatorNumberOfTickMarksKey];
|
|
||||||
_numberOfMajorTickMarks = [aCoder decodeIntForKey:CPLevelIndicatorNumberOfMajorTickMarksKey];
|
|
||||||
|
|
||||||
_isEditable = [aCoder decodeBoolForKey:CPLevelIndicatorIsEditableKey];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeInt:_levelIndicatorStyle forKey:CPLevelIndicatorStyleKey];
|
|
||||||
[aCoder encodeDouble:_minValue forKey:CPLevelIndicatorMinValueKey];
|
|
||||||
[aCoder encodeDouble:_maxValue forKey:CPLevelIndicatorMaxValueKey];
|
|
||||||
[aCoder encodeDouble:_warningValue forKey:CPLevelIndicatorWarningValueKey];
|
|
||||||
[aCoder encodeDouble:_criticalValue forKey:CPLevelIndicatorCriticalValueKey];
|
|
||||||
[aCoder encodeInt:_tickMarkPosition forKey:CPLevelIndicatorTickMarkPositionKey];
|
|
||||||
[aCoder encodeInt:_numberOfTickMarks forKey:CPLevelIndicatorNumberOfTickMarksKey];
|
|
||||||
[aCoder encodeInt:_numberOfMajorTickMarks forKey:CPLevelIndicatorNumberOfMajorTickMarksKey];
|
|
||||||
[aCoder encodeBool:_isEditable forKey:CPLevelIndicatorIsEditableKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+72
-215
@@ -25,10 +25,12 @@
|
|||||||
@import <Foundation/CPNotificationCenter.j>
|
@import <Foundation/CPNotificationCenter.j>
|
||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
|
|
||||||
@import "CPKeyValueBinding.j"
|
@import "_CPMenuManager.j"
|
||||||
|
@import "CPApplication.j"
|
||||||
|
@import "CPClipView.j"
|
||||||
@import "CPMenuItem.j"
|
@import "CPMenuItem.j"
|
||||||
|
@import "CPPanel.j"
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
CPMenuDidAddItemNotification = @"CPMenuDidAddItemNotification";
|
CPMenuDidAddItemNotification = @"CPMenuDidAddItemNotification";
|
||||||
CPMenuDidChangeItemNotification = @"CPMenuDidChangeItemNotification";
|
CPMenuDidChangeItemNotification = @"CPMenuDidChangeItemNotification";
|
||||||
@@ -36,8 +38,12 @@ CPMenuDidRemoveItemNotification = @"CPMenuDidRemoveItemNotification";
|
|||||||
|
|
||||||
CPMenuDidEndTrackingNotification = @"CPMenuDidEndTrackingNotification";
|
CPMenuDidEndTrackingNotification = @"CPMenuDidEndTrackingNotification";
|
||||||
|
|
||||||
|
var MENUBAR_HEIGHT = 28.0;
|
||||||
|
|
||||||
var _CPMenuBarVisible = NO,
|
var _CPMenuBarVisible = NO,
|
||||||
_CPMenuBarTitle = @"",
|
_CPMenuBarTitle = @"",
|
||||||
|
_CPMenuBarIconImage = nil,
|
||||||
|
_CPMenuBarIconImageAlphaValue = 1.0,
|
||||||
_CPMenuBarAttributes = nil,
|
_CPMenuBarAttributes = nil,
|
||||||
_CPMenuBarSharedWindow = nil;
|
_CPMenuBarSharedWindow = nil;
|
||||||
|
|
||||||
@@ -46,7 +52,7 @@ var _CPMenuBarVisible = NO,
|
|||||||
@class CPMenu
|
@class CPMenu
|
||||||
|
|
||||||
Menus provide the user with a list of actions and/or submenus. Submenus themselves are full fledged menus
|
Menus provide the user with a list of actions and/or submenus. Submenus themselves are full fledged menus
|
||||||
and so a hierarchical structure appears.
|
and so a heirarchical structure appears.
|
||||||
*/
|
*/
|
||||||
@implementation CPMenu : CPObject
|
@implementation CPMenu : CPObject
|
||||||
{
|
{
|
||||||
@@ -68,18 +74,13 @@ var _CPMenuBarVisible = NO,
|
|||||||
|
|
||||||
int _highlightedIndex;
|
int _highlightedIndex;
|
||||||
_CPMenuWindow _menuWindow;
|
_CPMenuWindow _menuWindow;
|
||||||
|
|
||||||
CPEvent _lastCloseEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managing the Menu Bar
|
// Managing the Menu Bar
|
||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self !== [CPMenu class])
|
[[self class] setMenuBarAttributes:[CPDictionary dictionary]];
|
||||||
return;
|
|
||||||
|
|
||||||
[[self class] setMenuBarAttributes:@{}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL)menuBarVisible
|
+ (BOOL)menuBarVisible
|
||||||
@@ -105,8 +106,8 @@ var _CPMenuBarVisible = NO,
|
|||||||
[_CPMenuBarSharedWindow setMenu:[CPApp mainMenu]];
|
[_CPMenuBarSharedWindow setMenu:[CPApp mainMenu]];
|
||||||
|
|
||||||
[_CPMenuBarSharedWindow setTitle:_CPMenuBarTitle];
|
[_CPMenuBarSharedWindow setTitle:_CPMenuBarTitle];
|
||||||
[_CPMenuBarSharedWindow setIconImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-icon-image" forClass:_CPMenuView]];
|
[_CPMenuBarSharedWindow setIconImage:_CPMenuBarIconImage];
|
||||||
[_CPMenuBarSharedWindow setIconImageAlphaValue:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-icon-image-alpha-value" forClass:_CPMenuView]];
|
[_CPMenuBarSharedWindow setIconImageAlphaValue:_CPMenuBarIconImageAlphaValue];
|
||||||
|
|
||||||
[_CPMenuBarSharedWindow setColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarBackgroundColor"]];
|
[_CPMenuBarSharedWindow setColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarBackgroundColor"]];
|
||||||
[_CPMenuBarSharedWindow setTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTextColor"]];
|
[_CPMenuBarSharedWindow setTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTextColor"]];
|
||||||
@@ -150,13 +151,6 @@ var _CPMenuBarVisible = NO,
|
|||||||
return _CPMenuBarImage;
|
return _CPMenuBarImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)_setOrRemoveMenuBarAttribute:(id)aValue forKey:(id)aKey
|
|
||||||
{
|
|
||||||
if (aValue === nil)
|
|
||||||
[_CPMenuBarAttributes removeObjectForKey:aKey];
|
|
||||||
else
|
|
||||||
[_CPMenuBarAttributes setObject:aValue forKey:aKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)setMenuBarAttributes:(CPDictionary)attributes
|
+ (void)setMenuBarAttributes:(CPDictionary)attributes
|
||||||
{
|
{
|
||||||
@@ -181,8 +175,8 @@ var _CPMenuBarVisible = NO,
|
|||||||
|
|
||||||
else if (!textColor && !titleColor)
|
else if (!textColor && !titleColor)
|
||||||
{
|
{
|
||||||
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-color" forClass:_CPMenuView] forKey:@"CPMenuBarTextColor"];
|
[_CPMenuBarAttributes setObject:[CPColor colorWithRed:0.051 green:0.2 blue:0.275 alpha:1.0] forKey:@"CPMenuBarTextColor"];
|
||||||
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-title-color" forClass:_CPMenuView] forKey:@"CPMenuBarTitleColor"];
|
[_CPMenuBarAttributes setObject:[CPColor colorWithRed:0.051 green:0.2 blue:0.275 alpha:1.0] forKey:@"CPMenuBarTitleColor"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!textShadowColor && titleShadowColor)
|
if (!textShadowColor && titleShadowColor)
|
||||||
@@ -193,18 +187,18 @@ var _CPMenuBarVisible = NO,
|
|||||||
|
|
||||||
else if (!textShadowColor && !titleShadowColor)
|
else if (!textShadowColor && !titleShadowColor)
|
||||||
{
|
{
|
||||||
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-shadow-color" forClass:_CPMenuView] forKey:@"CPMenuBarTextShadowColor"];
|
[_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarTextShadowColor"];
|
||||||
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-title-shadow-color" forClass:_CPMenuView] forKey:@"CPMenuBarTitleShadowColor"];
|
[_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarTitleShadowColor"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!highlightColor)
|
if (!highlightColor)
|
||||||
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-color" forClass:_CPMenuView] forKey:@"CPMenuBarHighlightColor"];
|
[_CPMenuBarAttributes setObject:[CPColor colorWithCalibratedRed:94.0/255.0 green:130.0/255.0 blue:186.0/255.0 alpha:1.0] forKey:@"CPMenuBarHighlightColor"];
|
||||||
|
|
||||||
if (!highlightTextColor)
|
if (!highlightTextColor)
|
||||||
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-color" forClass:_CPMenuView] forKey:@"CPMenuBarHighlightTextColor"];
|
[_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarHighlightTextColor"];
|
||||||
|
|
||||||
if (!highlightTextShadowColor)
|
if (!highlightTextShadowColor)
|
||||||
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-shadow-color" forClass:_CPMenuView] forKey:@"CPMenuBarHighlightTextShadowColor"];
|
[_CPMenuBarAttributes setObject:[CPColor blackColor] forKey:@"CPMenuBarHighlightTextShadowColor"];
|
||||||
|
|
||||||
if (_CPMenuBarSharedWindow)
|
if (_CPMenuBarSharedWindow)
|
||||||
{
|
{
|
||||||
@@ -233,14 +227,14 @@ var _CPMenuBarVisible = NO,
|
|||||||
- (float)menuBarHeight
|
- (float)menuBarHeight
|
||||||
{
|
{
|
||||||
if (self === [CPApp mainMenu])
|
if (self === [CPApp mainMenu])
|
||||||
return [CPMenu menuBarHeight];
|
return MENUBAR_HEIGHT;
|
||||||
|
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (float)menuBarHeight
|
+ (float)menuBarHeight
|
||||||
{
|
{
|
||||||
return [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-height" forClass:_CPMenuView];
|
return MENUBAR_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creating a CPMenu Object
|
// Creating a CPMenu Object
|
||||||
@@ -280,7 +274,22 @@ var _CPMenuBarVisible = NO,
|
|||||||
*/
|
*/
|
||||||
- (void)insertItem:(CPMenuItem)aMenuItem atIndex:(unsigned)anIndex
|
- (void)insertItem:(CPMenuItem)aMenuItem atIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
[self insertObject:aMenuItem inItemsAtIndex:anIndex];
|
var menu = [aMenuItem menu];
|
||||||
|
|
||||||
|
if (menu)
|
||||||
|
if (menu !== self)
|
||||||
|
[CPException raise:CPInternalInconsistencyException reason:@"Attempted to insert item into menu that was already in another menu."];
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
[aMenuItem setMenu:self];
|
||||||
|
[_items insertObject:aMenuItem atIndex:anIndex];
|
||||||
|
|
||||||
|
[[CPNotificationCenter defaultCenter]
|
||||||
|
postNotificationName:CPMenuDidAddItemNotification
|
||||||
|
object:self
|
||||||
|
userInfo:[CPDictionary dictionaryWithObject:anIndex forKey:@"CPMenuItemIndex"]];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -337,30 +346,16 @@ var _CPMenuBarVisible = NO,
|
|||||||
*/
|
*/
|
||||||
- (void)removeItemAtIndex:(unsigned)anIndex
|
- (void)removeItemAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
[self removeObjectFromItemsAtIndex:anIndex];
|
if (anIndex < 0 || anIndex >= _items.length)
|
||||||
}
|
return;
|
||||||
|
|
||||||
/*!
|
[_items[anIndex] setMenu:nil];
|
||||||
Removes all the menu items in the receiver.
|
[_items removeObjectAtIndex:anIndex];
|
||||||
This method is more efficient than removing menu items individually.
|
|
||||||
|
|
||||||
Unlike the other remove methods, this method does not post CPMenuDidChangeItemNotification notifications.
|
[[CPNotificationCenter defaultCenter]
|
||||||
*/
|
postNotificationName:CPMenuDidRemoveItemNotification
|
||||||
- (void)removeAllItems
|
object:self
|
||||||
{
|
userInfo:[CPDictionary dictionaryWithObject:anIndex forKey:@"CPMenuItemIndex"]];
|
||||||
var count = [_items count];
|
|
||||||
|
|
||||||
// Remove the connection to this menu in case
|
|
||||||
// someone else has a reference to the menu item.
|
|
||||||
while (count--)
|
|
||||||
[_items[count] setMenu:nil];
|
|
||||||
|
|
||||||
[self _highlightItemAtIndex:CPNotFound];
|
|
||||||
|
|
||||||
// Because we are changing _items directly, be sure to notify KVO
|
|
||||||
[self willChangeValueForKey:@"items"];
|
|
||||||
_items = [CPMutableArray array];
|
|
||||||
[self didChangeValueForKey:@"items"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -369,20 +364,13 @@ var _CPMenuBarVisible = NO,
|
|||||||
*/
|
*/
|
||||||
- (void)itemChanged:(CPMenuItem)aMenuItem
|
- (void)itemChanged:(CPMenuItem)aMenuItem
|
||||||
{
|
{
|
||||||
/*
|
if ([aMenuItem menu] != self)
|
||||||
During cib unarchiving, menu items will have a reference to their menu,
|
|
||||||
but of course the items are still being unarchived and the menu's _items
|
|
||||||
have not yet been instantiated. In that case we will not do anything here.
|
|
||||||
*/
|
|
||||||
if ([aMenuItem menu] !== self || !_items)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[aMenuItem setValue:[aMenuItem valueForKey:@"changeCount"] + 1 forKey:@"changeCount"];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
postNotificationName:CPMenuDidChangeItemNotification
|
postNotificationName:CPMenuDidChangeItemNotification
|
||||||
object:self
|
object:self
|
||||||
userInfo:@{ @"CPMenuItemIndex": [_items indexOfObjectIdenticalTo:aMenuItem] }];
|
userInfo:[CPDictionary dictionaryWithObject:[_items indexOfObjectIdenticalTo:aMenuItem] forKey:@"CPMenuItemIndex"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finding Menu Items
|
// Finding Menu Items
|
||||||
@@ -567,6 +555,7 @@ var _CPMenuBarVisible = NO,
|
|||||||
*/
|
*/
|
||||||
- (void)submenuAction:(id)aSender
|
- (void)submenuAction:(id)aSender
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -614,60 +603,11 @@ var _CPMenuBarVisible = NO,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Enables or disables the receiver’s menu items.
|
Not implemented.
|
||||||
If the item has no target, an action binding is checked. If the target does not implement
|
|
||||||
the menu item's action method or the binding's selector, the item is disabled.
|
|
||||||
If the target responds to selector validateMenuItem: or validateUserInterfaceItem: (in that order) the return value is used.
|
|
||||||
*/
|
*/
|
||||||
- (void)update
|
- (void)update
|
||||||
{
|
{
|
||||||
if (![self autoenablesItems])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var items = [self itemArray];
|
|
||||||
|
|
||||||
for (var i = 0; i < [items count]; i++)
|
|
||||||
{
|
|
||||||
var item = [items objectAtIndex:i];
|
|
||||||
|
|
||||||
if ([item hasSubmenu])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var validator = [CPApp targetForAction:[item action] to:[item target] from:item];
|
|
||||||
|
|
||||||
if (!validator)
|
|
||||||
{
|
|
||||||
// If targetForAction: returns nil, it could be that there is no action.
|
|
||||||
// If there is an action and nil is returned, no valid target could be found.
|
|
||||||
if ([item action] || [item target])
|
|
||||||
[item setEnabled:NO];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Check to see if there is a target binding with an invalid selector
|
|
||||||
var info = [CPBinder infoForBinding:CPTargetBinding forObject:item];
|
|
||||||
|
|
||||||
if (info)
|
|
||||||
{
|
|
||||||
var object = [info objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [info objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [info objectForKey:CPOptionsKey],
|
|
||||||
target = [object valueForKeyPath:keyPath],
|
|
||||||
selector = [options valueForKey:CPSelectorNameBindingOption];
|
|
||||||
|
|
||||||
if (target && selector && ![target respondsToSelector:CPSelectorFromString(selector)])
|
|
||||||
[item setEnabled:NO];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (![validator respondsToSelector:[item action]])
|
|
||||||
[item setEnabled:NO];
|
|
||||||
else if ([validator respondsToSelector:@selector(validateMenuItem:)])
|
|
||||||
[item setEnabled:[validator validateMenuItem:item]];
|
|
||||||
else if ([validator respondsToSelector:@selector(validateUserInterfaceItem:)])
|
|
||||||
[item setEnabled:[validator validateUserInterfaceItem:item]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[[_menuWindow _menuView] tile];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managing the Title
|
// Managing the Title
|
||||||
@@ -709,13 +649,7 @@ var _CPMenuBarVisible = NO,
|
|||||||
// highlightedItem is always enabled. Do there exist edge cases: disabling on closing a menu,
|
// highlightedItem is always enabled. Do there exist edge cases: disabling on closing a menu,
|
||||||
// etc.? Requires further investigation and tests.
|
// etc.? Requires further investigation and tests.
|
||||||
if (highlightedItem && [highlightedItem isEnabled])
|
if (highlightedItem && [highlightedItem isEnabled])
|
||||||
{
|
|
||||||
// Perform any action binding
|
|
||||||
var binding = [CPBinder getBinding:CPTargetBinding forObject:highlightedItem];
|
|
||||||
[binding invokeAction];
|
|
||||||
|
|
||||||
[CPApp sendAction:[highlightedItem action] to:[highlightedItem target] from:highlightedItem];
|
[CPApp sendAction:[highlightedItem action] to:[highlightedItem target] from:highlightedItem];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -755,7 +689,10 @@ var _CPMenuBarVisible = NO,
|
|||||||
if (aView && !theWindow)
|
if (aView && !theWindow)
|
||||||
throw "In call to popUpMenuPositioningItem:atLocation:inView:callback:, view is not in any window.";
|
throw "In call to popUpMenuPositioningItem:atLocation:inView:callback:, view is not in any window.";
|
||||||
|
|
||||||
[self _menuWillOpen];
|
var delegate = [self delegate];
|
||||||
|
|
||||||
|
if ([delegate respondsToSelector:@selector(menuWillOpen:)])
|
||||||
|
[delegate menuWillOpen:aMenu];
|
||||||
|
|
||||||
// Convert location to global coordinates if not already in them.
|
// Convert location to global coordinates if not already in them.
|
||||||
if (aView)
|
if (aView)
|
||||||
@@ -843,16 +780,17 @@ var _CPMenuBarVisible = NO,
|
|||||||
|
|
||||||
+ (void)popUpContextMenu:(CPMenu)aMenu withEvent:(CPEvent)anEvent forView:(CPView)aView withFont:(CPFont)aFont
|
+ (void)popUpContextMenu:(CPMenu)aMenu withEvent:(CPEvent)anEvent forView:(CPView)aView withFont:(CPFont)aFont
|
||||||
{
|
{
|
||||||
[aMenu _menuWillOpen];
|
var delegate = [aMenu delegate];
|
||||||
|
|
||||||
|
if ([delegate respondsToSelector:@selector(menuWillOpen:)])
|
||||||
|
[delegate menuWillOpen:aMenu];
|
||||||
|
|
||||||
if (!aFont)
|
if (!aFont)
|
||||||
aFont = [CPFont systemFontOfSize:[CPFont systemFontSize]];
|
aFont = [CPFont systemFontOfSize:12.0];
|
||||||
|
|
||||||
var theWindow = [aView window],
|
var theWindow = [aView window],
|
||||||
menuWindow = [_CPMenuWindow menuWindowWithMenu:aMenu font:aFont];
|
menuWindow = [_CPMenuWindow menuWindowWithMenu:aMenu font:aFont];
|
||||||
|
|
||||||
[_CPMenuWindow poolMenuWindow:menuWindow];
|
|
||||||
|
|
||||||
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
||||||
|
|
||||||
var constraintRect = [CPMenu _constraintRectForView:aView],
|
var constraintRect = [CPMenu _constraintRectForView:aView],
|
||||||
@@ -915,15 +853,7 @@ var _CPMenuBarVisible = NO,
|
|||||||
*/
|
*/
|
||||||
- (CPMenuItem)highlightedItem
|
- (CPMenuItem)highlightedItem
|
||||||
{
|
{
|
||||||
if (_highlightedIndex < 0)
|
return _highlightedIndex >= 0 ? _items[_highlightedIndex] : nil;
|
||||||
return nil;
|
|
||||||
|
|
||||||
var highlightedItem = _items[_highlightedIndex];
|
|
||||||
|
|
||||||
if ([highlightedItem isSeparatorItem])
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return highlightedItem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managing the Delegate
|
// Managing the Delegate
|
||||||
@@ -938,29 +868,9 @@ var _CPMenuBarVisible = NO,
|
|||||||
return _delegate;
|
return _delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_menuWillOpen
|
|
||||||
{
|
|
||||||
var delegate = [self delegate];
|
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(menuWillOpen:)])
|
|
||||||
[delegate menuWillOpen:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_menuDidClose
|
|
||||||
{
|
|
||||||
// Remember which event caused this menu to close, if any. CPPopUpButton uses this to detect
|
|
||||||
// when a click on the button itself caused the menu to close.
|
|
||||||
_lastCloseEvent = [CPApp currentEvent];
|
|
||||||
|
|
||||||
var delegate = [self delegate];
|
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(menuDidClose:)])
|
|
||||||
[delegate menuDidClose:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling Tracking
|
// Handling Tracking
|
||||||
/*!
|
/*!
|
||||||
Cancels tracking.
|
Cancels tracking.
|
||||||
*/
|
*/
|
||||||
- (void)cancelTracking
|
- (void)cancelTracking
|
||||||
{
|
{
|
||||||
@@ -971,7 +881,7 @@ var _CPMenuBarVisible = NO,
|
|||||||
{
|
{
|
||||||
[CPApp sendEvent:[CPEvent
|
[CPApp sendEvent:[CPEvent
|
||||||
otherEventWithType:CPAppKitDefined
|
otherEventWithType:CPAppKitDefined
|
||||||
location:CGPointMakeZero()
|
location:_CGPointMakeZero()
|
||||||
modifierFlags:0
|
modifierFlags:0
|
||||||
timestamp:0
|
timestamp:0
|
||||||
windowNumber:0
|
windowNumber:0
|
||||||
@@ -1020,9 +930,10 @@ var _CPMenuBarVisible = NO,
|
|||||||
characters = [anEvent charactersIgnoringModifiers],
|
characters = [anEvent charactersIgnoringModifiers],
|
||||||
modifierFlags = [anEvent modifierFlags];
|
modifierFlags = [anEvent modifierFlags];
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for(; index < count; ++index)
|
||||||
{
|
{
|
||||||
var item = _items[index];
|
var item = _items[index],
|
||||||
|
modifierMask = [item keyEquivalentModifierMask];
|
||||||
|
|
||||||
if ([anEvent _triggersKeyEquivalent:[item keyEquivalent] withModifierMask:[item keyEquivalentModifierMask]])
|
if ([anEvent _triggersKeyEquivalent:[item keyEquivalent] withModifierMask:[item keyEquivalentModifierMask]])
|
||||||
{
|
{
|
||||||
@@ -1056,6 +967,14 @@ var _CPMenuBarVisible = NO,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
/*
|
||||||
|
@ignore
|
||||||
|
*/
|
||||||
|
- (BOOL)_itemIsHighlighted:(CPMenuItem)aMenuItem
|
||||||
|
{
|
||||||
|
return _items[_highlightedIndex] == aMenuItem;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ignore
|
@ignore
|
||||||
*/
|
*/
|
||||||
@@ -1071,9 +990,6 @@ var _CPMenuBarVisible = NO,
|
|||||||
|
|
||||||
if (_highlightedIndex !== CPNotFound)
|
if (_highlightedIndex !== CPNotFound)
|
||||||
[[_items[_highlightedIndex] _menuItemView] highlight:YES];
|
[[_items[_highlightedIndex] _menuItemView] highlight:YES];
|
||||||
|
|
||||||
if (_highlightedIndex !== CPNotFound && _menuWindow)
|
|
||||||
[_menuWindow._menuView scrollRectToVisible:[[_items[_highlightedIndex] _menuItemView] frame]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_setMenuName:(CPString)aName
|
- (void)_setMenuName:(CPString)aName
|
||||||
@@ -1119,63 +1035,6 @@ var _CPMenuBarVisible = NO,
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPMenu (CPKeyValueCoding)
|
|
||||||
|
|
||||||
- (CPUInteger)countOfItems
|
|
||||||
{
|
|
||||||
return [_items count];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPMenuItem)objectInItemsAtIndex:(CPUInteger)anIndex
|
|
||||||
{
|
|
||||||
return [_items objectAtIndex:anIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)itemsAtIndexes:(CPIndexSet)indexes
|
|
||||||
{
|
|
||||||
return [_items objectsAtIndexes:indexes];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPMenu (CPKeyValueObserving)
|
|
||||||
|
|
||||||
- (void)insertObject:(CPMenuItem)aMenuItem inItemsAtIndex:(CPUInteger)anIndex
|
|
||||||
{
|
|
||||||
var menu = [aMenuItem menu];
|
|
||||||
|
|
||||||
if (menu)
|
|
||||||
if (menu !== self)
|
|
||||||
[CPException raise:CPInternalInconsistencyException reason:@"Attempted to insert item into menu that was already in another menu."];
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
|
|
||||||
[aMenuItem setMenu:self];
|
|
||||||
[self _highlightItemAtIndex:CPNotFound];
|
|
||||||
[_items insertObject:aMenuItem atIndex:anIndex];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
|
||||||
postNotificationName:CPMenuDidAddItemNotification
|
|
||||||
object:self
|
|
||||||
userInfo:@{ @"CPMenuItemIndex": anIndex }];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObjectFromItemsAtIndex:(CPUInteger)anIndex
|
|
||||||
{
|
|
||||||
if (anIndex < 0 || anIndex >= [_items count])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[[_items objectAtIndex:anIndex] setMenu:nil];
|
|
||||||
[self _highlightItemAtIndex:CPNotFound];
|
|
||||||
[_items removeObjectAtIndex:anIndex];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
|
||||||
postNotificationName:CPMenuDidRemoveItemNotification
|
|
||||||
object:self
|
|
||||||
userInfo:@{ @"CPMenuItemIndex": anIndex }];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPMenuTitleKey = @"CPMenuTitleKey",
|
var CPMenuTitleKey = @"CPMenuTitleKey",
|
||||||
CPMenuNameKey = @"CPMenuNameKey",
|
CPMenuNameKey = @"CPMenuNameKey",
|
||||||
@@ -1202,8 +1061,6 @@ var CPMenuTitleKey = @"CPMenuTitleKey",
|
|||||||
|
|
||||||
_showsStateColumn = ![aCoder containsValueForKey:CPMenuShowsStateColumnKey] || [aCoder decodeBoolForKey:CPMenuShowsStateColumnKey];
|
_showsStateColumn = ![aCoder containsValueForKey:CPMenuShowsStateColumnKey] || [aCoder decodeBoolForKey:CPMenuShowsStateColumnKey];
|
||||||
|
|
||||||
_autoenablesItems = YES;
|
|
||||||
|
|
||||||
[self setMinimumWidth:0];
|
[self setMinimumWidth:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,14 @@
|
|||||||
/*
|
|
||||||
* _CPMenuBarWindow.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 "CPPanel.j"
|
@import "CPPanel.j"
|
||||||
@import "_CPMenuManager.j"
|
@import "_CPMenuWindow.j"
|
||||||
|
|
||||||
@class _CPMenuView
|
|
||||||
|
|
||||||
@global CPMenuDidAddItemNotification
|
var MENUBAR_HEIGHT = 28.0,
|
||||||
@global CPMenuDidChangeItemNotification
|
MENUBAR_MARGIN = 10.0,
|
||||||
@global CPMenuDidRemoveItemNotification
|
MENUBAR_LEFT_MARGIN = 10.0,
|
||||||
|
MENUBAR_RIGHT_MARGIN = 10.0;
|
||||||
|
|
||||||
|
var _CPMenuBarWindowBackgroundColor = nil,
|
||||||
|
_CPMenuBarWindowFont = nil;
|
||||||
|
|
||||||
@implementation _CPMenuBarWindow : CPPanel
|
@implementation _CPMenuBarWindow : CPPanel
|
||||||
{
|
{
|
||||||
@@ -51,9 +32,19 @@
|
|||||||
CPColor _highlightTextShadowColor;
|
CPColor _highlightTextShadowColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (void)initialize
|
||||||
|
{
|
||||||
|
if (self != [_CPMenuBarWindow class])
|
||||||
|
return;
|
||||||
|
|
||||||
|
var bundle = [CPBundle bundleForClass:self];
|
||||||
|
|
||||||
|
_CPMenuBarWindowFont = [CPFont boldSystemFontOfSize:12.0];
|
||||||
|
}
|
||||||
|
|
||||||
+ (CPFont)font
|
+ (CPFont)font
|
||||||
{
|
{
|
||||||
return [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-font" forClass:_CPMenuView];
|
return _CPMenuBarWindowFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
@@ -61,14 +52,12 @@
|
|||||||
// This only shows up in browser land, so don't bother calculating metrics in desktop.
|
// This only shows up in browser land, so don't bother calculating metrics in desktop.
|
||||||
var contentRect = [[CPPlatformWindow primaryPlatformWindow] contentBounds];
|
var contentRect = [[CPPlatformWindow primaryPlatformWindow] contentBounds];
|
||||||
|
|
||||||
contentRect.size.height = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-height" forClass:_CPMenuView];
|
contentRect.size.height = MENUBAR_HEIGHT;
|
||||||
|
|
||||||
self = [super initWithContentRect:contentRect styleMask:CPBorderlessWindowMask];
|
self = [super initWithContentRect:contentRect styleMask:CPBorderlessWindowMask];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_constrainsToUsableScreen = NO;
|
|
||||||
|
|
||||||
[self setLevel:CPMainMenuWindowLevel];
|
[self setLevel:CPMainMenuWindowLevel];
|
||||||
[self setAutoresizingMask:CPWindowWidthSizable];
|
[self setAutoresizingMask:CPWindowWidthSizable];
|
||||||
|
|
||||||
@@ -85,7 +74,7 @@
|
|||||||
|
|
||||||
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
[_titleField setFont:[CPFont boldSystemFontOfSize:[CPFont systemFontSize] + 1]];
|
[_titleField setFont:[CPFont boldSystemFontOfSize:13.0]];
|
||||||
[_titleField setAlignment:CPCenterTextAlignment];
|
[_titleField setAlignment:CPCenterTextAlignment];
|
||||||
[_titleField setTextShadowOffset:CGSizeMake(0, 1)];
|
[_titleField setTextShadowOffset:CGSizeMake(0, 1)];
|
||||||
|
|
||||||
@@ -130,7 +119,12 @@
|
|||||||
- (void)setColor:(CPColor)aColor
|
- (void)setColor:(CPColor)aColor
|
||||||
{
|
{
|
||||||
if (!aColor)
|
if (!aColor)
|
||||||
[[self contentView] setBackgroundColor:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-background-color" forClass:_CPMenuView]];
|
{
|
||||||
|
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
|
else
|
||||||
[[self contentView] setBackgroundColor:aColor];
|
[[self contentView] setBackgroundColor:aColor];
|
||||||
}
|
}
|
||||||
@@ -143,7 +137,6 @@
|
|||||||
_textColor = aColor;
|
_textColor = aColor;
|
||||||
|
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(setTextColor:) withObject:_textColor];
|
[_menuItemViews makeObjectsPerformSelector:@selector(setTextColor:) withObject:_textColor];
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuTextColor:) withObject:_textColor];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setTitleColor:(CPColor)aColor
|
- (void)setTitleColor:(CPColor)aColor
|
||||||
@@ -164,7 +157,6 @@
|
|||||||
_textShadowColor = aColor;
|
_textShadowColor = aColor;
|
||||||
|
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(setTextShadowColor:) withObject:_textShadowColor];
|
[_menuItemViews makeObjectsPerformSelector:@selector(setTextShadowColor:) withObject:_textShadowColor];
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuTextShadowColor:) withObject:_textShadowColor];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setTitleShadowColor:(CPColor)aColor
|
- (void)setTitleShadowColor:(CPColor)aColor
|
||||||
@@ -183,8 +175,6 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_highlightColor = aColor;
|
_highlightColor = aColor;
|
||||||
|
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuHighlightColor:) withObject:_highlightColor];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setHighlightTextColor:(CPColor)aColor
|
- (void)setHighlightTextColor:(CPColor)aColor
|
||||||
@@ -194,7 +184,7 @@
|
|||||||
|
|
||||||
_highlightTextColor = aColor;
|
_highlightTextColor = aColor;
|
||||||
|
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuHighlightTextColor:) withObject:_highlightTextColor];
|
// [_menuItemViews makeObjectsPerformSelector:@selector(setActivateColor:) withObject:_highlightTextColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setHighlightTextShadowColor:(CPColor)aColor
|
- (void)setHighlightTextShadowColor:(CPColor)aColor
|
||||||
@@ -204,7 +194,7 @@
|
|||||||
|
|
||||||
_highlightTextShadowColor = aColor;
|
_highlightTextShadowColor = aColor;
|
||||||
|
|
||||||
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuHighlightTextShadowColor:) withObject:_highlightTextShadowColor];
|
// [_menuItemViews makeObjectsPerformSelector:@selector(setActivateShadowColor:) withObject:_highlightTextShadowColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setMenu:(CPMenu)aMenu
|
- (void)setMenu:(CPMenu)aMenu
|
||||||
@@ -267,7 +257,7 @@
|
|||||||
items = [_menu itemArray],
|
items = [_menu itemArray],
|
||||||
count = items.length;
|
count = items.length;
|
||||||
|
|
||||||
for (var index = 0; index < count; ++index)
|
for (index = 0; index < count; ++index)
|
||||||
{
|
{
|
||||||
var item = items[index],
|
var item = items[index],
|
||||||
menuItemView = [item _menuItemView];
|
menuItemView = [item _menuItemView];
|
||||||
@@ -326,11 +316,6 @@
|
|||||||
[self tile];
|
[self tile];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var constraintRect = CGRectInset([[self platformWindow] visibleFrame], 5.0, 0.0);
|
var constraintRect = CGRectInset([[self platformWindow] visibleFrame], 5.0, 0.0);
|
||||||
@@ -350,7 +335,7 @@
|
|||||||
|
|
||||||
- (CPFont)font
|
- (CPFont)font
|
||||||
{
|
{
|
||||||
[CPFont systemFontOfSize:[CPFont systemFontSize]];
|
[CPFont systemFontOfSize:12.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)tile
|
- (void)tile
|
||||||
@@ -359,7 +344,7 @@
|
|||||||
index = 0,
|
index = 0,
|
||||||
count = items.length,
|
count = items.length,
|
||||||
|
|
||||||
x = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-left-margin" forClass:_CPMenuView],
|
x = MENUBAR_LEFT_MARGIN,
|
||||||
y = 0.0,
|
y = 0.0,
|
||||||
isLeftAligned = YES;
|
isLeftAligned = YES;
|
||||||
|
|
||||||
@@ -369,7 +354,7 @@
|
|||||||
|
|
||||||
if ([item isSeparatorItem])
|
if ([item isSeparatorItem])
|
||||||
{
|
{
|
||||||
x = CGRectGetWidth([self frame]) - [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-right-margin" forClass:_CPMenuView];
|
x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN;
|
||||||
isLeftAligned = NO;
|
isLeftAligned = NO;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -383,13 +368,13 @@
|
|||||||
|
|
||||||
if (isLeftAligned)
|
if (isLeftAligned)
|
||||||
{
|
{
|
||||||
[menuItemView setFrame:CGRectMake(x, 0.0, CGRectGetWidth(frame), [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-height" forClass:_CPMenuView])];
|
[menuItemView setFrame:CGRectMake(x, 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
|
||||||
|
|
||||||
x += CGRectGetWidth([menuItemView frame]);
|
x += CGRectGetWidth([menuItemView frame]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[menuItemView setFrame:CGRectMake(x - CGRectGetWidth(frame), 0.0, CGRectGetWidth(frame), [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-height" forClass:_CPMenuView])];
|
[menuItemView setFrame:CGRectMake(x - CGRectGetWidth(frame), 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
|
||||||
|
|
||||||
x = CGRectGetMinX([menuItemView frame]);
|
x = CGRectGetMinX([menuItemView frame]);
|
||||||
}
|
}
|
||||||
@@ -417,7 +402,7 @@
|
|||||||
|
|
||||||
[super setFrame:aRect display:shouldDisplay animate:shouldAnimate];
|
[super setFrame:aRect display:shouldDisplay animate:shouldAnimate];
|
||||||
|
|
||||||
if (!CGSizeEqualToSize(size, aRect.size))
|
if (!_CGSizeEqualToSize(size, aRect.size))
|
||||||
[self tile];
|
[self tile];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,3 +452,102 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@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
|
||||||
|
|||||||
+57
-459
@@ -1,52 +1,17 @@
|
|||||||
/*
|
|
||||||
* _CPMenuManager.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Francisco Tolmasky.
|
|
||||||
* Copyright 2009, 280 North, Inc.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPNotificationCenter.j>
|
|
||||||
|
|
||||||
@import "CPEvent.j"
|
|
||||||
@import "CPKeyBinding.j"
|
|
||||||
|
|
||||||
@class CPWindow
|
|
||||||
@class _CPMenuWindow
|
|
||||||
|
|
||||||
|
|
||||||
@global CPApp
|
_CPMenuManagerScrollingStateUp = -1,
|
||||||
@global CPMenuDidEndTrackingNotification
|
_CPMenuManagerScrollingStateDown = 1,
|
||||||
@global _CPMenuWindowMenuBarBackgroundStyle
|
|
||||||
@global _CPMenuWindowPopUpBackgroundStyle
|
|
||||||
|
|
||||||
_CPMenuManagerScrollingStateUp = -1;
|
|
||||||
_CPMenuManagerScrollingStateDown = 1;
|
|
||||||
_CPMenuManagerScrollingStateNone = 0;
|
_CPMenuManagerScrollingStateNone = 0;
|
||||||
|
|
||||||
var STICKY_TIME_INTERVAL = 0.4,
|
var STICKY_TIME_INTERVAL = 500;
|
||||||
SharedMenuManager = nil;
|
|
||||||
|
var SharedMenuManager = nil;
|
||||||
|
|
||||||
@implementation _CPMenuManager: CPObject
|
@implementation _CPMenuManager: CPObject
|
||||||
{
|
{
|
||||||
CPTimeInterval _startTime;
|
CPTimeInterval _startTime;
|
||||||
CPEvent _openEvent;
|
|
||||||
BOOL _mouseWasDragged;
|
|
||||||
int _scrollingState;
|
int _scrollingState;
|
||||||
CGPoint _lastGlobalLocation;
|
CGPoint _lastGlobalLocation;
|
||||||
|
|
||||||
@@ -56,13 +21,6 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
CPMutableArray _menuContainerStack;
|
CPMutableArray _menuContainerStack;
|
||||||
|
|
||||||
Function _trackingCallback;
|
Function _trackingCallback;
|
||||||
|
|
||||||
CPString _keyBuffer;
|
|
||||||
|
|
||||||
CPMenuItem _previousActiveItem;
|
|
||||||
int _showTimerID;
|
|
||||||
|
|
||||||
int _menuBarButtonItemIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (_CPMenuManager)sharedMenuManager
|
+ (_CPMenuManager)sharedMenuManager
|
||||||
@@ -98,13 +56,9 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
{
|
{
|
||||||
var menu = [aMenuContainer menu];
|
var menu = [aMenuContainer menu];
|
||||||
|
|
||||||
if ([menu numberOfItems] <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
CPApp._activeMenu = menu;
|
CPApp._activeMenu = menu;
|
||||||
|
|
||||||
_startTime = [anEvent timestamp];
|
_startTime = [anEvent timestamp];//new Date();
|
||||||
_openEvent = anEvent;
|
|
||||||
_scrollingState = _CPMenuManagerScrollingStateNone;
|
_scrollingState = _CPMenuManagerScrollingStateNone;
|
||||||
|
|
||||||
_constraintRect = aRect;
|
_constraintRect = aRect;
|
||||||
@@ -113,32 +67,25 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
|
|
||||||
if (menu === [CPApp mainMenu])
|
if (menu === [CPApp mainMenu])
|
||||||
{
|
{
|
||||||
var globalLocation = [anEvent globalLocation],
|
var globalLocation = [anEvent globalLocation];
|
||||||
|
|
||||||
// Find which menu window the mouse is currently on top of
|
// Find which menu window the mouse is currently on top of
|
||||||
menuLocation = [aMenuContainer convertGlobalToBase:globalLocation],
|
var menuLocation = [aMenuContainer convertGlobalToBase:globalLocation];
|
||||||
|
|
||||||
// Find out the item the mouse is currently on top of
|
// Find out the item the mouse is currently on top of
|
||||||
activeItemIndex = [aMenuContainer itemIndexAtPoint:menuLocation],
|
var activeItemIndex = [aMenuContainer itemIndexAtPoint:menuLocation],
|
||||||
activeItem = activeItemIndex !== CPNotFound ? [menu itemAtIndex:activeItemIndex] : nil;
|
activeItem = activeItemIndex !== CPNotFound ? [menu itemAtIndex:activeItemIndex] : nil;
|
||||||
|
|
||||||
_menuBarButtonItemIndex = activeItemIndex;
|
_menuBarButtonItemIndex = activeItemIndex;
|
||||||
// _menuBarButtonMenuContainer = aMenuContainer;
|
_menuBarButtonMenuContainer = aMenuContainer;
|
||||||
|
|
||||||
if ([activeItem _isMenuBarButton])
|
if ([activeItem _isMenuBarButton])
|
||||||
return [self trackMenuBarButtonEvent:anEvent];
|
return [self trackMenuBarButtonEvent:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
_mouseWasDragged = NO;
|
|
||||||
|
|
||||||
[self trackEvent:anEvent];
|
[self trackEvent:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_trackAgain
|
|
||||||
{
|
|
||||||
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPKeyDownMask | CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseDownMask | CPLeftMouseUpMask | CPRightMouseUpMask | CPAppKitDefinedMask | CPScrollWheelMask untilDate:nil inMode:nil dequeue:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)trackEvent:(CPEvent)anEvent
|
- (void)trackEvent:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var type = [anEvent type],
|
var type = [anEvent type],
|
||||||
@@ -148,80 +95,23 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
if (type === CPAppKitDefined)
|
if (type === CPAppKitDefined)
|
||||||
return [self completeTracking];
|
return [self completeTracking];
|
||||||
|
|
||||||
if (type === CPKeyDown)
|
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES];
|
||||||
{
|
|
||||||
var menu = trackingMenu,
|
|
||||||
submenu = [[menu highlightedItem] submenu];
|
|
||||||
|
|
||||||
// get the current active menu
|
|
||||||
while (submenu && [submenu._menuWindow isVisible])
|
|
||||||
{
|
|
||||||
menu = submenu;
|
|
||||||
submenu = [[menu highlightedItem] submenu];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([menu numberOfItems])
|
|
||||||
[self interpretKeyEvent:anEvent forMenu:menu];
|
|
||||||
|
|
||||||
[self _trackAgain];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Periodic events don't have a valid location.
|
// Periodic events don't have a valid location.
|
||||||
var globalLocation = type === CPPeriodic ? _lastGlobalLocation : [anEvent globalLocation];
|
var globalLocation = type === CPPeriodic ? _lastGlobalLocation : [anEvent globalLocation];
|
||||||
|
|
||||||
if (!globalLocation)
|
// Remember this for the next periodic event.
|
||||||
{
|
_lastGlobalLocation = globalLocation;
|
||||||
[self _trackAgain];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find which menu window the mouse is currently on top of
|
// Find which menu window the mouse is currently on top of
|
||||||
var activeMenuContainer = [self menuContainerForPoint:globalLocation],
|
var activeMenuContainer = [self menuContainerForPoint:globalLocation],
|
||||||
activeMenu = [activeMenuContainer menu],
|
activeMenu = [activeMenuContainer menu],
|
||||||
menuLocation = [activeMenuContainer convertGlobalToBase:globalLocation],
|
menuLocation = [activeMenuContainer convertGlobalToBase:globalLocation];
|
||||||
|
|
||||||
// Find out the item the mouse is currently on top of
|
// Find out the item the mouse is currently on top of
|
||||||
activeItemIndex = activeMenuContainer ? [activeMenuContainer itemIndexAtPoint:menuLocation] : CPNotFound,
|
var activeItemIndex = activeMenuContainer ? [activeMenuContainer itemIndexAtPoint:menuLocation] : CPNotFound,
|
||||||
activeItem = activeItemIndex !== CPNotFound ? [activeMenu itemAtIndex:activeItemIndex] : nil;
|
activeItem = activeItemIndex !== CPNotFound ? [activeMenu itemAtIndex:activeItemIndex] : nil;
|
||||||
|
|
||||||
/*
|
|
||||||
Click outside the menu structure?
|
|
||||||
|
|
||||||
The event which caused the menu to open is not considered even if it's
|
|
||||||
technically a click outside of an open menu, since this would mean the same
|
|
||||||
click which opened the menu could also close it. This is actually common.
|
|
||||||
E.g. you click on a button and it opens a menu below itself.
|
|
||||||
*/
|
|
||||||
if (type === CPLeftMouseDown && _openEvent !== anEvent && (!activeMenuContainer || !CGRectContainsPoint([activeMenuContainer globalFrame], globalLocation)))
|
|
||||||
{
|
|
||||||
[self completeTracking];
|
|
||||||
|
|
||||||
/*
|
|
||||||
You can close and interact with a control in a single click. E.g. you can have a menu open,
|
|
||||||
click on a button outside of it and have the menu immediately close and the button activate,
|
|
||||||
without having to click once to close the menu and once to activate the button.
|
|
||||||
|
|
||||||
Since we normally dequeue the event after tracking it, we'll have to put it back on the stack
|
|
||||||
in this special case. Note that it's important the event is executed /right now/, since certain
|
|
||||||
controls such as HTML upload buttons need a native click event at the top of the stack trace
|
|
||||||
to activate - it's not something we can fake later.
|
|
||||||
*/
|
|
||||||
[CPApp sendEvent:anEvent];
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self _trackAgain];
|
|
||||||
|
|
||||||
// unhighlight when mouse is moved off the menu
|
|
||||||
if (_lastGlobalLocation && CGRectContainsPoint([activeMenuContainer globalFrame], _lastGlobalLocation)
|
|
||||||
&& !CGRectContainsPoint([activeMenuContainer globalFrame], globalLocation))
|
|
||||||
[activeMenu _highlightItemAtIndex:CPNotFound];
|
|
||||||
|
|
||||||
// Remember this for the next periodic event.
|
|
||||||
_lastGlobalLocation = globalLocation;
|
|
||||||
|
|
||||||
// If the item isn't enabled its as if we clicked on nothing.
|
// If the item isn't enabled its as if we clicked on nothing.
|
||||||
if (![activeItem isEnabled] || [activeItem _isMenuBarButton])
|
if (![activeItem isEnabled] || [activeItem _isMenuBarButton])
|
||||||
{
|
{
|
||||||
@@ -231,13 +121,11 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
|
|
||||||
var mouseOverMenuView = [activeItem view];
|
var mouseOverMenuView = [activeItem view];
|
||||||
|
|
||||||
if (type === CPScrollWheel && ![activeMenuContainer isMenuBar])
|
|
||||||
[activeMenuContainer scrollByDelta:[anEvent deltaY]];
|
|
||||||
|
|
||||||
if (type === CPPeriodic)
|
if (type === CPPeriodic)
|
||||||
{
|
{
|
||||||
if (_scrollingState === _CPMenuManagerScrollingStateUp)
|
if (_scrollingState === _CPMenuManagerScrollingStateUp)
|
||||||
[activeMenuContainer scrollUp];
|
[activeMenuContainer scrollUp];
|
||||||
|
|
||||||
else if (_scrollingState === _CPMenuManagerScrollingStateDown)
|
else if (_scrollingState === _CPMenuManagerScrollingStateDown)
|
||||||
[activeMenuContainer scrollDown];
|
[activeMenuContainer scrollDown];
|
||||||
}
|
}
|
||||||
@@ -250,28 +138,26 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
|
|
||||||
if (_lastMouseOverMenuView != mouseOverMenuView)
|
if (_lastMouseOverMenuView != mouseOverMenuView)
|
||||||
{
|
{
|
||||||
[_lastMouseOverMenuView mouseExited:anEvent];
|
[mouseOverMenuView mouseExited:anEvent];
|
||||||
// FIXME: Possibly multiple of these?
|
// FIXME: Possibly multiple of these?
|
||||||
[mouseOverMenuView mouseEntered:anEvent];
|
[_lastMouseOverMenuView mouseEntered:anEvent];
|
||||||
|
|
||||||
_lastMouseOverMenuView = mouseOverMenuView;
|
_lastMouseOverMenuView = mouseOverMenuView;
|
||||||
}
|
}
|
||||||
|
|
||||||
var menuContainerWindow = activeMenuContainer;
|
var menuContainerWindow = activeMenuContainer;
|
||||||
|
|
||||||
if (![menuContainerWindow isKindOfClass:CPWindow])
|
if (![menuContainerWindow isKindOfClass:[CPWindow class]])
|
||||||
menuContainerWindow = [menuContainerWindow window];
|
menuContainerWindow = [menuContainerWindow window];
|
||||||
|
|
||||||
[menuContainerWindow
|
[menuContainerWindow
|
||||||
sendEvent:[CPEvent mouseEventWithType:type
|
sendEvent:[CPEvent mouseEventWithType:type location:menuLocation modifierFlags:[anEvent modifierFlags]
|
||||||
location:menuLocation
|
timestamp:[anEvent timestamp]
|
||||||
modifierFlags:[anEvent modifierFlags]
|
windowNumber:menuContainerWindow
|
||||||
timestamp:[anEvent timestamp]
|
context:nil
|
||||||
windowNumber:menuContainerWindow
|
eventNumber:0
|
||||||
context:nil
|
clickCount:[anEvent clickCount]
|
||||||
eventNumber:0
|
pressure:[anEvent pressure]]];
|
||||||
clickCount:[anEvent clickCount]
|
|
||||||
pressure:[anEvent pressure]]];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -281,14 +167,10 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
_lastMouseOverMenuView = nil;
|
_lastMouseOverMenuView = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeItemIndex !== CPNotFound)
|
[activeMenu _highlightItemAtIndex:activeItemIndex];
|
||||||
[activeMenu _highlightItemAtIndex:activeItemIndex];
|
|
||||||
|
|
||||||
if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown || type === CPPeriodic)
|
if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown)
|
||||||
{
|
{
|
||||||
if (type === CPLeftMouseDragged)
|
|
||||||
_mouseWasDragged = YES;
|
|
||||||
|
|
||||||
var oldScrollingState = _scrollingState;
|
var oldScrollingState = _scrollingState;
|
||||||
|
|
||||||
_scrollingState = [activeMenuContainer scrollingStateForPoint:globalLocation];
|
_scrollingState = [activeMenuContainer scrollingStateForPoint:globalLocation];
|
||||||
@@ -297,89 +179,34 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
{
|
{
|
||||||
if (_scrollingState === _CPMenuManagerScrollingStateNone)
|
if (_scrollingState === _CPMenuManagerScrollingStateNone)
|
||||||
[CPEvent stopPeriodicEvents];
|
[CPEvent stopPeriodicEvents];
|
||||||
|
|
||||||
else if (oldScrollingState === _CPMenuManagerScrollingStateNone)
|
else if (oldScrollingState === _CPMenuManagerScrollingStateNone)
|
||||||
[CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04];
|
[CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type === CPLeftMouseUp || type === CPRightMouseUp)
|
else if (type === CPLeftMouseUp && ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL))
|
||||||
{
|
[trackingMenu cancelTracking];
|
||||||
/*
|
|
||||||
There are a few possibilites:
|
|
||||||
|
|
||||||
1. The user clicks and releases without dragging within the sticky time.
|
|
||||||
This is considered a regular mouse click and not a drag. In this case
|
|
||||||
we allow the user to track the menu by moving the mouse. The next
|
|
||||||
mouse up will end tracking.
|
|
||||||
|
|
||||||
2. The user clicks and releases without dragging after the sticky time.
|
|
||||||
This is considered a drag and release and tracking ends.
|
|
||||||
|
|
||||||
3. The user clicks, drags and then releases. Tracking ends.
|
|
||||||
*/
|
|
||||||
if (_mouseWasDragged || ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Close the menu if:
|
|
||||||
|
|
||||||
1. The mouse was dragged.
|
|
||||||
2. The mouse is released in the menubar.
|
|
||||||
3. The current item has a submenu with a custom action.
|
|
||||||
*/
|
|
||||||
if (_mouseWasDragged ||
|
|
||||||
[activeMenuContainer isMenuBar] ||
|
|
||||||
[activeItem action] !== @selector(submenuAction:))
|
|
||||||
{
|
|
||||||
[trackingMenu cancelTracking];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent previous selected menu items from opening by stopping the timer if a
|
|
||||||
// new item is selected before the timer runs out
|
|
||||||
if (_previousActiveItem !== activeItem)
|
|
||||||
{
|
|
||||||
clearTimeout(_showTimerID);
|
|
||||||
_showTimerID = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the item has a submenu, show it.
|
// If the item has a submenu, show it.
|
||||||
if ([activeItem hasSubmenu]) // && [activeItem action] === @selector(submenuAction:))
|
if ([activeItem hasSubmenu])// && [activeItem action] === @selector(submenuAction:))
|
||||||
{
|
{
|
||||||
var activeItemRect = [activeMenuContainer rectForItemAtIndex:activeItemIndex],
|
var activeItemRect = [activeMenuContainer rectForItemAtIndex:activeItemIndex];
|
||||||
newMenuOrigin;
|
|
||||||
|
|
||||||
if ([activeMenuContainer isMenuBar])
|
if ([activeMenuContainer isMenuBar])
|
||||||
newMenuOrigin = CGPointMake(CGRectGetMinX(activeItemRect), CGRectGetMaxY(activeItemRect));
|
var newMenuOrigin = CGPointMake(CGRectGetMinX(activeItemRect), CGRectGetMaxY(activeItemRect));
|
||||||
else
|
else
|
||||||
newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect));
|
var newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect));
|
||||||
|
|
||||||
newMenuOrigin = [activeMenuContainer convertBaseToGlobal:newMenuOrigin];
|
newMenuOrigin = [activeMenuContainer convertBaseToGlobal:newMenuOrigin];
|
||||||
|
|
||||||
// Only start a new timer if the previous was cancelled
|
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin];
|
||||||
if (_showTimerID === undefined)
|
|
||||||
{
|
|
||||||
// Close the current menu item because we are going to select a new one after a short delay
|
|
||||||
[self showMenu:nil fromMenu:activeMenu atPoint:CGPointMakeZero()];
|
|
||||||
|
|
||||||
if (![activeMenuContainer isMenuBar])
|
|
||||||
{
|
|
||||||
_showTimerID = setTimeout(function()
|
|
||||||
{
|
|
||||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin];
|
|
||||||
}, 250);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This handles both the case where we've moved away from the menu, and where
|
// This handles both the case where we've moved away from the menu, and where
|
||||||
// we've moved to an item without a submenu.
|
// we've moved to an item without a submenu.
|
||||||
else
|
else
|
||||||
[self showMenu:nil fromMenu:activeMenu atPoint:CGPointMakeZero()];
|
[self showMenu:nil fromMenu:activeMenu atPoint:CGPointMakeZero()];
|
||||||
|
|
||||||
_previousActiveItem = activeItem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)trackMenuBarButtonEvent:(CPEvent)anEvent
|
- (void)trackMenuBarButtonEvent:(CPEvent)anEvent
|
||||||
@@ -389,15 +216,16 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
if (type === CPAppKitDefined)
|
if (type === CPAppKitDefined)
|
||||||
return [self completeTracking];
|
return [self completeTracking];
|
||||||
|
|
||||||
var globalLocation = [anEvent globalLocation],
|
var globalLocation = [anEvent globalLocation];
|
||||||
|
|
||||||
// Find which menu window the mouse is currently on top of
|
// Find which menu window the mouse is currently on top of
|
||||||
menu = [self trackingMenu],
|
var menu = [self trackingMenu],
|
||||||
trackingMenuContainer = [self trackingMenuContainer],
|
trackingMenuContainer = [self trackingMenuContainer],
|
||||||
menuLocation = [trackingMenuContainer convertGlobalToBase:globalLocation];
|
menuLocation = [trackingMenuContainer convertGlobalToBase:globalLocation];
|
||||||
|
|
||||||
if ([trackingMenuContainer itemIndexAtPoint:menuLocation] === _menuBarButtonItemIndex)
|
if ([trackingMenuContainer itemIndexAtPoint:menuLocation] === _menuBarButtonItemIndex)
|
||||||
[menu _highlightItemAtIndex:_menuBarButtonItemIndex];
|
[menu _highlightItemAtIndex:_menuBarButtonItemIndex];
|
||||||
|
|
||||||
else
|
else
|
||||||
[menu _highlightItemAtIndex:CPNotFound];
|
[menu _highlightItemAtIndex:CPNotFound];
|
||||||
|
|
||||||
@@ -414,15 +242,23 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
// Stop all periodic events at this point.
|
// Stop all periodic events at this point.
|
||||||
[CPEvent stopPeriodicEvents];
|
[CPEvent stopPeriodicEvents];
|
||||||
|
|
||||||
|
// Get the highlighted item from the original menu.
|
||||||
|
var highlightedItem = [trackingMenu highlightedItem];
|
||||||
|
|
||||||
// Hide all submenus.
|
// Hide all submenus.
|
||||||
[self showMenu:nil fromMenu:trackingMenu atPoint:nil];
|
[self showMenu:nil fromMenu:trackingMenu atPoint:nil];
|
||||||
[trackingMenu _menuDidClose];
|
|
||||||
|
var delegate = [trackingMenu delegate];
|
||||||
|
|
||||||
|
if ([delegate respondsToSelector:@selector(menuDidClose:)])
|
||||||
|
[delegate menuDidClose:trackingMenu];
|
||||||
|
|
||||||
if (_trackingCallback)
|
if (_trackingCallback)
|
||||||
_trackingCallback([self trackingMenuContainer], trackingMenu);
|
_trackingCallback([self trackingMenuContainer], trackingMenu);
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPMenuDidEndTrackingNotification
|
[[CPNotificationCenter defaultCenter]
|
||||||
object:trackingMenu];
|
postNotificationName:CPMenuDidEndTrackingNotification
|
||||||
|
object:trackingMenu];
|
||||||
|
|
||||||
CPApp._activeMenu = nil;
|
CPApp._activeMenu = nil;
|
||||||
}
|
}
|
||||||
@@ -457,8 +293,8 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
{
|
{
|
||||||
var menuContainer = _menuContainerStack[index],
|
var menuContainer = _menuContainerStack[index],
|
||||||
menuContainerFrame = [menuContainer globalFrame],
|
menuContainerFrame = [menuContainer globalFrame],
|
||||||
menuContainerMinX = CGRectGetMinX(menuContainerFrame),
|
menuContainerMinX = _CGRectGetMinX(menuContainerFrame),
|
||||||
menuContainerMaxX = CGRectGetMaxX(menuContainerFrame);
|
menuContainerMaxX = _CGRectGetMaxX(menuContainerFrame);
|
||||||
|
|
||||||
// If within the x bounds of this menu container, return it.
|
// If within the x bounds of this menu container, return it.
|
||||||
if (x < menuContainerMaxX && x >= menuContainerMinX)
|
if (x < menuContainerMaxX && x >= menuContainerMinX)
|
||||||
@@ -485,8 +321,6 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
var count = _menuContainerStack.length,
|
var count = _menuContainerStack.length,
|
||||||
index = count;
|
index = count;
|
||||||
|
|
||||||
[newMenu _menuWillOpen];
|
|
||||||
|
|
||||||
// Hide all menus up to the base menu...
|
// Hide all menus up to the base menu...
|
||||||
while (index--)
|
while (index--)
|
||||||
{
|
{
|
||||||
@@ -498,7 +332,7 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// If this menu is already being shown, unhighlight and return.
|
// If this menu is already being shown, unhighlight and return.
|
||||||
if (menu === newMenu) //&& [menu supermenu] === baseMenu)
|
if (menu === newMenu)//&& [menu supermenu] === baseMenu)
|
||||||
return [newMenu _highlightItemAtIndex:CPNotFound];
|
return [newMenu _highlightItemAtIndex:CPNotFound];
|
||||||
|
|
||||||
[menuContainer orderOut:self];
|
[menuContainer orderOut:self];
|
||||||
@@ -506,8 +340,6 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
|
|
||||||
[_CPMenuWindow poolMenuWindow:menuContainer];
|
[_CPMenuWindow poolMenuWindow:menuContainer];
|
||||||
[_menuContainerStack removeObjectAtIndex:index];
|
[_menuContainerStack removeObjectAtIndex:index];
|
||||||
|
|
||||||
[menu _menuDidClose];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newMenu)
|
if (!newMenu)
|
||||||
@@ -532,238 +364,4 @@ var STICKY_TIME_INTERVAL = 0.4,
|
|||||||
[menuWindow orderFront:self];
|
[menuWindow orderFront:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// handle keyboard navigation
|
|
||||||
- (void)interpretKeyEvent:(CPEvent)anEvent forMenu:(CPMenu)menu
|
|
||||||
{
|
|
||||||
var modifierFlags = [anEvent modifierFlags],
|
|
||||||
character = [anEvent charactersIgnoringModifiers],
|
|
||||||
selectorNames = [CPKeyBinding selectorsForKey:character modifierFlags:modifierFlags];
|
|
||||||
|
|
||||||
if (selectorNames)
|
|
||||||
{
|
|
||||||
var iter = [selectorNames objectEnumerator],
|
|
||||||
obj;
|
|
||||||
|
|
||||||
while ((obj = [iter nextObject]) !== nil)
|
|
||||||
{
|
|
||||||
var aSelector = CPSelectorFromString(obj);
|
|
||||||
|
|
||||||
if ([self respondsToSelector:aSelector])
|
|
||||||
[self performSelector:aSelector withObject:menu];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!(modifierFlags & (CPCommandKeyMask | CPControlKeyMask)))
|
|
||||||
{
|
|
||||||
if (([anEvent timestamp] - _startTime) > STICKY_TIME_INTERVAL)
|
|
||||||
_keyBuffer = nil;
|
|
||||||
|
|
||||||
if (!_keyBuffer)
|
|
||||||
{
|
|
||||||
_startTime = [anEvent timestamp];
|
|
||||||
_keyBuffer = character;
|
|
||||||
|
|
||||||
[CPEvent stopPeriodicEvents];
|
|
||||||
[CPEvent startPeriodicEventsAfterDelay:0.1 withPeriod:0.1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_keyBuffer += character;
|
|
||||||
|
|
||||||
[self selectNextItemBeginningWith:_keyBuffer inMenu:menu];
|
|
||||||
_lastGlobalLocation = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)selectNextItemBeginningWith:(CPString)characters inMenu:(CPMenu)menu
|
|
||||||
{
|
|
||||||
var iter = [[menu itemArray] objectEnumerator],
|
|
||||||
obj;
|
|
||||||
|
|
||||||
while ((obj = [iter nextObject]) !== nil)
|
|
||||||
{
|
|
||||||
if ([obj isHidden] || ![obj isEnabled])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ([[[obj title] commonPrefixWithString:characters options:CPCaseInsensitiveSearch] length] == [characters length])
|
|
||||||
{
|
|
||||||
[menu _highlightItemAtIndex:iter._index];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_startTime = [CPEvent currentTimestamp];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollToBeginningOfDocument:(CPMenu)menu
|
|
||||||
{
|
|
||||||
[menu _highlightItemAtIndex:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollToEndOfDocument:(CPMenu)menu
|
|
||||||
{
|
|
||||||
[menu _highlightItemAtIndex:[menu numberOfItems] - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollPageDown:(CPMenu)menu
|
|
||||||
{
|
|
||||||
var menuWindow = menu._menuWindow,
|
|
||||||
menuClipView = menuWindow._menuClipView,
|
|
||||||
bottom = [menuClipView bounds].size.height,
|
|
||||||
first = [menuWindow itemIndexAtPoint:CGPointMake(1, 10)],
|
|
||||||
last = [menuWindow itemIndexAtPoint:CGPointMake(1, bottom)],
|
|
||||||
current = [menu indexOfItem:[menu highlightedItem]];
|
|
||||||
|
|
||||||
if (current == CPNotFound)
|
|
||||||
{
|
|
||||||
[menu _highlightItemAtIndex:0];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var next = current + (last - first);
|
|
||||||
|
|
||||||
if (next < [menu numberOfItems])
|
|
||||||
[menu _highlightItemAtIndex:next];
|
|
||||||
else
|
|
||||||
[menu _highlightItemAtIndex:[menu numberOfItems] - 1];
|
|
||||||
|
|
||||||
var item = [menu highlightedItem];
|
|
||||||
|
|
||||||
if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled])
|
|
||||||
[self moveDown:menu];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollPageUp:(CPMenu)menu
|
|
||||||
{
|
|
||||||
var menuWindow = menu._menuWindow,
|
|
||||||
menuClipView = menuWindow._menuClipView,
|
|
||||||
bottom = [menuClipView bounds].size.height,
|
|
||||||
first = [menuWindow itemIndexAtPoint:CGPointMake(1, 10)],
|
|
||||||
last = [menuWindow itemIndexAtPoint:CGPointMake(1, bottom)],
|
|
||||||
current = [menu indexOfItem:[menu highlightedItem]];
|
|
||||||
|
|
||||||
if (current == CPNotFound)
|
|
||||||
{
|
|
||||||
[menu _highlightItemAtIndex:0];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var next = current - (last - first);
|
|
||||||
|
|
||||||
if (next < 0)
|
|
||||||
[menu _highlightItemAtIndex:0];
|
|
||||||
else
|
|
||||||
[menu _highlightItemAtIndex:next];
|
|
||||||
|
|
||||||
var item = [menu highlightedItem];
|
|
||||||
|
|
||||||
if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled])
|
|
||||||
[self moveUp:menu];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveLeft:(CPMenu)menu
|
|
||||||
{
|
|
||||||
if ([menu supermenu])
|
|
||||||
{
|
|
||||||
if ([menu supermenu] == [CPApp mainMenu])
|
|
||||||
{
|
|
||||||
[self showMenu:nil fromMenu:[menu supermenu] atPoint:CGPointMakeZero()];
|
|
||||||
[self moveUp:[CPApp mainMenu]];
|
|
||||||
|
|
||||||
var activeItem = [[CPApp mainMenu] highlightedItem],
|
|
||||||
menuLocation = CGPointMake([[activeItem _menuItemView] frameOrigin].x , [[activeItem _menuItemView] frameSize].height);
|
|
||||||
|
|
||||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:menuLocation];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[self showMenu:nil fromMenu:[menu supermenu] atPoint:CGPointMakeZero()];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveRight:(CPMenu)menu
|
|
||||||
{
|
|
||||||
var activeItem = [menu highlightedItem];
|
|
||||||
|
|
||||||
if ([activeItem hasSubmenu])
|
|
||||||
{
|
|
||||||
if ([[activeItem submenu] numberOfItems])
|
|
||||||
{
|
|
||||||
var activeItemIndex = [menu indexOfItem:activeItem],
|
|
||||||
activeMenuContainer = menu._menuWindow,
|
|
||||||
activeItemRect = [activeMenuContainer rectForItemAtIndex:activeItemIndex],
|
|
||||||
newMenuOrigin;
|
|
||||||
|
|
||||||
if ([activeMenuContainer isMenuBar])
|
|
||||||
newMenuOrigin = CGPointMake(CGRectGetMinX(activeItemRect), CGRectGetMaxY(activeItemRect));
|
|
||||||
else
|
|
||||||
newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect));
|
|
||||||
|
|
||||||
newMenuOrigin = [activeMenuContainer convertBaseToGlobal:newMenuOrigin];
|
|
||||||
|
|
||||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin];
|
|
||||||
[self moveDown:[activeItem submenu]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ([self trackingMenu] == [CPApp mainMenu])
|
|
||||||
{
|
|
||||||
[self showMenu:nil fromMenu:menu atPoint:CGPointMakeZero()];
|
|
||||||
[self moveDown:[CPApp mainMenu]];
|
|
||||||
|
|
||||||
var activeItem = [[CPApp mainMenu] highlightedItem],
|
|
||||||
menuLocation = CGPointMake([[activeItem _menuItemView] frameOrigin].x , [[activeItem _menuItemView] frameSize].height);
|
|
||||||
|
|
||||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:menuLocation];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveDown:(CPMenu)menu
|
|
||||||
{
|
|
||||||
var index = menu._highlightedIndex + 1;
|
|
||||||
|
|
||||||
if (index < [menu numberOfItems])
|
|
||||||
{
|
|
||||||
[menu _highlightItemAtIndex:index];
|
|
||||||
|
|
||||||
var item = [menu highlightedItem];
|
|
||||||
|
|
||||||
if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled])
|
|
||||||
[self moveDown:menu];
|
|
||||||
}
|
|
||||||
else if (menu == [CPApp mainMenu])
|
|
||||||
[menu _highlightItemAtIndex:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveUp:(CPMenu)menu
|
|
||||||
{
|
|
||||||
var index = menu._highlightedIndex - 1;
|
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
if (index != CPNotFound || menu == [CPApp mainMenu])
|
|
||||||
[menu _highlightItemAtIndex:[menu numberOfItems] - 1];
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[menu _highlightItemAtIndex:index];
|
|
||||||
|
|
||||||
var item = [menu highlightedItem];
|
|
||||||
|
|
||||||
if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled])
|
|
||||||
[self moveUp:menu];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertNewline:(CPMenu)menu
|
|
||||||
{
|
|
||||||
if ([[menu highlightedItem] hasSubmenu])
|
|
||||||
[self moveRight:menu];
|
|
||||||
else
|
|
||||||
[menu cancelTracking]
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)cancelOperation:(CPMenu)menu
|
|
||||||
{
|
|
||||||
[menu _highlightItemAtIndex:CPNotFound];
|
|
||||||
[CPEvent stopPeriodicEvents];
|
|
||||||
[[self trackingMenu] cancelTracking];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+95
-129
@@ -1,29 +1,5 @@
|
|||||||
/*
|
|
||||||
* _CPMenuWindow.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 "CPClipView.j"
|
|
||||||
@import "CPImageView.j"
|
|
||||||
@import "CPWindow.j"
|
@import "CPWindow.j"
|
||||||
@import "_CPMenuManager.j"
|
|
||||||
|
|
||||||
var _CPMenuWindowPool = [],
|
var _CPMenuWindowPool = [],
|
||||||
_CPMenuWindowPoolCapacity = 5,
|
_CPMenuWindowPoolCapacity = 5,
|
||||||
@@ -34,6 +10,15 @@ _CPMenuWindowMenuBarBackgroundStyle = 0;
|
|||||||
_CPMenuWindowPopUpBackgroundStyle = 1;
|
_CPMenuWindowPopUpBackgroundStyle = 1;
|
||||||
_CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
_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
|
@ignore
|
||||||
*/
|
*/
|
||||||
@@ -78,17 +63,26 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
_CPMenuWindowPool.push(aMenuWindow);
|
_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
|
- (id)initWithContentRect:(CGRect)aRect styleMask:(unsigned)aStyleMask
|
||||||
{
|
{
|
||||||
_constraintRect = CGRectMakeZero();
|
_constraintRect = _CGRectMakeZero();
|
||||||
_unconstrainedFrame = CGRectMakeZero();
|
_unconstrainedFrame = _CGRectMakeZero();
|
||||||
|
|
||||||
self = [super initWithContentRect:aRect styleMask:CPBorderlessWindowMask];
|
self = [super initWithContentRect:aRect styleMask:CPBorderlessWindowMask];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_constrainsToUsableScreen = NO;
|
|
||||||
|
|
||||||
[self setLevel:CPPopUpMenuWindowLevel];
|
[self setLevel:CPPopUpMenuWindowLevel];
|
||||||
[self setHasShadow:YES];
|
[self setHasShadow:YES];
|
||||||
[self setShadowStyle:CPMenuWindowShadowStyle];
|
[self setShadowStyle:CPMenuWindowShadowStyle];
|
||||||
@@ -98,24 +92,26 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
|
|
||||||
_menuView = [[_CPMenuView alloc] initWithFrame:CGRectMakeZero()];
|
_menuView = [[_CPMenuView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
_menuClipView = [[CPClipView alloc] initWithFrame:CGRectMake([_menuView valueForThemeAttribute:@"menu-window-margin-inset"].left, [_menuView valueForThemeAttribute:@"menu-window-margin-inset"].top, 0.0, 0.0)];
|
_menuClipView = [[CPClipView alloc] initWithFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, 0.0, 0.0)];
|
||||||
[_menuClipView setDocumentView:_menuView];
|
[_menuClipView setDocumentView:_menuView];
|
||||||
|
|
||||||
[contentView addSubview:_menuClipView];
|
[contentView addSubview:_menuClipView];
|
||||||
|
|
||||||
_moreAboveView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
_moreAboveView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
[_moreAboveView setImage:[_menuView valueForThemeAttribute:@"menu-window-more-above-image"]];
|
[_moreAboveView setImage:_CPMenuWindowMoreAboveImage];
|
||||||
[_moreAboveView setFrameSize:[[_menuView valueForThemeAttribute:@"menu-window-more-above-image"] size]];
|
[_moreAboveView setFrameSize:[_CPMenuWindowMoreAboveImage size]];
|
||||||
|
|
||||||
[contentView addSubview:_moreAboveView];
|
[contentView addSubview:_moreAboveView];
|
||||||
|
|
||||||
_moreBelowView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
_moreBelowView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
|
||||||
[_moreBelowView setImage:[_menuView valueForThemeAttribute:@"menu-window-more-below-image"]];
|
[_moreBelowView setImage:_CPMenuWindowMoreBelowImage];
|
||||||
[_moreBelowView setFrameSize:[[_menuView valueForThemeAttribute:@"menu-window-more-below-image"] size]];
|
[_moreBelowView setFrameSize:[_CPMenuWindowMoreBelowImage size]];
|
||||||
|
|
||||||
[contentView addSubview:_moreBelowView];
|
[contentView addSubview:_moreBelowView];
|
||||||
|
|
||||||
|
[self setShadowStyle:CPWindowShadowStyleMenu];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -123,7 +119,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
|
|
||||||
+ (float)_standardLeftMargin
|
+ (float)_standardLeftMargin
|
||||||
{
|
{
|
||||||
return [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-window-margin-inset" forClass:_CPMenuView].left;
|
return LEFT_MARGIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
- (void)setFont:(CPFont)aFont
|
||||||
@@ -145,10 +141,36 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
var bundle = [CPBundle bundleForClass:[self class]];
|
var bundle = [CPBundle bundleForClass:[self class]];
|
||||||
|
|
||||||
if (aBackgroundStyle == _CPMenuWindowPopUpBackgroundStyle)
|
if (aBackgroundStyle == _CPMenuWindowPopUpBackgroundStyle)
|
||||||
color = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-window-pop-up-background-style-color" forClass:_CPMenuView];
|
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)
|
else if (aBackgroundStyle == _CPMenuWindowMenuBarBackgroundStyle)
|
||||||
color = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-window-menu-bar-background-style-color" forClass:_CPMenuView];
|
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;
|
_CPMenuWindowBackgroundColors[aBackgroundStyle] = color;
|
||||||
}
|
}
|
||||||
@@ -166,13 +188,12 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
[aMenu _setMenuWindow:self];
|
[aMenu _setMenuWindow:self];
|
||||||
[_menuView setMenu:aMenu];
|
[_menuView setMenu:aMenu];
|
||||||
|
|
||||||
var menuViewSize = [_menuView frame].size,
|
var menuViewSize = [_menuView frame].size;
|
||||||
marginInset = [_menuView valueForThemeAttribute:@"menu-window-margin-inset"];
|
|
||||||
|
|
||||||
[self setFrameSize:CGSizeMake(marginInset.left + menuViewSize.width + marginInset.right, marginInset.top + menuViewSize.height + marginInset.bottom)];
|
[self setFrameSize:CGSizeMake(LEFT_MARGIN + menuViewSize.width + RIGHT_MARGIN, TOP_MARGIN + menuViewSize.height + BOTTOM_MARGIN)];
|
||||||
|
|
||||||
[_menuView scrollPoint:CGPointMake(0.0, 0.0)];
|
[_menuView scrollPoint:CGPointMake(0.0, 0.0)];
|
||||||
[_menuClipView setFrame:CGRectMake(marginInset.left, marginInset.top, menuViewSize.width, menuViewSize.height)];
|
[_menuClipView setFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, menuViewSize.width, menuViewSize.height)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setMinWidth:(float)aWidth
|
- (void)setMinWidth:(float)aWidth
|
||||||
@@ -187,14 +208,8 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
return [_menuView menu];
|
return [_menuView menu];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (_CPMenuView)_menuView
|
|
||||||
{
|
|
||||||
return _menuView;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)orderFront:(id)aSender
|
- (void)orderFront:(id)aSender
|
||||||
{
|
{
|
||||||
[[self menu] update];
|
|
||||||
[self setFrame:_unconstrainedFrame];
|
[self setFrame:_unconstrainedFrame];
|
||||||
|
|
||||||
[super orderFront:aSender];
|
[super orderFront:aSender];
|
||||||
@@ -209,30 +224,28 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
|
|
||||||
- (CGRect)unconstrainedFrame
|
- (CGRect)unconstrainedFrame
|
||||||
{
|
{
|
||||||
return CGRectMakeCopy(_unconstrainedFrame);
|
return _CGRectMakeCopy(_unconstrainedFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need this because if not this will call setFrame: with -frame instead of -unconstrainedFrame, turning
|
// We need this because if not this will call setFrame: with -frame instead of -unconstrainedFrame, turning
|
||||||
// the constrained frame into the unconstrained frame.
|
// the constrained frame into the unconstrained frame.
|
||||||
- (void)setFrameOrigin:(CGPoint)aPoint
|
- (void)setFrameOrigin:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
[super setFrame:CGRectMake(aPoint.x, aPoint.y, CGRectGetWidth(_unconstrainedFrame), CGRectGetHeight(_unconstrainedFrame))];
|
[super setFrame:_CGRectMake(aPoint.x, aPoint.y, _CGRectGetWidth(_unconstrainedFrame), _CGRectGetHeight(_unconstrainedFrame))];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFrameSize:(CGSize)aSize
|
- (void)setFrameSize:(CGSize)aSize
|
||||||
{
|
{
|
||||||
[super setFrame:CGRectMake(CGRectGetMinX(_unconstrainedFrame), CGRectGetMinY(_unconstrainedFrame), aSize.width, aSize.height)];
|
[super setFrame:_CGRectMake(_CGRectGetMinX(_unconstrainedFrame), _CGRectGetMinY(_unconstrainedFrame), aSize.width, aSize.height)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
||||||
{
|
{
|
||||||
// FIXME: There are integral window issues with platform windows.
|
// FIXME: There are integral window issues with platform windows.
|
||||||
// FIXME: This gets called far too often.
|
// FIXME: This gets called far too often.
|
||||||
_unconstrainedFrame = CGRectMakeCopy(aFrame);
|
_unconstrainedFrame = _CGRectMakeCopy(aFrame);
|
||||||
|
|
||||||
var constrainedFrame = CGRectIntersection(_unconstrainedFrame, _constraintRect),
|
var constrainedFrame = CGRectIntersection(_unconstrainedFrame, _constraintRect);
|
||||||
marginInset = [_menuView valueForThemeAttribute:@"menu-window-margin-inset"],
|
|
||||||
scrollIndicatorHeight = [_menuView valueForThemeAttribute:@"menu-window-scroll-indicator-height"];
|
|
||||||
|
|
||||||
// We don't want to simply intersect the visible frame and the unconstrained frame.
|
// 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).
|
// We should be allowing as much of the width to fit as possible (pushing back and forward).
|
||||||
@@ -251,42 +264,37 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
[super setFrame:constrainedFrame display:shouldDisplay animate:shouldAnimate];
|
[super setFrame:constrainedFrame display:shouldDisplay animate:shouldAnimate];
|
||||||
|
|
||||||
// This needs to happen before changing the frame.
|
// This needs to happen before changing the frame.
|
||||||
var menuViewOrigin = CGPointMake(CGRectGetMinX(aFrame) + marginInset.left, CGRectGetMinY(aFrame) + marginInset.top),
|
var menuViewOrigin = CGPointMake(CGRectGetMinX(aFrame) + LEFT_MARGIN, CGRectGetMinY(aFrame) + TOP_MARGIN),
|
||||||
moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + marginInset.top,
|
moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + TOP_MARGIN,
|
||||||
moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - marginInset.bottom,
|
moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - BOTTOM_MARGIN,
|
||||||
|
|
||||||
topMargin = marginInset.top,
|
topMargin = TOP_MARGIN,
|
||||||
bottomMargin = marginInset.bottom,
|
bottomMargin = BOTTOM_MARGIN,
|
||||||
|
|
||||||
contentView = [self contentView],
|
contentView = [self contentView],
|
||||||
bounds = [contentView bounds];
|
bounds = [contentView bounds];
|
||||||
|
|
||||||
if (moreAbove)
|
if (moreAbove)
|
||||||
{
|
{
|
||||||
topMargin += scrollIndicatorHeight;
|
topMargin += SCROLL_INDICATOR_HEIGHT;
|
||||||
|
|
||||||
var frame = [_moreAboveView frame];
|
var frame = [_moreAboveView frame];
|
||||||
|
|
||||||
[_moreAboveView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(frame)) / 2.0, (marginInset.top + scrollIndicatorHeight - CGRectGetHeight(frame)) / 2.0)];
|
[_moreAboveView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(frame)) / 2.0, (TOP_MARGIN + SCROLL_INDICATOR_HEIGHT - CGRectGetHeight(frame)) / 2.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_moreAboveView setHidden:!moreAbove];
|
[_moreAboveView setHidden:!moreAbove];
|
||||||
|
|
||||||
if (moreBelow)
|
if (moreBelow)
|
||||||
{
|
{
|
||||||
bottomMargin += scrollIndicatorHeight;
|
bottomMargin += SCROLL_INDICATOR_HEIGHT;
|
||||||
|
|
||||||
[_moreBelowView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth([_moreBelowView frame])) / 2.0, CGRectGetHeight(bounds) - scrollIndicatorHeight - marginInset.bottom)];
|
[_moreBelowView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth([_moreBelowView frame])) / 2.0, CGRectGetHeight(bounds) - SCROLL_INDICATOR_HEIGHT - BOTTOM_MARGIN)];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_moreBelowView setHidden:!moreBelow];
|
[_moreBelowView setHidden:!moreBelow];
|
||||||
|
|
||||||
var clipFrame = CGRectMakeZero();
|
var clipFrame = CGRectMake(LEFT_MARGIN, topMargin, CGRectGetWidth(constrainedFrame) - LEFT_MARGIN - RIGHT_MARGIN, CGRectGetHeight(constrainedFrame) - topMargin - bottomMargin)
|
||||||
|
|
||||||
clipFrame.origin.x = marginInset.left;
|
|
||||||
clipFrame.origin.y = topMargin;
|
|
||||||
clipFrame.size.width = CGRectGetWidth(constrainedFrame) - marginInset.left - marginInset.right;
|
|
||||||
clipFrame.size.height = CGRectGetHeight(constrainedFrame) - topMargin - bottomMargin;
|
|
||||||
|
|
||||||
[_menuClipView setFrame:clipFrame];
|
[_menuClipView setFrame:clipFrame];
|
||||||
[_menuView setFrameSize:CGSizeMake(CGRectGetWidth(clipFrame), CGRectGetHeight([_menuView frame]))];
|
[_menuView setFrameSize:CGSizeMake(CGRectGetWidth(clipFrame), CGRectGetHeight([_menuView frame]))];
|
||||||
@@ -339,29 +347,24 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
return [self canScrollUp] || [self canScrollDown];
|
return [self canScrollUp] || [self canScrollDown];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollByDelta:(float)theDelta
|
|
||||||
{
|
|
||||||
if (theDelta === 0.0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (theDelta > 0.0 && ![self canScrollDown])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (theDelta < 0.0 && ![self canScrollUp])
|
|
||||||
return;
|
|
||||||
|
|
||||||
_unconstrainedFrame.origin.y -= theDelta;
|
|
||||||
[self setFrame:_unconstrainedFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollUp
|
- (void)scrollUp
|
||||||
{
|
{
|
||||||
[self scrollByDelta:-10.0];
|
if (CGRectGetMinY(_unconstrainedFrame) >= CGRectGetMinY(_constraintRect))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_unconstrainedFrame.origin.y += 10;
|
||||||
|
|
||||||
|
[self setFrame:_unconstrainedFrame];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollDown
|
- (void)scrollDown
|
||||||
{
|
{
|
||||||
[self scrollByDelta:10.0];
|
if (CGRectGetMaxY(_unconstrainedFrame) <= CGRectGetHeight(_constraintRect))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_unconstrainedFrame.origin.y -= 10;
|
||||||
|
|
||||||
|
[self setFrame:_unconstrainedFrame];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -381,15 +384,16 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
||||||
{
|
{
|
||||||
var frame = [self frame];
|
var frame = [self frame];
|
||||||
if (!CGRectContainsPoint(frame,aGlobalLocation) || ![self canScroll])
|
|
||||||
|
if (![self canScroll])
|
||||||
return _CPMenuManagerScrollingStateNone;
|
return _CPMenuManagerScrollingStateNone;
|
||||||
|
|
||||||
// If we're at or above of the top scroll indicator...
|
// If we're at or above of the top scroll indicator...
|
||||||
if (aGlobalLocation.y < CGRectGetMinY(frame) + [_menuView valueForThemeAttribute:@"menu-window-margin-inset"].top + [_menuView valueForThemeAttribute:@"menu-window-scroll-indicator-height"] && ![_moreAboveView isHidden])
|
if (aGlobalLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT)
|
||||||
return _CPMenuManagerScrollingStateUp;
|
return _CPMenuManagerScrollingStateUp;
|
||||||
|
|
||||||
// If we're at or below the bottom scroll indicator...
|
// If we're at or below the bottom scroll indicator...
|
||||||
if (aGlobalLocation.y > CGRectGetMaxY(frame) - [_menuView valueForThemeAttribute:@"menu-window-margin-inset"].bottom - [_menuView valueForThemeAttribute:@"menu-window-scroll-indicator-height"] && ![_moreBelowView isHidden])
|
if (aGlobalLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT)
|
||||||
return _CPMenuManagerScrollingStateDown;
|
return _CPMenuManagerScrollingStateDown;
|
||||||
|
|
||||||
return _CPMenuManagerScrollingStateNone;
|
return _CPMenuManagerScrollingStateNone;
|
||||||
@@ -397,7 +401,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
|
|
||||||
- (float)deltaYForItemAtIndex:(int)anIndex
|
- (float)deltaYForItemAtIndex:(int)anIndex
|
||||||
{
|
{
|
||||||
return [_menuView valueForThemeAttribute:@"menu-window-margin-inset"].top + CGRectGetMinY([_menuView rectForItemAtIndex:anIndex]);
|
return TOP_MARGIN + CGRectGetMinY([_menuView rectForItemAtIndex:anIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGPoint)rectForItemAtIndex:(int)anIndex
|
- (CGPoint)rectForItemAtIndex:(int)anIndex
|
||||||
@@ -428,44 +432,6 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
CPFont _font @accessors(property=font);
|
CPFont _font @accessors(property=font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return "menu-view";
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return @{
|
|
||||||
@"menu-window-more-above-image": [CPNull null],
|
|
||||||
@"menu-window-more-below-image": [CPNull null],
|
|
||||||
@"menu-window-pop-up-background-style-color": [CPNull null],
|
|
||||||
@"menu-window-menu-bar-background-style-color": [CPNull null],
|
|
||||||
@"menu-window-margin-inset": CGInsetMake(5.0, 1.0, 1.0, 5.0),
|
|
||||||
@"menu-window-scroll-indicator-height": 16.0,
|
|
||||||
@"menu-bar-window-background-color": [CPNull null],
|
|
||||||
@"menu-bar-window-background-selected-color": [CPNull null],
|
|
||||||
@"menu-bar-window-font": [CPNull null],
|
|
||||||
@"menu-bar-window-height": 30.0,
|
|
||||||
@"menu-bar-window-margin": 10.0,
|
|
||||||
@"menu-bar-window-left-margin": 10.0,
|
|
||||||
@"menu-bar-window-right-margin": 10.0,
|
|
||||||
@"menu-bar-text-color": [CPNull null],
|
|
||||||
@"menu-bar-title-color": [CPNull null],
|
|
||||||
@"menu-bar-text-shadow-color": [CPNull null],
|
|
||||||
@"menu-bar-title-shadow-color": [CPNull null],
|
|
||||||
@"menu-bar-highlight-color": [CPNull null],
|
|
||||||
@"menu-bar-highlight-text-color": [CPNull null],
|
|
||||||
@"menu-bar-highlight-text-shadow-color": [CPNull null],
|
|
||||||
@"menu-bar-height": 28.0,
|
|
||||||
@"menu-bar-icon-image": [CPNull null],
|
|
||||||
@"menu-bar-icon-image-alpha-value": 1.0,
|
|
||||||
@"menu-general-icon-new": [CPNull null],
|
|
||||||
@"menu-general-icon-save": [CPNull null],
|
|
||||||
@"menu-general-icon-open": [CPNull null],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)numberOfUnhiddenItems
|
- (unsigned)numberOfUnhiddenItems
|
||||||
{
|
{
|
||||||
return _visibleMenuItemInfos.length;
|
return _visibleMenuItemInfos.length;
|
||||||
@@ -496,7 +462,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
|||||||
while (low <= high)
|
while (low <= high)
|
||||||
{
|
{
|
||||||
var middle = FLOOR(low + (high - low) / 2),
|
var middle = FLOOR(low + (high - low) / 2),
|
||||||
info = _visibleMenuItemInfos[middle],
|
info = _visibleMenuItemInfos[middle]
|
||||||
frame = [info.view frame];
|
frame = [info.view frame];
|
||||||
|
|
||||||
if (y < CGRectGetMinY(frame))
|
if (y < CGRectGetMinY(frame))
|
||||||
|
|||||||
@@ -25,33 +25,10 @@
|
|||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
|
|
||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
@import "CPText.j"
|
@import "CPMenu.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
@import "_CPMenuItemView.j"
|
@import "_CPMenuItemView.j"
|
||||||
|
|
||||||
@class CPMenu
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
var CPMenuItemStringRepresentationDictionary = @{
|
|
||||||
CPEscapeFunctionKey: "\u238B",
|
|
||||||
CPTabCharacter: "\u21E5",
|
|
||||||
CPBackTabCharacter: "\u21E4",
|
|
||||||
CPSpaceFunctionKey: "\u2423",
|
|
||||||
CPCarriageReturnCharacter: "\u23CE",
|
|
||||||
CPBackspaceCharacter: "\u232B",
|
|
||||||
CPDeleteFunctionKey: "\u232B",
|
|
||||||
CPDeleteCharacter: "\u2326",
|
|
||||||
CPHomeFunctionKey: "\u21F1",
|
|
||||||
CPEndFunctionKey: "\u21F2",
|
|
||||||
CPPageUpFunctionKey: "\u21DE",
|
|
||||||
CPPageDownFunctionKey: "\u21DF",
|
|
||||||
CPUpArrowFunctionKey: "\u2191",
|
|
||||||
CPDownArrowFunctionKey: "\u2193",
|
|
||||||
CPLeftArrowFunctionKey: "\u2190",
|
|
||||||
CPRightArrowFunctionKey: "\u2192",
|
|
||||||
CPClearDisplayFunctionKey: "\u2327",
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -100,21 +77,9 @@ var CPMenuItemStringRepresentationDictionary = @{
|
|||||||
id _representedObject;
|
id _representedObject;
|
||||||
CPView _view;
|
CPView _view;
|
||||||
|
|
||||||
int _changeCount;
|
|
||||||
|
|
||||||
_CPMenuItemView _menuItemView;
|
_CPMenuItemView _menuItemView;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if ([aBinding hasPrefix:CPEnabledBinding])
|
|
||||||
return [CPMultipleValueAndBinding class];
|
|
||||||
else if (aBinding === CPTargetBinding || [aBinding hasPrefix:CPArgumentBinding])
|
|
||||||
return [CPActionBinding class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
return [self initWithTitle:@"" action:nil keyEquivalent:nil];
|
return [self initWithTitle:@"" action:nil keyEquivalent:nil];
|
||||||
@@ -133,14 +98,12 @@ var CPMenuItemStringRepresentationDictionary = @{
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_changeCount = 0;
|
|
||||||
_isSeparator = NO;
|
_isSeparator = NO;
|
||||||
|
|
||||||
_title = aTitle;
|
_title = aTitle;
|
||||||
_action = anAction;
|
_action = anAction;
|
||||||
|
|
||||||
_isEnabled = YES;
|
_isEnabled = YES;
|
||||||
_isHidden = NO;
|
|
||||||
|
|
||||||
_tag = 0;
|
_tag = 0;
|
||||||
_state = CPOffState;
|
_state = CPOffState;
|
||||||
@@ -163,12 +126,13 @@ var CPMenuItemStringRepresentationDictionary = @{
|
|||||||
*/
|
*/
|
||||||
- (void)setEnabled:(BOOL)isEnabled
|
- (void)setEnabled:(BOOL)isEnabled
|
||||||
{
|
{
|
||||||
if (_isEnabled === isEnabled)
|
if ([_menu autoenablesItems])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_isEnabled = !!isEnabled;
|
_isEnabled = isEnabled;
|
||||||
|
|
||||||
[_menuItemView setDirty];
|
[_menuItemView setDirty];
|
||||||
|
|
||||||
[_menu itemChanged:self];
|
[_menu itemChanged:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +259,7 @@ var CPMenuItemStringRepresentationDictionary = @{
|
|||||||
*/
|
*/
|
||||||
- (void)setFont:(CPFont)aFont
|
- (void)setFont:(CPFont)aFont
|
||||||
{
|
{
|
||||||
if ([_font isEqual:aFont])
|
if (_font == aFont)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_font = aFont;
|
_font = aFont;
|
||||||
@@ -481,7 +445,7 @@ CPOffState
|
|||||||
return _mixedStateImage;
|
return _mixedStateImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managing Submenus
|
// Managing Subemenus
|
||||||
/*!
|
/*!
|
||||||
Sets the submenu for this item
|
Sets the submenu for this item
|
||||||
@param aMenu the submenu
|
@param aMenu the submenu
|
||||||
@@ -495,14 +459,13 @@ CPOffState
|
|||||||
|
|
||||||
if (supermenu)
|
if (supermenu)
|
||||||
[CPException raise:CPInvalidArgumentException
|
[CPException raise:CPInvalidArgumentException
|
||||||
reason: @"Can't add submenu \"" + [aMenu title] + "\" to item \"" + [self title] + "\", because it is already submenu of \"" + [[aMenu supermenu] title] + "\""];
|
reason: @"Can't add submenu \"" + [aMenu title] + "\" to item \"" + [self title] + "\", because it is already submenu of \"" + [[aMenu supermenu] title] + "\""];
|
||||||
|
|
||||||
_submenu = aMenu;
|
_submenu = aMenu;
|
||||||
|
|
||||||
if (_submenu)
|
if (_submenu)
|
||||||
{
|
{
|
||||||
[_submenu setSupermenu:_menu];
|
[_submenu setSupermenu:_menu];
|
||||||
[_submenu setTitle:[self title]]
|
|
||||||
|
|
||||||
[self setTarget:_menu];
|
[self setTarget:_menu];
|
||||||
[self setAction:@selector(submenuAction:)];
|
[self setAction:@selector(submenuAction:)];
|
||||||
@@ -632,22 +595,19 @@ CPControlKeyMask
|
|||||||
needsShift = _keyEquivalentModifierMask & CPShiftKeyMask ||
|
needsShift = _keyEquivalentModifierMask & CPShiftKeyMask ||
|
||||||
(string === _keyEquivalent && _keyEquivalent.toLowerCase() !== _keyEquivalent.toUpperCase());
|
(string === _keyEquivalent && _keyEquivalent.toLowerCase() !== _keyEquivalent.toUpperCase());
|
||||||
|
|
||||||
if ([CPMenuItemStringRepresentationDictionary objectForKey:string])
|
|
||||||
string = [CPMenuItemStringRepresentationDictionary objectForKey:string];
|
|
||||||
|
|
||||||
if (CPBrowserIsOperatingSystem(CPMacOperatingSystem))
|
if (CPBrowserIsOperatingSystem(CPMacOperatingSystem))
|
||||||
{
|
{
|
||||||
if (_keyEquivalentModifierMask & CPCommandKeyMask)
|
if (_keyEquivalentModifierMask & CPCommandKeyMask)
|
||||||
string = "\u2318" + string;
|
string = "⌘" + string;
|
||||||
|
|
||||||
if (needsShift)
|
if (needsShift)
|
||||||
string = "\u21E7" + string;
|
string = "⇧" + string;
|
||||||
|
|
||||||
if (_keyEquivalentModifierMask & CPAlternateKeyMask)
|
if (_keyEquivalentModifierMask & CPAlternateKeyMask)
|
||||||
string = "\u2325" + string;
|
string = "⌥" + string;
|
||||||
|
|
||||||
if (_keyEquivalentModifierMask & CPControlKeyMask)
|
if (_keyEquivalentModifierMask & CPControlKeyMask)
|
||||||
string = "\u2303" + string;
|
string = "^" + string;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -684,7 +644,7 @@ CPControlKeyMask
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the title of the menu item and the mnemonic character. The mnemonic character should be preceded by an '&'.
|
Sets the title of the menu item and the mnemonic character. The mnemonic chracter should be preceded by an '&'.
|
||||||
@param aTitle the title string with a denoted mnemonic
|
@param aTitle the title string with a denoted mnemonic
|
||||||
*/
|
*/
|
||||||
- (void)setTitleWithMnemonicLocation:(CPString)aTitle
|
- (void)setTitleWithMnemonicLocation:(CPString)aTitle
|
||||||
@@ -887,11 +847,6 @@ CPControlKeyMask
|
|||||||
return ![self submenu] && [self menu] === [CPApp mainMenu];
|
return ![self submenu] && [self menu] === [CPApp mainMenu];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
return [super description] + @" target: " + [self target] + @" action: " + CPStringFromSelector([self action]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||||
@@ -935,7 +890,6 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_changeCount = 0;
|
|
||||||
_isSeparator = [aCoder containsValueForKey:CPMenuItemIsSeparatorKey] && [aCoder decodeBoolForKey:CPMenuItemIsSeparatorKey];
|
_isSeparator = [aCoder containsValueForKey:CPMenuItemIsSeparatorKey] && [aCoder decodeBoolForKey:CPMenuItemIsSeparatorKey];
|
||||||
|
|
||||||
_title = [aCoder decodeObjectForKey:CPMenuItemTitleKey];
|
_title = [aCoder decodeObjectForKey:CPMenuItemTitleKey];
|
||||||
@@ -946,33 +900,34 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
|||||||
_action = [aCoder decodeObjectForKey:CPMenuItemActionKey];
|
_action = [aCoder decodeObjectForKey:CPMenuItemActionKey];
|
||||||
|
|
||||||
_isEnabled = DEFAULT_VALUE(CPMenuItemIsEnabledKey, YES);
|
_isEnabled = DEFAULT_VALUE(CPMenuItemIsEnabledKey, YES);
|
||||||
_isHidden = [aCoder decodeBoolForKey:CPMenuItemIsHiddenKey];
|
_isHidden = DEFAULT_VALUE(CPMenuItemIsHiddenKey, NO);
|
||||||
_tag = [aCoder decodeIntForKey:CPMenuItemTagKey];
|
_tag = DEFAULT_VALUE(CPMenuItemTagKey, 0);
|
||||||
_state = [aCoder decodeIntForKey:CPMenuItemStateKey];
|
_state = DEFAULT_VALUE(CPMenuItemStateKey, CPOffState);
|
||||||
|
|
||||||
_image = [aCoder decodeObjectForKey:CPMenuItemImageKey];
|
_image = DEFAULT_VALUE(CPMenuItemImageKey, nil);
|
||||||
_alternateImage = [aCoder decodeObjectForKey:CPMenuItemAlternateImageKey];
|
_alternateImage = DEFAULT_VALUE(CPMenuItemAlternateImageKey, nil);
|
||||||
// CPImage _onStateImage;
|
// CPImage _onStateImage;
|
||||||
// CPImage _offStateImage;
|
// CPImage _offStateImage;
|
||||||
// CPImage _mixedStateImage;
|
// CPImage _mixedStateImage;
|
||||||
|
|
||||||
// This order matters because setSubmenu: needs _menu to be around.
|
// This order matters because setSubmenu: needs _menu to be around.
|
||||||
_menu = [aCoder decodeObjectForKey:CPMenuItemMenuKey];
|
_menu = DEFAULT_VALUE(CPMenuItemMenuKey, nil);
|
||||||
[self setSubmenu:[aCoder decodeObjectForKey:CPMenuItemSubmenuKey]];
|
[self setSubmenu:DEFAULT_VALUE(CPMenuItemSubmenuKey, nil)];
|
||||||
|
|
||||||
_keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @"";
|
_keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @"";
|
||||||
_keyEquivalentModifierMask = [aCoder decodeIntForKey:CPMenuItemKeyEquivalentModifierMaskKey];
|
_keyEquivalentModifierMask = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentModifierMaskKey] || 0;
|
||||||
|
|
||||||
// int _mnemonicLocation;
|
// int _mnemonicLocation;
|
||||||
|
|
||||||
// BOOL _isAlternate;
|
// BOOL _isAlternate;
|
||||||
|
|
||||||
[self setIndentationLevel:[aCoder decodeIntForKey:CPMenuItemIndentationLevelKey]];
|
// Default is 0.
|
||||||
|
[self setIndentationLevel:[aCoder decodeIntForKey:CPMenuItemIndentationLevelKey] || 0];
|
||||||
|
|
||||||
// CPString _toolTip;
|
// CPString _toolTip;
|
||||||
|
|
||||||
_representedObject = [aCoder decodeObjectForKey:CPMenuItemRepresentedObjectKey];
|
_representedObject = DEFAULT_VALUE(CPMenuItemRepresentedObjectKey, nil);
|
||||||
_view = [aCoder decodeObjectForKey:CPMenuItemViewKey];
|
_view = DEFAULT_VALUE(CPMenuItemViewKey, nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
@@ -1,61 +1,54 @@
|
|||||||
/*
|
@import "CPView.j"
|
||||||
* _CPMenuItemMenuBarView.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 "CPControl.j"
|
|
||||||
@import "_CPImageAndTextView.j"
|
|
||||||
|
|
||||||
@class _CPMenuBarWindow
|
var HORIZONTAL_MARGIN = 8.0,
|
||||||
@class _CPMenuView
|
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
|
@implementation _CPMenuItemMenuBarView : CPView
|
||||||
{
|
{
|
||||||
CPColor _highlightColor @accessors(property=highlightColor);
|
|
||||||
CPColor _textColor @accessors(property=textColor);
|
|
||||||
CPColor _textShadowColor @accessors(property=textShadowColor);
|
|
||||||
CPColor _highlightTextColor @accessors(property=highlightTextColor);
|
|
||||||
CPColor _highlightTextShadowColor @accessors(property=highlightTextShadowColor);
|
|
||||||
|
|
||||||
CPMenuItem _menuItem @accessors(property=menuItem);
|
CPMenuItem _menuItem @accessors(property=menuItem);
|
||||||
|
|
||||||
CPFont _font;
|
CPFont _font;
|
||||||
|
CPColor _textColor;
|
||||||
|
CPColor _textShadowColor;
|
||||||
|
|
||||||
BOOL _isDirty;
|
BOOL _isDirty;
|
||||||
BOOL _shouldHighlight;
|
|
||||||
|
|
||||||
_CPImageAndTextView _imageAndTextView;
|
_CPImageAndTextView _imageAndTextView;
|
||||||
|
CPView _submenuIndicatorView;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
return "menu-item-bar-view";
|
if (self !== [_CPMenuItemMenuBarView class])
|
||||||
}
|
return;
|
||||||
|
|
||||||
+ (id)themeAttributes
|
var bundle = [CPBundle bundleForClass:self];
|
||||||
{
|
|
||||||
return @{
|
SelectionColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackgroundSelected.png"] size:CGSizeMake(1.0, 28.0)]];
|
||||||
@"horizontal-margin": 9.0,
|
|
||||||
@"submenu-indicator-margin": 3.0,
|
SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
|
||||||
@"vertical-margin": 4.0,
|
|
||||||
};
|
_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
|
+ (id)view
|
||||||
@@ -69,7 +62,7 @@
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake([self valueForThemeAttribute:@"horizontal-margin"], 0.0, 0.0, 0.0)];
|
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(HORIZONTAL_MARGIN, 0.0, 0.0, 0.0)];
|
||||||
|
|
||||||
[_imageAndTextView setImagePosition:CPImageLeft];
|
[_imageAndTextView setImagePosition:CPImageLeft];
|
||||||
[_imageAndTextView setImageOffset:3.0];
|
[_imageAndTextView setImageOffset:3.0];
|
||||||
@@ -78,98 +71,88 @@
|
|||||||
|
|
||||||
[self addSubview:_imageAndTextView];
|
[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];
|
[self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPColor)setTextColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
_textColor = aColor;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)setTextShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
_textShadowColor = aColor;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)setHighlightTextColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
_highlightTextColor = aColor;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)setHighlightTextShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
_highlightTextShadowColor = aColor;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textColor
|
- (CPColor)textColor
|
||||||
{
|
{
|
||||||
if (![_menuItem isEnabled])
|
if (![_menuItem isEnabled])
|
||||||
return [CPColor lightGrayColor];
|
return [CPColor lightGrayColor];
|
||||||
|
|
||||||
return _textColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-color" forClass:_CPMenuView];
|
return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPColor)textShadowColor
|
- (CPColor)textShadowColor
|
||||||
{
|
{
|
||||||
if (![_menuItem isEnabled])
|
if (![_menuItem isEnabled])
|
||||||
return [CPColor clearColor];
|
return [CPColor colorWithWhite:0.8 alpha:0.8];
|
||||||
|
|
||||||
return _textShadowColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-shadow-color" forClass:_CPMenuView];
|
return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)highlightTextColor
|
|
||||||
{
|
|
||||||
if (![_menuItem isEnabled])
|
|
||||||
return [CPColor lightGrayColor];
|
|
||||||
|
|
||||||
return _highlightTextColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-color" forClass:_CPMenuView];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)highlightTextShadowColor
|
|
||||||
{
|
|
||||||
if (![_menuItem isEnabled])
|
|
||||||
return [CPColor clearColor];
|
|
||||||
|
|
||||||
return _highlightTextShadowColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-shadow-color" forClass:_CPMenuView];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)highlightColor
|
|
||||||
{
|
|
||||||
return _highlightColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-background-selected-color" forClass:_CPMenuView];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)update
|
- (void)update
|
||||||
{
|
{
|
||||||
var x = [self valueForThemeAttribute:@"horizontal-margin"],
|
var x = HORIZONTAL_MARGIN,
|
||||||
height = 0.0;
|
height = 0.0;
|
||||||
|
|
||||||
[_imageAndTextView setText:[_menuItem title]];
|
|
||||||
[_imageAndTextView setFont:[_menuItem font] || [_CPMenuBarWindow font]];
|
[_imageAndTextView setFont:[_menuItem font] || [_CPMenuBarWindow font]];
|
||||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
|
||||||
[_imageAndTextView setImage:[_menuItem image]];
|
[_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];
|
[_imageAndTextView sizeToFit];
|
||||||
|
|
||||||
var imageAndTextViewFrame = [_imageAndTextView frame];
|
var imageAndTextViewFrame = [_imageAndTextView frame];
|
||||||
|
|
||||||
imageAndTextViewFrame.origin.x = x;
|
imageAndTextViewFrame.origin.x = x;
|
||||||
x += CGRectGetWidth(imageAndTextViewFrame);
|
x += CGRectGetWidth(imageAndTextViewFrame);
|
||||||
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame)) + 2.0 * [self valueForThemeAttribute:@"vertical-margin"];
|
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);
|
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
||||||
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
[_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 setAutoresizesSubviews:NO];
|
||||||
[self setFrameSize:CGSizeMake(x + [self valueForThemeAttribute:@"horizontal-margin"], height)];
|
[self setFrameSize:_minSize];
|
||||||
[self setAutoresizesSubviews:YES];
|
[self setAutoresizesSubviews:YES];
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)highlight:(BOOL)shouldHighlight
|
- (void)highlight:(BOOL)shouldHighlight
|
||||||
@@ -178,20 +161,17 @@
|
|||||||
if (![_menuItem isEnabled])
|
if (![_menuItem isEnabled])
|
||||||
shouldHighlight = NO;
|
shouldHighlight = NO;
|
||||||
|
|
||||||
_shouldHighlight = shouldHighlight;
|
if (shouldHighlight)
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
if (_shouldHighlight)
|
|
||||||
{
|
{
|
||||||
if (![_menuItem _isMenuBarButton])
|
if (![_menuItem _isMenuBarButton])
|
||||||
[self setBackgroundColor:[self highlightColor]];
|
[self setBackgroundColor:SelectionColor];
|
||||||
|
|
||||||
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
||||||
[_imageAndTextView setTextColor:[self highlightTextColor]];
|
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
||||||
[_imageAndTextView setTextShadowColor:[self highlightTextShadowColor]];
|
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||||
|
|
||||||
|
[_submenuIndicatorView setColor:[CPColor whiteColor]];
|
||||||
|
[_submenuIndicatorView setShadowColor:[CPColor colorWithWhite:0.1 alpha:0.7]];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -200,7 +180,60 @@
|
|||||||
[_imageAndTextView setImage:[_menuItem image]];
|
[_imageAndTextView setImage:[_menuItem image]];
|
||||||
[_imageAndTextView setTextColor:[self textColor]];
|
[_imageAndTextView setTextColor:[self textColor]];
|
||||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||||
|
|
||||||
|
[_submenuIndicatorView setColor:[self textColor]];
|
||||||
|
[_submenuIndicatorView setShadowColor:[self textShadowColor]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@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,29 +1,6 @@
|
|||||||
/*
|
|
||||||
* _CPMenuItemSeparatorView.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 "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
@class _CPMenuItemStandardView
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPMenuItemSeparatorView : CPView
|
@implementation _CPMenuItemSeparatorView : CPView
|
||||||
{
|
{
|
||||||
@@ -54,7 +31,7 @@
|
|||||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-item-separator-color" forClass:_CPMenuItemStandardView]);
|
CGContextSetStrokeColor(context, [CPColor lightGrayColor]);
|
||||||
CGContextStrokePath(context);
|
CGContextStrokePath(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,32 @@
|
|||||||
/*
|
|
||||||
* _CPMenuItemStandardView.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 "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPImageView.j"
|
|
||||||
@import "_CPImageAndTextView.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
|
@implementation _CPMenuItemStandardView : CPView
|
||||||
{
|
{
|
||||||
CPMenuItem _menuItem @accessors(property=menuItem);
|
CPMenuItem _menuItem @accessors(property=menuItem);
|
||||||
|
|
||||||
CPFont _font;
|
CPFont _font;
|
||||||
|
CPColor _textColor;
|
||||||
|
CPColor _textShadowColor;
|
||||||
|
|
||||||
CGSize _minSize @accessors(readonly, property=minSize);
|
CGSize _minSize @accessors(readonly, property=minSize);
|
||||||
BOOL _isDirty;
|
BOOL _isDirty;
|
||||||
BOOL _highlighted;
|
|
||||||
|
|
||||||
CPImageView _stateView;
|
CPImageView _stateView;
|
||||||
_CPImageAndTextView _imageAndTextView;
|
_CPImageAndTextView _imageAndTextView;
|
||||||
@@ -40,32 +34,26 @@
|
|||||||
CPView _submenuIndicatorView;
|
CPView _submenuIndicatorView;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
return "menu-item-standard-view";
|
if (self !== [_CPMenuItemStandardView class])
|
||||||
}
|
return;
|
||||||
|
|
||||||
+ (id)themeAttributes
|
SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
|
||||||
{
|
|
||||||
return @{
|
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
||||||
@"submenu-indicator-color": [CPNull null],
|
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
|
||||||
@"menu-item-selection-color": [CPNull null],
|
|
||||||
@"menu-item-text-shadow-color": [CPNull null],
|
var bundle = [CPBundle bundleForClass:self];
|
||||||
@"menu-item-text-color": [CPNull null],
|
|
||||||
@"menu-item-default-off-state-image": [CPNull null],
|
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
||||||
@"menu-item-default-off-state-highlighted-image": [CPNull null],
|
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
||||||
@"menu-item-default-on-state-image": [CPNull null],
|
|
||||||
@"menu-item-default-on-state-highlighted-image": [CPNull null],
|
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
||||||
@"menu-item-default-mixed-state-image": [CPNull null],
|
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
||||||
@"menu-item-default-mixed-state-highlighted-image": [CPNull null],
|
|
||||||
@"menu-item-separator-color": [CPNull null],
|
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
||||||
@"left-margin": 3.0,
|
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
||||||
@"right-margin": 17.0,
|
|
||||||
@"state-column-width": 14.0,
|
|
||||||
@"indentation-width": 17.0,
|
|
||||||
@"vertical-margin": 4.0,
|
|
||||||
@"right-columns-margin": 30.0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)view
|
+ (id)view
|
||||||
@@ -75,7 +63,7 @@
|
|||||||
|
|
||||||
+ (float)_standardLeftMargin
|
+ (float)_standardLeftMargin
|
||||||
{
|
{
|
||||||
return [[CPTheme defaultTheme] valueForAttributeWithName:@"left-margin" forClass:[self class]] + [[CPTheme defaultTheme] valueForAttributeWithName:@"state-column-width" forClass:[self class]];
|
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
@@ -86,7 +74,7 @@
|
|||||||
{
|
{
|
||||||
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||||
|
|
||||||
[_stateView setImageScaling:CPImageScaleNone];
|
[_stateView setImageScaling:CPScaleNone];
|
||||||
|
|
||||||
[self addSubview:_stateView];
|
[self addSubview:_stateView];
|
||||||
|
|
||||||
@@ -107,7 +95,7 @@
|
|||||||
|
|
||||||
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
|
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
|
||||||
|
|
||||||
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
|
[_submenuIndicatorView setColor:SUBMENU_INDICATOR_COLOR];
|
||||||
[_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin];
|
[_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin];
|
||||||
|
|
||||||
[self addSubview:_submenuIndicatorView];
|
[self addSubview:_submenuIndicatorView];
|
||||||
@@ -123,10 +111,7 @@
|
|||||||
if (![_menuItem isEnabled])
|
if (![_menuItem isEnabled])
|
||||||
return [CPColor lightGrayColor];
|
return [CPColor lightGrayColor];
|
||||||
|
|
||||||
if (_highlighted)
|
return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
|
||||||
return [CPColor whiteColor];
|
|
||||||
|
|
||||||
return [self valueForThemeAttribute:@"menu-item-text-color"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPColor)textShadowColor
|
- (CPColor)textShadowColor
|
||||||
@@ -134,10 +119,7 @@
|
|||||||
if (![_menuItem isEnabled])
|
if (![_menuItem isEnabled])
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
if (_highlighted)
|
return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
|
||||||
return nil;
|
|
||||||
|
|
||||||
return [self valueForThemeAttribute:@"menu-item-text-shadow-color"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
- (void)setFont:(CPFont)aFont
|
||||||
@@ -147,34 +129,16 @@
|
|||||||
|
|
||||||
- (void)update
|
- (void)update
|
||||||
{
|
{
|
||||||
var x = [self valueForThemeAttribute:@"left-margin"] + [_menuItem indentationLevel] * [self valueForThemeAttribute:@"indentation-width"],
|
var x = LEFT_MARGIN + [_menuItem indentationLevel] * INDENTATION_WIDTH,
|
||||||
height = 0.0,
|
height = 0.0,
|
||||||
hasStateColumn = [[_menuItem menu] showsStateColumn];
|
hasStateColumn = [[_menuItem menu] showsStateColumn];
|
||||||
|
|
||||||
if (hasStateColumn)
|
if (hasStateColumn)
|
||||||
{
|
{
|
||||||
[_stateView setHidden:NO];
|
[_stateView setHidden:NO];
|
||||||
//[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
|
[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
|
||||||
|
|
||||||
switch ([_menuItem state])
|
x += STATE_COLUMN_WIDTH;
|
||||||
{
|
|
||||||
case CPOnState:
|
|
||||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image"]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPOffState:
|
|
||||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image"]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPMixedState:
|
|
||||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image"]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
x += [self valueForThemeAttribute:@"state-column-width"];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[_stateView setHidden:YES];
|
[_stateView setHidden:YES];
|
||||||
@@ -198,7 +162,7 @@
|
|||||||
hasSubmenu = [_menuItem hasSubmenu];
|
hasSubmenu = [_menuItem hasSubmenu];
|
||||||
|
|
||||||
if (hasKeyEquivalent || hasSubmenu)
|
if (hasKeyEquivalent || hasSubmenu)
|
||||||
x += [self valueForThemeAttribute:@"right-columns-margin"];
|
x += RIGHT_COLUMNS_MARGIN;
|
||||||
|
|
||||||
if (hasKeyEquivalent)
|
if (hasKeyEquivalent)
|
||||||
{
|
{
|
||||||
@@ -209,7 +173,7 @@
|
|||||||
[_keyEquivalentView setTextColor:[self textColor]];
|
[_keyEquivalentView setTextColor:[self textColor]];
|
||||||
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
||||||
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0, 1)];
|
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0, 1)];
|
||||||
[_keyEquivalentView setFrameOrigin:CGPointMake(x, [self valueForThemeAttribute:@"vertical-margin"])];
|
[_keyEquivalentView setFrameOrigin:CGPointMake(x, VERTICAL_MARGIN)];
|
||||||
[_keyEquivalentView sizeToFit];
|
[_keyEquivalentView sizeToFit];
|
||||||
|
|
||||||
var keyEquivalentViewFrame = [_keyEquivalentView frame];
|
var keyEquivalentViewFrame = [_keyEquivalentView frame];
|
||||||
@@ -219,7 +183,7 @@
|
|||||||
height = MAX(height, CGRectGetHeight(keyEquivalentViewFrame));
|
height = MAX(height, CGRectGetHeight(keyEquivalentViewFrame));
|
||||||
|
|
||||||
if (hasSubmenu)
|
if (hasSubmenu)
|
||||||
x += [self valueForThemeAttribute:@"right-columns-margin"];
|
x += RIGHT_COLUMNS_MARGIN;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[_keyEquivalentView setHidden:YES];
|
[_keyEquivalentView setHidden:YES];
|
||||||
@@ -238,13 +202,13 @@
|
|||||||
else
|
else
|
||||||
[_submenuIndicatorView setHidden:YES];
|
[_submenuIndicatorView setHidden:YES];
|
||||||
|
|
||||||
height += 2.0 * [self valueForThemeAttribute:@"vertical-margin"];
|
height += 2.0 * VERTICAL_MARGIN;
|
||||||
|
|
||||||
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
||||||
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
||||||
|
|
||||||
if (hasStateColumn)
|
if (hasStateColumn)
|
||||||
[_stateView setFrameSize:CGSizeMake([self valueForThemeAttribute:@"state-column-width"], height)];
|
[_stateView setFrameSize:CGSizeMake(STATE_COLUMN_WIDTH, height)];
|
||||||
|
|
||||||
if (hasKeyEquivalent)
|
if (hasKeyEquivalent)
|
||||||
{
|
{
|
||||||
@@ -258,7 +222,7 @@
|
|||||||
[_submenuIndicatorView setFrame:submenuViewFrame];
|
[_submenuIndicatorView setFrame:submenuViewFrame];
|
||||||
}
|
}
|
||||||
|
|
||||||
_minSize = CGSizeMake(x + [self valueForThemeAttribute:@"right-margin"], height);
|
_minSize = CGSizeMake(x + RIGHT_MARGIN, height);
|
||||||
|
|
||||||
[self setAutoresizesSubviews:NO];
|
[self setAutoresizesSubviews:NO];
|
||||||
[self setFrameSize:_minSize];
|
[self setFrameSize:_minSize];
|
||||||
@@ -271,76 +235,40 @@
|
|||||||
if (![_menuItem isEnabled])
|
if (![_menuItem isEnabled])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_highlighted = shouldHighlight;
|
|
||||||
|
|
||||||
[_imageAndTextView setTextColor:[self textColor]];
|
|
||||||
[_keyEquivalentView setTextColor:[self textColor]];
|
|
||||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
|
||||||
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
|
||||||
|
|
||||||
if (shouldHighlight)
|
if (shouldHighlight)
|
||||||
{
|
{
|
||||||
[self setBackgroundColor:[self valueForThemeAttribute:@"menu-item-selection-color"]];
|
[self setBackgroundColor:_CPMenuItemSelectionColor];
|
||||||
|
|
||||||
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
||||||
[_submenuIndicatorView setColor:[self textColor]];
|
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
||||||
|
[_keyEquivalentView setTextColor:[CPColor whiteColor]];
|
||||||
|
[_submenuIndicatorView setColor:[CPColor whiteColor]];
|
||||||
|
|
||||||
|
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||||
|
[_keyEquivalentView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[self setBackgroundColor:nil];
|
[self setBackgroundColor:nil];
|
||||||
|
|
||||||
[_imageAndTextView setImage:[_menuItem image]];
|
[_imageAndTextView setImage:[_menuItem image]];
|
||||||
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
|
[_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 ([[_menuItem menu] showsStateColumn])
|
||||||
{
|
{
|
||||||
if (shouldHighlight)
|
if (shouldHighlight)
|
||||||
{
|
[_stateView setImage:_CPMenuItemDefaultStateHighlightedImages[[_menuItem state]] || nil];
|
||||||
switch ([_menuItem state])
|
|
||||||
{
|
|
||||||
case CPOnState:
|
|
||||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-highlighted-image"]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPOffState:
|
|
||||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-highlighted-image"]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPMixedState:
|
|
||||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-highlighted-image"]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
|
||||||
switch ([_menuItem state])
|
|
||||||
{
|
|
||||||
case CPOnState:
|
|
||||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image"]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPOffState:
|
|
||||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image"]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPMixedState:
|
|
||||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image"]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isHighlighted
|
|
||||||
{
|
|
||||||
return _highlighted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation _CPMenuItemSubmenuIndicatorView : CPView
|
@implementation _CPMenuItemSubmenuIndicatorView : CPView
|
||||||
|
|||||||
@@ -1,32 +1,22 @@
|
|||||||
/*
|
|
||||||
* _CPMenuItemView.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 "CPControl.j"
|
@import <AppKit/CPControl.j>
|
||||||
|
|
||||||
@import "_CPMenuItemSeparatorView.j"
|
@import "_CPMenuItemSeparatorView.j"
|
||||||
@import "_CPMenuItemStandardView.j"
|
@import "_CPMenuItemStandardView.j"
|
||||||
@import "_CPMenuItemMenuBarView.j"
|
@import "_CPMenuItemMenuBarView.j"
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
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
|
@ignore
|
||||||
@@ -34,7 +24,7 @@
|
|||||||
@implementation _CPMenuItemView : CPView
|
@implementation _CPMenuItemView : CPView
|
||||||
{
|
{
|
||||||
CPMenuItem _menuItem;
|
CPMenuItem _menuItem;
|
||||||
CPView _view @accessors(property=view, readonly);
|
CPView _view;
|
||||||
|
|
||||||
CPFont _font;
|
CPFont _font;
|
||||||
CPColor _textColor;
|
CPColor _textColor;
|
||||||
@@ -44,26 +34,36 @@
|
|||||||
BOOL _isDirty;
|
BOOL _isDirty;
|
||||||
BOOL _showsStateColumn;
|
BOOL _showsStateColumn;
|
||||||
|
|
||||||
|
CPImageView _stateView;
|
||||||
_CPImageAndTextView _imageAndTextView;
|
_CPImageAndTextView _imageAndTextView;
|
||||||
CPView _submenuView;
|
CPView _submenuView;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
return "menu-item-view";
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)themeAttributes
|
+ (float)leftMargin
|
||||||
{
|
{
|
||||||
return @{};
|
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not used in the Appkit
|
|
||||||
// + (float)leftMargin
|
|
||||||
// {
|
|
||||||
// return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
|
||||||
// }
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem
|
- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
- (void)setFont:(CPFont)aFont
|
||||||
{
|
{
|
||||||
if ([_font isEqual:aFont])
|
if (_font === aFont)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_font = aFont;
|
_font = aFont;
|
||||||
@@ -220,36 +220,6 @@
|
|||||||
return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8];
|
return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setParentMenuHighlightColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if ([_view respondsToSelector:@selector(setHighlightColor:)])
|
|
||||||
[_view setHighlightColor:aColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setParentMenuHighlightTextColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if ([_view respondsToSelector:@selector(setHighlightTextColor:)])
|
|
||||||
[_view setHighlightTextColor:aColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setParentMenuHighlightTextShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if ([_view respondsToSelector:@selector(setHighlightTextShadowColor:)])
|
|
||||||
[_view setHighlightTextShadowColor:aColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setParentMenuTextColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if ([_view respondsToSelector:@selector(setTextColor:)])
|
|
||||||
[_view setTextColor:aColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setParentMenuTextShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
if ([_view respondsToSelector:@selector(setTextShadowColor:)])
|
|
||||||
[_view setTextShadowColor:aColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation _CPMenuItemArrowView : CPView
|
@implementation _CPMenuItemArrowView : CPView
|
||||||
@@ -276,6 +246,8 @@
|
|||||||
CGContextMoveToPoint(context, 1.0, 4.0);
|
CGContextMoveToPoint(context, 1.0, 4.0);
|
||||||
CGContextAddLineToPoint(context, 9.0, 4.0);
|
CGContextAddLineToPoint(context, 9.0, 4.0);
|
||||||
CGContextAddLineToPoint(context, 5.0, 8.0);
|
CGContextAddLineToPoint(context, 5.0, 8.0);
|
||||||
|
CGContextAddLineToPoint(context, 1.0, 4.0);
|
||||||
|
|
||||||
CGContextClosePath(context);
|
CGContextClosePath(context);
|
||||||
|
|
||||||
CGContextSetFillColor(context, _color);
|
CGContextSetFillColor(context, _color);
|
||||||
|
|||||||
+74
-271
@@ -1,49 +1,16 @@
|
|||||||
/*
|
|
||||||
* CPObjectController.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/CPDictionary.j>
|
@import <Foundation/CPDictionary.j>
|
||||||
@import <Foundation/CPCountedSet.j>
|
@import <Foundation/CPCountedSet.j>
|
||||||
@import <Foundation/_CPCollectionKVCOperators.j>
|
|
||||||
|
|
||||||
@import "CPController.j"
|
@import "CPController.j"
|
||||||
@import "CPKeyValueBinding.j"
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@class
|
|
||||||
|
|
||||||
CPObjectController is a bindings-compatible controller class.
|
|
||||||
Properties of the content object of an object of this class can be bound to user interface elements to change and access their values.
|
|
||||||
|
|
||||||
The content of an CPObjectController instance is an CPMutableDictionary object by default.
|
|
||||||
This allows a single CPObjectController instance to be used to manage several properties accessed by key value paths.
|
|
||||||
The default content object class can be changed by calling setObjectClass:, which a subclass must override.
|
|
||||||
*/
|
|
||||||
@implementation CPObjectController : CPController
|
@implementation CPObjectController : CPController
|
||||||
{
|
{
|
||||||
id _contentObject;
|
id _contentObject;
|
||||||
id _selection;
|
id _selection;
|
||||||
|
|
||||||
Class _objectClass;
|
Class _objectClass;
|
||||||
CPString _objectClassName;
|
|
||||||
|
|
||||||
BOOL _isEditable;
|
BOOL _isEditable;
|
||||||
BOOL _automaticallyPreparesContent;
|
BOOL _automaticallyPreparesContent;
|
||||||
@@ -53,9 +20,6 @@
|
|||||||
|
|
||||||
+ (id)initialize
|
+ (id)initialize
|
||||||
{
|
{
|
||||||
if (self !== [CPObjectController class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self exposeBinding:@"editable"];
|
[self exposeBinding:@"editable"];
|
||||||
[self exposeBinding:@"contentObject"];
|
[self exposeBinding:@"contentObject"];
|
||||||
}
|
}
|
||||||
@@ -88,49 +52,30 @@
|
|||||||
return [CPSet setWithObjects:"editable", "selection"];
|
return [CPSet setWithObjects:"editable", "selection"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
return [self initWithContent:nil];
|
return [self initWithContent:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Inits and returns a CPObjectController object with the given content.
|
|
||||||
|
|
||||||
@param id aContent - The object the controller will use.
|
|
||||||
@return id the CPObjectConroller instance.
|
|
||||||
*/
|
|
||||||
- (id)initWithContent:(id)aContent
|
- (id)initWithContent:(id)aContent
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
{
|
{
|
||||||
|
[self setContent:aContent];
|
||||||
[self setEditable:YES];
|
[self setEditable:YES];
|
||||||
[self setObjectClass:[CPMutableDictionary class]];
|
[self setObjectClass:[CPMutableDictionary class]];
|
||||||
|
|
||||||
_observedKeys = [[CPCountedSet alloc] init];
|
_observedKeys = [[CPCountedSet alloc] init];
|
||||||
_selection = [[CPControllerSelectionProxy alloc] initWithController:self];
|
|
||||||
|
|
||||||
[self setContent:aContent];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the controller's content object.
|
|
||||||
@return id - The content object of the controller.
|
|
||||||
*/
|
|
||||||
- (id)content
|
- (id)content
|
||||||
{
|
{
|
||||||
return _contentObject;
|
return _contentObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the content object for the controller.
|
|
||||||
@param id aContent - The new content object for the controller.
|
|
||||||
*/
|
|
||||||
- (void)setContent:(id)aContent
|
- (void)setContent:(id)aContent
|
||||||
{
|
{
|
||||||
[self willChangeValueForKey:@"contentObject"];
|
[self willChangeValueForKey:@"contentObject"];
|
||||||
@@ -138,197 +83,113 @@
|
|||||||
|
|
||||||
_contentObject = aContent;
|
_contentObject = aContent;
|
||||||
|
|
||||||
[self _selectionDidChange];
|
|
||||||
[self didChangeValueForKey:@"contentObject"];
|
[self didChangeValueForKey:@"contentObject"];
|
||||||
|
[self _selectionDidChange];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_setContentObject:(id)aContent
|
- (void)_setContentObject:(id)aContent
|
||||||
{
|
{
|
||||||
[self setContent:aContent];
|
[self setContent:aContent];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (id)_contentObject
|
- (id)_contentObject
|
||||||
{
|
{
|
||||||
return [self content];
|
return [self content];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets whether the controller automatically creates and inserts new content objects automatically when loading from a cib file.
|
|
||||||
If you pass YES and the controller uses prepareContent to create the content object.
|
|
||||||
The default is NO.
|
|
||||||
|
|
||||||
@param BOOL shouldAutomaticallyPrepareContent - YES if the content should be prepared, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setAutomaticallyPreparesContent:(BOOL)shouldAutomaticallyPrepareContent
|
- (void)setAutomaticallyPreparesContent:(BOOL)shouldAutomaticallyPrepareContent
|
||||||
{
|
{
|
||||||
_automaticallyPreparesContent = shouldAutomaticallyPrepareContent;
|
_automaticallyPreparesContent = shouldAutomaticallyPrepareContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns if the controller prepares the content automatically.
|
|
||||||
@return BOOL - YES if the content is prepared, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)automaticallyPreparesContent
|
- (BOOL)automaticallyPreparesContent
|
||||||
{
|
{
|
||||||
return _automaticallyPreparesContent;
|
return _automaticallyPreparesContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Overridden by a subclass that require control over the creation of new objects.
|
|
||||||
*/
|
|
||||||
- (void)prepareContent
|
- (void)prepareContent
|
||||||
{
|
{
|
||||||
[self setContent:[self newObject]];
|
[self setContent:[self newObject]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the object class when creating new objects.
|
|
||||||
@param Class - the class of new objects that will be created.
|
|
||||||
*/
|
|
||||||
- (void)setObjectClass:(Class)aClass
|
- (void)setObjectClass:(Class)aClass
|
||||||
{
|
{
|
||||||
_objectClass = aClass;
|
_objectClass = aClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the class of what new objects will be when they are created.
|
|
||||||
|
|
||||||
@return Class - The class of new objects.
|
|
||||||
*/
|
|
||||||
- (Class)objectClass
|
- (Class)objectClass
|
||||||
{
|
{
|
||||||
return _objectClass;
|
return _objectClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (id)newObject
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (id)_defaultNewObject
|
|
||||||
{
|
{
|
||||||
return [[[self objectClass] alloc] init];
|
return [[[self objectClass] alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Creates and returns a new object of the appropriate class.
|
|
||||||
@return id - The object created.
|
|
||||||
*/
|
|
||||||
- (id)newObject
|
|
||||||
{
|
|
||||||
return [self _defaultNewObject];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the controller's content object.
|
|
||||||
@param id anObject - The object to set for the controller.
|
|
||||||
*/
|
|
||||||
- (void)addObject:(id)anObject
|
- (void)addObject:(id)anObject
|
||||||
{
|
{
|
||||||
[self setContent:anObject];
|
[self setContent:anObject];
|
||||||
|
|
||||||
var binderClass = [[self class] _binderClassForBinding:@"contentObject"];
|
[[CPKeyValueBinding getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
|
||||||
[[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Removes a given object from the controller.
|
|
||||||
@param id anObject - The object to remove from the receiver.
|
|
||||||
*/
|
|
||||||
- (void)removeObject:(id)anObject
|
- (void)removeObject:(id)anObject
|
||||||
{
|
{
|
||||||
if ([self content] === anObject)
|
if ([self content] === anObject)
|
||||||
[self setContent:nil];
|
[self setContent:nil];
|
||||||
|
|
||||||
var binderClass = [[self class] _binderClassForBinding:@"contentObject"];
|
[[CPKeyValueBinding getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
|
||||||
[[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Creates and adds a sets the object as the controller's content.
|
|
||||||
@param id aSender - The sender of the message.
|
|
||||||
*/
|
|
||||||
- (void)add:(id)aSender
|
- (void)add:(id)aSender
|
||||||
{
|
{
|
||||||
// FIXME: This should happen on the next run loop?
|
// FIXME: This should happen on the next run loop?
|
||||||
[self addObject:[self newObject]];
|
[self addObject:[self newObject]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@return BOOL - YES if you can added to the controller using add:
|
|
||||||
*/
|
|
||||||
- (BOOL)canAdd
|
- (BOOL)canAdd
|
||||||
{
|
{
|
||||||
return [self isEditable];
|
return [self isEditable];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Removes the content object from the controller.
|
|
||||||
@param id aSender - The sender of the message.
|
|
||||||
*/
|
|
||||||
- (void)remove:(id)aSender
|
- (void)remove:(id)aSender
|
||||||
{
|
{
|
||||||
// FIXME: This should happen on the next run loop?
|
// FIXME: This should happen on the next run loop?
|
||||||
[self removeObject:[self content]];
|
[self removeObject:[self content]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@return BOOL - Returns YES if you can remove the controller's content using remove:
|
|
||||||
*/
|
|
||||||
- (BOOL)canRemove
|
- (BOOL)canRemove
|
||||||
{
|
{
|
||||||
return [self isEditable] && [[self selectedObjects] count];
|
return [self isEditable] && [[self selectedObjects] count];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets whether the controller allows for the editing of the content.
|
|
||||||
@param BOOL shouldBeEditable - YES if the content should be editable, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setEditable:(BOOL)shouldBeEditable
|
- (void)setEditable:(BOOL)shouldBeEditable
|
||||||
{
|
{
|
||||||
_isEditable = shouldBeEditable;
|
_isEditable = shouldBeEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@return BOOL - Returns YES if the content of the controller is editable, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)isEditable
|
- (BOOL)isEditable
|
||||||
{
|
{
|
||||||
return _isEditable;
|
return _isEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@return CPArray - Returns an array of all objects to be affected by editing.
|
|
||||||
*/
|
|
||||||
- (CPArray)selectedObjects
|
- (CPArray)selectedObjects
|
||||||
{
|
{
|
||||||
return [[_CPObservableArray alloc] initWithArray:[_contentObject]];
|
return [[_CPObservableArray alloc] initWithObjects:[_contentObject] count:1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a proxy object representing the controller's selection.
|
|
||||||
*/
|
|
||||||
- (id)selection
|
- (id)selection
|
||||||
{
|
{
|
||||||
return _selection;
|
return _selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_selectionWillChange
|
- (void)_selectionWillChange
|
||||||
{
|
{
|
||||||
[_selection controllerWillChange];
|
[_selection controllerWillChange];
|
||||||
[self willChangeValueForKey:@"selection"];
|
[self willChangeValueForKey:@"selection"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_selectionDidChange
|
- (void)_selectionDidChange
|
||||||
{
|
{
|
||||||
if (_selection === undefined || _selection === nil)
|
if (_selection === undefined || _selection === nil)
|
||||||
@@ -338,9 +199,6 @@
|
|||||||
[self didChangeValueForKey:@"selection"];
|
[self didChangeValueForKey:@"selection"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@return id - Returns the keys which are being observed.
|
|
||||||
*/
|
|
||||||
- (id)observedKeys
|
- (id)observedKeys
|
||||||
{
|
{
|
||||||
return _observedKeys;
|
return _observedKeys;
|
||||||
@@ -360,8 +218,7 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPObjectControllerContentKey = @"CPObjectControllerContentKey",
|
var CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
|
||||||
CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
|
|
||||||
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
|
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
|
||||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
|
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
|
||||||
|
|
||||||
@@ -376,10 +233,11 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
|
var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
|
||||||
objectClass = CPClassFromString(objectClassName);
|
objectClass = CPClassFromString(objectClassName);
|
||||||
|
|
||||||
[self setObjectClass:objectClass || [CPMutableDictionary class]];
|
// FIXME: Error if objectClass === nil
|
||||||
|
|
||||||
|
[self setObjectClass:objectClass];
|
||||||
[self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
|
[self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
|
||||||
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey]];
|
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey] || NO];
|
||||||
[self setContent:[aCoder decodeObjectForKey:CPObjectControllerContentKey]];
|
|
||||||
|
|
||||||
_observedKeys = [[CPCountedSet alloc] init];
|
_observedKeys = [[CPCountedSet alloc] init];
|
||||||
}
|
}
|
||||||
@@ -389,21 +247,11 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
[aCoder encodeObject:[self content] forKey:CPObjectControllerContentKey];
|
[aCoder encodeObject:CPStringFromClass(objectClass) forKey:CPObjectControllerObjectClassNameKey];
|
||||||
|
[aCoder encodeObject:[self isEditable] forKey:CPObjectControllerIsEditableKey];
|
||||||
|
|
||||||
if (_objectClass)
|
if (![self automaticallyPreparesContent])
|
||||||
[aCoder encodeObject:CPStringFromClass(_objectClass) forKey:CPObjectControllerObjectClassNameKey];
|
[aCoder encodeBOOL:YES forKey:CPObjectControllerAutomaticallyPreparesContentKey];
|
||||||
else if (_objectClassName)
|
|
||||||
[aCoder encodeObject:_objectClassName forKey:CPObjectControllerObjectClassNameKey];
|
|
||||||
|
|
||||||
[aCoder encodeBool:[self isEditable] forKey:CPObjectControllerIsEditableKey];
|
|
||||||
[aCoder encodeBool:[self automaticallyPreparesContent] forKey:CPObjectControllerAutomaticallyPreparesContentKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromCib
|
|
||||||
{
|
|
||||||
if (![self content] && [self automaticallyPreparesContent])
|
|
||||||
[self prepareContent];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -422,7 +270,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
|
|
||||||
- (id)initWithKeyPath:(id)aKeyPath observer:(id)anObserver object:(id)anObject
|
- (id)initWithKeyPath:(id)aKeyPath observer:(id)anObserver object:(id)anObject
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self=[super init])
|
||||||
{
|
{
|
||||||
_keyPath = aKeyPath;
|
_keyPath = aKeyPath;
|
||||||
_observer = anObserver;
|
_observer = anObserver;
|
||||||
@@ -471,9 +319,9 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)change context:(id)context
|
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)change context:(id)context
|
||||||
{
|
{
|
||||||
if (_notifyObject)
|
if (_notifyObject)
|
||||||
[_object observeValueForKeyPath:aKeyPath ofObject:_object change:change context:context];
|
[_object observeValueForKeyPath:_keyPath ofObject:_object change:change context:context];
|
||||||
|
|
||||||
[_observer observeValueForKeyPath:aKeyPath ofObject:_object change:change context:context];
|
[_observer observeValueForKeyPath:_keyPath ofObject:_object change:change context:context];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPString)description
|
- (CPString)description
|
||||||
@@ -483,8 +331,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
// FIXME: This should subclass CPMutableArray not _CPJavaScriptArray
|
@implementation _CPObservableArray : CPMutableArray
|
||||||
@implementation _CPObservableArray : _CPJavaScriptArray
|
|
||||||
{
|
{
|
||||||
CPArray _observationProxies;
|
CPArray _observationProxies;
|
||||||
}
|
}
|
||||||
@@ -505,27 +352,33 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
|
|
||||||
- (CPString)description
|
- (CPString)description
|
||||||
{
|
{
|
||||||
return "<_CPObservableArray: " + [super description] + " >";
|
return "<_CPObservableArray: "+[super description]+" >";
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithArray:(CPArray)anArray
|
- (id)initWithArray:(CPArray)anArray
|
||||||
{
|
{
|
||||||
self = [super initWithArray:anArray];
|
if (self = [super initWithArray:anArray])
|
||||||
|
{
|
||||||
self.isa = [_CPObservableArray class];
|
_observationProxies = [];
|
||||||
self._observationProxies = [];
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
|
- (id)initWithObjects:(CPArray)objects count:(unsigned)count
|
||||||
{
|
{
|
||||||
if (aKeyPath.charAt(0) === "@")
|
if (self = [super initWithObjects:objects count:count])
|
||||||
{
|
{
|
||||||
// Simple collection operators are scalar and can't be proxied
|
_observationProxies = [];
|
||||||
if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
|
}
|
||||||
return;
|
|
||||||
|
|
||||||
|
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];
|
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self];
|
||||||
|
|
||||||
proxy._options = options;
|
proxy._options = options;
|
||||||
@@ -534,7 +387,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
[_observationProxies addObject:proxy];
|
[_observationProxies addObject:proxy];
|
||||||
|
|
||||||
var dotIndex = aKeyPath.indexOf("."),
|
var dotIndex = aKeyPath.indexOf("."),
|
||||||
remaining = aKeyPath.substring(dotIndex + 1),
|
remaining = aKeyPath.substring(dotIndex+1),
|
||||||
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
||||||
|
|
||||||
[self addObserver:proxy toObjectsAtIndexes:indexes forKeyPath:remaining options:options context:context];
|
[self addObserver:proxy toObjectsAtIndexes:indexes forKeyPath:remaining options:options context:context];
|
||||||
@@ -548,19 +401,15 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
|
|
||||||
- (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
|
- (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
|
||||||
{
|
{
|
||||||
if (aKeyPath.charAt(0) === "@")
|
if (aKeyPath.indexOf("@") === 0)
|
||||||
{
|
{
|
||||||
// Simple collection operators are scalar and can't be proxied
|
|
||||||
if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self],
|
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self],
|
||||||
index = [_observationProxies indexOfObject:proxy];
|
index = [_observationProxies indexOfObject:proxy];
|
||||||
|
|
||||||
proxy = [_observationProxies objectAtIndex:index];
|
proxy = [_observationProxies objectAtIndex:index];
|
||||||
|
|
||||||
var dotIndex = aKeyPath.indexOf("."),
|
var dotIndex = aKeyPath.indexOf("."),
|
||||||
remaining = aKeyPath.substring(dotIndex + 1),
|
remaining = aKeyPath.substring(dotIndex+1),
|
||||||
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
||||||
|
|
||||||
[self removeObserver:proxy fromObjectsAtIndexes:indexes forKeyPath:remaining];
|
[self removeObserver:proxy fromObjectsAtIndexes:indexes forKeyPath:remaining];
|
||||||
@@ -574,11 +423,11 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
|
|
||||||
- (void)insertObject:(id)anObject atIndex:(unsigned)anIndex
|
- (void)insertObject:(id)anObject atIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
for (var i = 0, count = [_observationProxies count]; i < count; i++)
|
for (var i=0, count=[_observationProxies count]; i<count; i++)
|
||||||
{
|
{
|
||||||
var proxy = [_observationProxies objectAtIndex:i],
|
var proxy = [_observationProxies objectAtIndex:i],
|
||||||
keyPath = [proxy keyPath],
|
keyPath = [proxy keyPath],
|
||||||
operator = keyPath.charAt(0) === ".";
|
operator = keyPath.indexOf(".") === 0;
|
||||||
|
|
||||||
if (operator)
|
if (operator)
|
||||||
[self willChangeValueForKey:keyPath];
|
[self willChangeValueForKey:keyPath];
|
||||||
@@ -594,18 +443,16 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
|
|
||||||
- (void)removeObjectAtIndex:(unsigned)anIndex
|
- (void)removeObjectAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
var currentObject = [self objectAtIndex:anIndex];
|
for (var i=0, count=[_observationProxies count]; i<count; i++)
|
||||||
|
|
||||||
for (var i = 0, count = [_observationProxies count]; i < count; i++)
|
|
||||||
{
|
{
|
||||||
var proxy = [_observationProxies objectAtIndex:i],
|
var proxy = [_observationProxies objectAtIndex:i],
|
||||||
keyPath = [proxy keyPath],
|
keyPath = [proxy keyPath],
|
||||||
operator = keyPath.charAt(0) === ".";
|
operator = keyPath.indexOf(".") === 0;
|
||||||
|
|
||||||
if (operator)
|
if (operator)
|
||||||
[self willChangeValueForKey:keyPath];
|
[self willChangeValueForKey:keyPath];
|
||||||
|
|
||||||
[currentObject removeObserver:proxy forKeyPath:keyPath];
|
[anObject removeObserver:proxy forKeyPath:keyPath];
|
||||||
|
|
||||||
if (operator)
|
if (operator)
|
||||||
[self didChangeValueForKey:keyPath];
|
[self didChangeValueForKey:keyPath];
|
||||||
@@ -633,11 +480,11 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
{
|
{
|
||||||
var currentObject = [self objectAtIndex:anIndex];
|
var currentObject = [self objectAtIndex:anIndex];
|
||||||
|
|
||||||
for (var i = 0, count = [_observationProxies count]; i < count; i++)
|
for (var i=0, count=[_observationProxies count]; i<count; i++)
|
||||||
{
|
{
|
||||||
var proxy = [_observationProxies objectAtIndex:i],
|
var proxy = [_observationProxies objectAtIndex:i],
|
||||||
keyPath = [proxy keyPath],
|
keyPath = [proxy keyPath],
|
||||||
operator = keyPath.charAt(0) === ".";
|
operator = keyPath.indexOf(".") === 0;
|
||||||
|
|
||||||
if (operator)
|
if (operator)
|
||||||
[self willChangeValueForKey:keyPath];
|
[self willChangeValueForKey:keyPath];
|
||||||
@@ -656,102 +503,60 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
|
|
||||||
@implementation CPControllerSelectionProxy : CPObject
|
@implementation CPControllerSelectionProxy : CPObject
|
||||||
{
|
{
|
||||||
id _controller;
|
id _controller;
|
||||||
id _keys;
|
id _keys;
|
||||||
|
|
||||||
CPDictionary _cachedValues;
|
CPDictionary _cachedValues;
|
||||||
CPArray _observationProxies;
|
CPArray _observationProxies;
|
||||||
|
|
||||||
Object _observedObjectsByKeyPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithController:(id)aController
|
- (id)initWithController:(id)aController
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
{
|
{
|
||||||
_cachedValues = @{};
|
_cachedValues = [CPDictionary dictionary];
|
||||||
_observationProxies = [CPArray array];
|
_observationProxies = [CPArray array];
|
||||||
_controller = aController;
|
_controller = aController;
|
||||||
_observedObjectsByKeyPath = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)_controllerMarkerForValues:(CPArray)theValues
|
- (id)valueForKey:(CPString)aKey
|
||||||
{
|
{
|
||||||
var count = [theValues count],
|
var value = [_cachedValues objectForKey:aKey];
|
||||||
value;
|
|
||||||
|
if (value !== undefined && value !== nil)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
var allValues = [[_controller selectedObjects] valueForKeyPath:aKey],
|
||||||
|
count = [allValues count];
|
||||||
|
|
||||||
if (!count)
|
if (!count)
|
||||||
value = CPNoSelectionMarker;
|
value = CPNoSelectionMarker;
|
||||||
else if (count === 1)
|
else if (count === 1)
|
||||||
value = [theValues objectAtIndex:0];
|
value = [allValues objectAtIndex:0];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ([_controller alwaysUsesMultipleValuesMarker])
|
if ([_controller alwaysUsesMultipleValuesMarker])
|
||||||
value = CPMultipleValuesMarker;
|
value = CPMultipleValuesMarker;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
value = [theValues objectAtIndex:0];
|
value = [allValues objectAtIndex:0];
|
||||||
|
|
||||||
for (var i = 0, count = [theValues count]; i < count && value != CPMultipleValuesMarker; i++)
|
for (var i=0, count=[allValues count]; i<count && value!=CPMultipleValuesMarker; i++)
|
||||||
{
|
{
|
||||||
if (![value isEqual:[theValues objectAtIndex:i]])
|
if (![value isEqual:[allValues objectAtIndex:i]])
|
||||||
value = CPMultipleValuesMarker;
|
value = CPMultipleValuesMarker;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === nil || value.isa && [value isEqual:[CPNull null]])
|
[_cachedValues setValue:value forKey:aKey];
|
||||||
value = CPNullMarker;
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)valueForKeyPath:(CPString)theKeyPath
|
|
||||||
{
|
|
||||||
var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath];
|
|
||||||
|
|
||||||
// Simple collection operators like @count return a scalar value, not an array or set
|
|
||||||
if ([values isKindOfClass:CPArray] || [values isKindOfClass:CPSet])
|
|
||||||
{
|
|
||||||
var value = [self _controllerMarkerForValues:values];
|
|
||||||
[_cachedValues setObject:value forKey:theKeyPath];
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForKey:(CPString)theKeyPath
|
|
||||||
{
|
|
||||||
return [self valueForKeyPath:theKeyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)theValue forKeyPath:(CPString)theKeyPath
|
|
||||||
{
|
|
||||||
[[_controller selectedObjects] setValue:theValue forKeyPath:theKeyPath];
|
|
||||||
[_cachedValues removeObjectForKey:theKeyPath];
|
|
||||||
|
|
||||||
// Allow handlesContentAsCompoundValue to work, based on observation of Cocoa's
|
|
||||||
// NSArrayController - when handlesContentAsCompoundValue and setValue:forKey:@"selection.X"
|
|
||||||
// is called, the array controller causes the compound value to be rewritten if
|
|
||||||
// handlesContentAsCompoundValue == YES. Note that
|
|
||||||
// A) this doesn't use observation (observe: X is not visible in backtraces)
|
|
||||||
// B) it only happens through the selection proxy and not on arrangedObject.X, content.X
|
|
||||||
// or even selectedObjects.X.
|
|
||||||
// FIXME The main code for this should somehow be in CPArrayController and also work
|
|
||||||
// for table based row edits.
|
|
||||||
[[CPBinder getBinding:@"contentArray" forObject:_controller] _contentArrayDidChange];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)theValue forKey:(CPString)theKeyPath
|
|
||||||
{
|
|
||||||
[self setValue:theKeyPath forKeyPath:theKeyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)count
|
- (unsigned)count
|
||||||
{
|
{
|
||||||
return [_cachedValues count];
|
return [_cachedValues count];
|
||||||
@@ -762,27 +567,32 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
return [_cachedValues keyEnumerator];
|
return [_cachedValues keyEnumerator];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)controllerWillChange
|
- (void)setValue:(id)aValue forKey:(CPString)aKey
|
||||||
|
{
|
||||||
|
[[_controller selectedObjects] setValue:aValue forKey:aKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)controllerWillChange
|
||||||
{
|
{
|
||||||
_keys = [_cachedValues allKeys];
|
_keys = [_cachedValues allKeys];
|
||||||
|
|
||||||
if (!_keys)
|
if (!_keys)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (var i = 0, count = _keys.length; i < count; i++)
|
for (var i=0, count=_keys.length; i<count; i++)
|
||||||
[self willChangeValueForKey:_keys[i]];
|
[self willChangeValueForKey:_keys[i]];
|
||||||
|
|
||||||
[_cachedValues removeAllObjects];
|
[_cachedValues removeAllObjects];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)controllerDidChange
|
-(void)controllerDidChange
|
||||||
{
|
{
|
||||||
[_cachedValues removeAllObjects];
|
[_cachedValues removeAllObjects];
|
||||||
|
|
||||||
if (!_keys)
|
if (!_keys)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (var i = 0, count = _keys.length; i < count; i++)
|
for (var i=0, count=_keys.length; i<count; i++)
|
||||||
[self didChangeValueForKey:_keys[i]];
|
[self didChangeValueForKey:_keys[i]];
|
||||||
|
|
||||||
_keys = nil;
|
_keys = nil;
|
||||||
@@ -800,10 +610,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
[proxy setNotifyObject:YES];
|
[proxy setNotifyObject:YES];
|
||||||
[_observationProxies addObject:proxy];
|
[_observationProxies addObject:proxy];
|
||||||
|
|
||||||
// We keep a reference to the observed objects because removeObserver: will be called after the selection changes.
|
[[_controller selectedObjects] addObserver:proxy forKeyPath:aKeyPath options:options context:context];
|
||||||
var observedObjects = [_controller selectedObjects];
|
|
||||||
_observedObjectsByKeyPath[aKeyPath] = observedObjects;
|
|
||||||
[observedObjects addObserver:proxy forKeyPath:aKeyPath options:options context:context];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeObserver:(id)anObject forKeyPath:(CPString)aKeyPath
|
- (void)removeObserver:(id)anObject forKeyPath:(CPString)aKeyPath
|
||||||
@@ -811,12 +618,8 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObject object:self],
|
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObject object:self],
|
||||||
index = [_observationProxies indexOfObject:proxy];
|
index = [_observationProxies indexOfObject:proxy];
|
||||||
|
|
||||||
var observedObjects = _observedObjectsByKeyPath[aKeyPath];
|
[[_controller selectedObjects] removeObserver:[_observationProxies objectAtIndex:index] forKeyPath:aKeyPath];
|
||||||
[observedObjects removeObserver:[_observationProxies objectAtIndex:index] forKeyPath:aKeyPath];
|
|
||||||
|
|
||||||
[_observationProxies removeObjectAtIndex:index];
|
[_observationProxies removeObjectAtIndex:index];
|
||||||
|
|
||||||
_observedObjectsByKeyPath[aKeyPath] = nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+2
-23
@@ -1,24 +1,3 @@
|
|||||||
/*
|
|
||||||
* CPOpenPanel.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 "CPPanel.j"
|
@import "CPPanel.j"
|
||||||
|
|
||||||
@@ -47,9 +26,9 @@
|
|||||||
var options = { directoryURL: [self directoryURL],
|
var options = { directoryURL: [self directoryURL],
|
||||||
canChooseFiles: [self canChooseFiles],
|
canChooseFiles: [self canChooseFiles],
|
||||||
canChooseDirectories: [self canChooseDirectories],
|
canChooseDirectories: [self canChooseDirectories],
|
||||||
allowsMultipleSelection: [self allowsMultipleSelection] },
|
allowsMultipleSelection: [self allowsMultipleSelection] };
|
||||||
|
|
||||||
result = window.cpOpenPanel(options);
|
var result = window.cpOpenPanel(options);
|
||||||
|
|
||||||
_URLs = result.URLs;
|
_URLs = result.URLs;
|
||||||
|
|
||||||
|
|||||||
+121
-853
File diff suppressed because it is too large
Load Diff
+1
-6
@@ -51,7 +51,7 @@ CPCancelButton = 0;
|
|||||||
@global
|
@global
|
||||||
@class CPWindow
|
@class CPWindow
|
||||||
*/
|
*/
|
||||||
CPDocModalWindowMask = 1 << 6;
|
CPDocModalWindowMask = 1 << 6;
|
||||||
|
|
||||||
@implementation CPPanel : CPWindow
|
@implementation CPPanel : CPWindow
|
||||||
{
|
{
|
||||||
@@ -114,9 +114,4 @@ CPDocModalWindowMask = 1 << 6;
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)canBecomeKeyWindow
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+10
-11
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPArray.j>
|
@import <Foundation/CPArray.j>
|
||||||
@import <Foundation/CPData.j>
|
|
||||||
@import <Foundation/CPDictionary.j>
|
@import <Foundation/CPDictionary.j>
|
||||||
@import <Foundation/CPPropertyListSerialization.j>
|
@import <Foundation/CPPropertyListSerialization.j>
|
||||||
|
|
||||||
@@ -74,12 +73,12 @@ var CPPasteboards = nil,
|
|||||||
*/
|
*/
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self !== [CPPasteboard class])
|
if (self != [CPPasteboard class])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self setVersion:1.0];
|
[self setVersion:1.0];
|
||||||
|
|
||||||
CPPasteboards = @{};
|
CPPasteboards = [CPDictionary dictionary];
|
||||||
|
|
||||||
if (typeof window.cpPasteboardWithName !== "undefined")
|
if (typeof window.cpPasteboardWithName !== "undefined")
|
||||||
supportsNativePasteboard = YES;
|
supportsNativePasteboard = YES;
|
||||||
@@ -118,11 +117,11 @@ var CPPasteboards = nil,
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
// _name = aName;
|
_name = aName;
|
||||||
_types = [];
|
_types = [];
|
||||||
|
|
||||||
_owners = @{};
|
_owners = [CPDictionary dictionary];
|
||||||
_provided = @{};
|
_provided = [CPDictionary dictionary];
|
||||||
|
|
||||||
_changeCount = 0;
|
_changeCount = 0;
|
||||||
|
|
||||||
@@ -151,7 +150,7 @@ var CPPasteboards = nil,
|
|||||||
{
|
{
|
||||||
var type = types[i];
|
var type = types[i];
|
||||||
|
|
||||||
if (![_owners objectForKey:type])
|
if(![_owners objectForKey:type])
|
||||||
{
|
{
|
||||||
[_types addObject:type];
|
[_types addObject:type];
|
||||||
[_provided removeObjectForKey:type];
|
[_provided removeObjectForKey:type];
|
||||||
@@ -188,8 +187,8 @@ var CPPasteboards = nil,
|
|||||||
{
|
{
|
||||||
[_types setArray:types];
|
[_types setArray:types];
|
||||||
|
|
||||||
_owners = @{};
|
_owners = [CPDictionary dictionary];
|
||||||
_provided = @{};
|
_provided = [CPDictionary dictionary];
|
||||||
|
|
||||||
var count = _types.length;
|
var count = _types.length;
|
||||||
|
|
||||||
@@ -248,14 +247,14 @@ var CPPasteboards = nil,
|
|||||||
|
|
||||||
// Determining Types
|
// Determining Types
|
||||||
/*!
|
/*!
|
||||||
Checks the pasteboard's types for a match with the types listed 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 \c nil if no match was found
|
||||||
*/
|
*/
|
||||||
- (CPString)availableTypeFromArray:(CPArray)anArray
|
- (CPString)availableTypeFromArray:(CPArray)anArray
|
||||||
{
|
{
|
||||||
return [anArray firstObjectCommonWithArray:[self types]];
|
return [[self types] firstObjectCommonWithArray:anArray];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
+202
-527
File diff suppressed because it is too large
Load Diff
@@ -1,382 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPPopover.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Antoine Mercadal.
|
|
||||||
* Copyright 2011 Antoine Mercadal.
|
|
||||||
*
|
|
||||||
* 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 "CPButton.j"
|
|
||||||
@import "CPColor.j"
|
|
||||||
@import "CPImage.j"
|
|
||||||
@import "CPImageView.j"
|
|
||||||
@import "CPResponder.j"
|
|
||||||
@import "CPView.j"
|
|
||||||
@import "_CPPopoverWindow.j"
|
|
||||||
|
|
||||||
|
|
||||||
CPPopoverBehaviorApplicationDefined = 0;
|
|
||||||
CPPopoverBehaviorTransient = 1;
|
|
||||||
CPPopoverBehaviorSemitransient = 2;
|
|
||||||
|
|
||||||
var CPPopoverDelegate_popover_willShow_ = 1 << 0,
|
|
||||||
CPPopoverDelegate_popover_didShow_ = 1 << 1,
|
|
||||||
CPPopoverDelegate_popover_shouldClose_ = 1 << 2,
|
|
||||||
CPPopoverDelegate_popover_willClose_ = 1 << 3,
|
|
||||||
CPPopoverDelegate_popover_didClose_ = 1 << 4;
|
|
||||||
|
|
||||||
|
|
||||||
/*! @ingroup appkit
|
|
||||||
@class CPPopover
|
|
||||||
|
|
||||||
This class represent a widget that displays a popover
|
|
||||||
view relative to another one.
|
|
||||||
|
|
||||||
Delegate can implement:
|
|
||||||
|
|
||||||
@code
|
|
||||||
– popoverShouldClose:(CPPopover)aPopOver
|
|
||||||
– popoverWillShow:(CPPopover)aPopOver
|
|
||||||
– popoverDidShow:(CPPopover)aPopOver
|
|
||||||
– popoverWillClose:(CPPopover)aPopOver
|
|
||||||
– popoverDidClose:(CPPopover)aPopOver
|
|
||||||
@endcode
|
|
||||||
*/
|
|
||||||
@implementation CPPopover : CPResponder
|
|
||||||
{
|
|
||||||
@outlet CPViewController _contentViewController @accessors(property=contentViewController);
|
|
||||||
@outlet id _delegate @accessors(getter=delegate);
|
|
||||||
|
|
||||||
BOOL _animates @accessors(getter=animates);
|
|
||||||
int _appearance @accessors(property=appearance);
|
|
||||||
int _behavior @accessors(getter=behavior);
|
|
||||||
|
|
||||||
_CPPopoverWindow _popoverWindow;
|
|
||||||
int _implementedDelegateMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Initialization
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initialize the CPPopover witn default values
|
|
||||||
|
|
||||||
@returns an initialized CPPopover
|
|
||||||
*/
|
|
||||||
- (CPPopover)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_animates = YES;
|
|
||||||
_appearance = CPPopoverAppearanceMinimal;
|
|
||||||
_behavior = CPPopoverBehaviorApplicationDefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Getters / Setters
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the current rect of the popover
|
|
||||||
|
|
||||||
@return CGRect representing the frame of the popover
|
|
||||||
*/
|
|
||||||
- (CGRect)positioningRect
|
|
||||||
{
|
|
||||||
if (![_popoverWindow isVisible])
|
|
||||||
return CGRectMakeZero();
|
|
||||||
|
|
||||||
return [_popoverWindow frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Sets the frame of the popover
|
|
||||||
@param aRect the desired frame
|
|
||||||
*/
|
|
||||||
- (void)setPositioningRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
if (![_popoverWindow isVisible])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[_popoverWindow setFrame:aRect];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the size of the popover's view
|
|
||||||
|
|
||||||
@return CGSize representing the size of the popover's view
|
|
||||||
*/
|
|
||||||
- (CGSize)contentSize
|
|
||||||
{
|
|
||||||
if (![_popoverWindow isVisible])
|
|
||||||
return CGRectMakeZero();
|
|
||||||
|
|
||||||
return [[_contentViewController view] frameSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the size of of the popover's view
|
|
||||||
|
|
||||||
@param aSize the desired size
|
|
||||||
*/
|
|
||||||
- (void)setContentSize:(CGSize)aSize
|
|
||||||
{
|
|
||||||
[[_contentViewController view] setFrameSize:aSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Indicates if CPPopover is visible
|
|
||||||
|
|
||||||
@returns \c YES if visible
|
|
||||||
*/
|
|
||||||
- (BOOL)isShown
|
|
||||||
{
|
|
||||||
return [_popoverWindow isVisible];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set if the popover should animate for open/close actions.
|
|
||||||
|
|
||||||
@param shouldAnimate if YES, the popover will be animated.
|
|
||||||
*/
|
|
||||||
- (void)setAnimates:(BOOL)shouldAnimate
|
|
||||||
{
|
|
||||||
if (_animates == shouldAnimate)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_animates = shouldAnimate;
|
|
||||||
[_popoverWindow setAnimates:_animates];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the behavior of the CPPopover. It can be:
|
|
||||||
|
|
||||||
- \c CPPopoverBehaviorTransient: the popover will close if another control outside the popover becomes the responder
|
|
||||||
- \c CPPopoverBehaviorApplicationDefined: (DEFAULT) the application is responsible for closing the popover
|
|
||||||
|
|
||||||
@param aBehavior the desired behavior
|
|
||||||
*/
|
|
||||||
- (void)setBehavior:(int)aBehavior
|
|
||||||
{
|
|
||||||
if (_behavior == aBehavior)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_behavior = aBehavior;
|
|
||||||
[_popoverWindow setStyleMask:[self styleMaskForBehavior]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDelegate:(id)aDelegate
|
|
||||||
{
|
|
||||||
if (_delegate === aDelegate)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_delegate = aDelegate;
|
|
||||||
_implementedDelegateMethods = 0;
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(popoverWillShow:)])
|
|
||||||
_implementedDelegateMethods |= CPPopoverDelegate_popover_willShow_;
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(popoverDidShow:)])
|
|
||||||
_implementedDelegateMethods |= CPPopoverDelegate_popover_didShow_;
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(popoverShouldClose:)])
|
|
||||||
_implementedDelegateMethods |= CPPopoverDelegate_popover_shouldClose_;
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(popoverWillClose:)])
|
|
||||||
_implementedDelegateMethods |= CPPopoverDelegate_popover_willClose_;
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(popoverDidClose:)])
|
|
||||||
_implementedDelegateMethods |= CPPopoverDelegate_popover_didClose_;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Positioning
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Show the popover
|
|
||||||
|
|
||||||
@param positioningRect if set, the popover will be positionned to a random rect relative to the window
|
|
||||||
@param positioningView if set, the popover will be positioned relative to this view
|
|
||||||
@param preferredEdge: \c CPRectEdge representing the preferred positioning.
|
|
||||||
*/
|
|
||||||
- (void)showRelativeToRect:(CGRect)positioningRect ofView:(CPView)positioningView preferredEdge:(CPRectEdge)preferredEdge
|
|
||||||
{
|
|
||||||
if (!positioningView)
|
|
||||||
[CPException raise:CPInvalidArgumentException reason:"positionView must not be nil"];
|
|
||||||
|
|
||||||
if (!_contentViewController)
|
|
||||||
[CPException raise:CPInternalInconsistencyException reason:@"contentViewController must not be nil"];
|
|
||||||
|
|
||||||
// If the popover is currently closing, do nothing. That is what Cocoa does.
|
|
||||||
if ([_popoverWindow isClosing])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_)
|
|
||||||
[_delegate popoverWillShow:self];
|
|
||||||
|
|
||||||
if (!_popoverWindow)
|
|
||||||
{
|
|
||||||
_popoverWindow = [[_CPPopoverWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:[self styleMaskForBehavior]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_popoverWindow setPlatformWindow:[[positioningView window] platformWindow]];
|
|
||||||
[_popoverWindow setAppearance:_appearance];
|
|
||||||
[_popoverWindow setAnimates:_animates];
|
|
||||||
[_popoverWindow setDelegate:self];
|
|
||||||
[_popoverWindow setMovableByWindowBackground:NO];
|
|
||||||
[_popoverWindow setFrame:[_popoverWindow frameRectForContentRect:[[_contentViewController view] frame]]];
|
|
||||||
[_popoverWindow setContentView:[_contentViewController view]];
|
|
||||||
[_popoverWindow positionRelativeToRect:positioningRect ofView:positioningView preferredEdge:preferredEdge];
|
|
||||||
|
|
||||||
if (!_animates && _implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
|
|
||||||
[_delegate popoverDidShow:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned)styleMaskForBehavior
|
|
||||||
{
|
|
||||||
return (_behavior == CPPopoverBehaviorTransient) ? CPClosableOnBlurWindowMask : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Closes the popover
|
|
||||||
*/
|
|
||||||
- (void)close
|
|
||||||
{
|
|
||||||
[self _close];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Closes the popover
|
|
||||||
*/
|
|
||||||
- (void)_close
|
|
||||||
{
|
|
||||||
if ([_popoverWindow isClosing] || ![self isShown])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_)
|
|
||||||
[_delegate popoverWillClose:self];
|
|
||||||
|
|
||||||
[_popoverWindow close];
|
|
||||||
|
|
||||||
// popoverDidClose will be sent from popoverWindowDidClose, since
|
|
||||||
// the popover window will close asynchronously when animating.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Action
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Close the popover
|
|
||||||
|
|
||||||
@param sender the sender of the action
|
|
||||||
*/
|
|
||||||
- (IBAction)performClose:(id)sender
|
|
||||||
{
|
|
||||||
if ([_popoverWindow isClosing])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_shouldClose_)
|
|
||||||
if (![_delegate popoverShouldClose:self])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self _close];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Delegates
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
- (BOOL)popoverWindowShouldClose:(_CPPopoverWindow)aPopoverWindow
|
|
||||||
{
|
|
||||||
[self performClose:self];
|
|
||||||
|
|
||||||
// We return NO, because we want the CPPopover to determine
|
|
||||||
// if the popover window can be closed and to give us a chance
|
|
||||||
// to send delegate messages.
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
- (void)popoverWindowDidClose:(_CPPopoverWindow)aPopoverWindow
|
|
||||||
{
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_didClose_)
|
|
||||||
[_delegate popoverDidClose:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
- (void)popoverWindowDidShow:(_CPPopoverWindow)aPopoverWindow
|
|
||||||
{
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
|
|
||||||
[_delegate popoverDidShow:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPPopover (Deprecated)
|
|
||||||
|
|
||||||
- (void)setBehaviour:(int)aBehavior
|
|
||||||
{
|
|
||||||
_CPReportLenientDeprecation(self, _cmd, @selector(setBehavior:));
|
|
||||||
|
|
||||||
[self setBehavior:aBehavior];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPPopoverNeedsNewPopoverWindowKey = @"CPPopoverNeedsNewPopoverWindowKey",
|
|
||||||
CPPopoverAppearanceKey = @"CPPopoverAppearanceKey",
|
|
||||||
CPPopoverAnimatesKey = @"CPPopoverAnimatesKey",
|
|
||||||
CPPopoverContentViewControllerKey = @"CPPopoverContentViewControllerKey",
|
|
||||||
CPPopoverDelegateKey = @"CPPopoverDelegateKey",
|
|
||||||
CPPopoverBehaviorKey = @"CPPopoverBehaviorKey";
|
|
||||||
|
|
||||||
@implementation CPPopover (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_appearance = [aCoder decodeIntForKey:CPPopoverAppearanceKey];
|
|
||||||
_animates = [aCoder decodeBoolForKey:CPPopoverAnimatesKey];
|
|
||||||
_contentViewController = [aCoder decodeObjectForKey:CPPopoverContentViewControllerKey];
|
|
||||||
[self setDelegate:[aCoder decodeObjectForKey:CPPopoverDelegateKey]];
|
|
||||||
[self setBehavior:[aCoder decodeIntForKey:CPPopoverBehaviorKey]];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeInt:_appearance forKey:CPPopoverAppearanceKey];
|
|
||||||
[aCoder encodeBool:_animates forKey:CPPopoverAnimatesKey];
|
|
||||||
[aCoder encodeObject:_contentViewController forKey:CPPopoverContentViewControllerKey];
|
|
||||||
[aCoder encodeObject:_delegate forKey:CPPopoverDelegateKey];
|
|
||||||
[aCoder encodeInt:_behavior forKey:CPPopoverBehaviorKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+108
-175
@@ -23,26 +23,29 @@
|
|||||||
@import "CGGeometry.j"
|
@import "CGGeometry.j"
|
||||||
@import "CPImageView.j"
|
@import "CPImageView.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
@import "CPWindow_Constants.j"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@global
|
@global
|
||||||
@group CPProgressIndicatorStyle
|
@group CPProgressIndicatorStyle
|
||||||
*/
|
*/
|
||||||
CPProgressIndicatorBarStyle = 0;
|
CPProgressIndicatorBarStyle = 0;
|
||||||
/*
|
/*
|
||||||
@global
|
@global
|
||||||
@group CPProgressIndicatorStyle
|
@group CPProgressIndicatorStyle
|
||||||
*/
|
*/
|
||||||
CPProgressIndicatorSpinningStyle = 1;
|
CPProgressIndicatorSpinningStyle = 1;
|
||||||
/*
|
/*
|
||||||
@global
|
@global
|
||||||
@group CPProgressIndicatorStyle
|
@group CPProgressIndicatorStyle
|
||||||
*/
|
*/
|
||||||
CPProgressIndicatorHUDBarStyle = 2;
|
CPProgressIndicatorHUDBarStyle = 2;
|
||||||
|
|
||||||
var CPProgressIndicatorSpinningStyleColors = [];
|
var CPProgressIndicatorSpinningStyleColors = nil,
|
||||||
|
|
||||||
|
CPProgressIndicatorClassName = nil,
|
||||||
|
CPProgressIndicatorStyleIdentifiers = nil,
|
||||||
|
CPProgressIndicatorStyleSizes = nil;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -61,39 +64,77 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
|||||||
|
|
||||||
CPControlSize _controlSize;
|
CPControlSize _controlSize;
|
||||||
|
|
||||||
BOOL _indeterminate;
|
BOOL _isIndeterminate;
|
||||||
CPProgressIndicatorStyle _style;
|
CPProgressIndicatorStyle _style;
|
||||||
|
|
||||||
BOOL _isAnimating;
|
BOOL _isAnimating;
|
||||||
|
|
||||||
BOOL _isDisplayedWhenStoppedSet;
|
BOOL _isDisplayedWhenStoppedSet;
|
||||||
BOOL _isDisplayedWhenStopped;
|
BOOL _isDisplayedWhenStopped;
|
||||||
|
|
||||||
|
CPView _barView;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
/*
|
||||||
|
@ignore
|
||||||
|
*/
|
||||||
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
return @"progress-indicator";
|
if (self != [CPProgressIndicator class])
|
||||||
}
|
return;
|
||||||
|
|
||||||
+ (CPDictionary)themeAttributes
|
var bundle = [CPBundle bundleForClass:self];
|
||||||
{
|
|
||||||
return @{
|
|
||||||
@"indeterminate-bar-color": [CPNull null],
|
|
||||||
@"bar-color": [CPNull null],
|
|
||||||
@"default-height": 20,
|
|
||||||
@"bezel-color": [CPNull null],
|
|
||||||
@"spinning-mini-gif": [CPNull null],
|
|
||||||
@"spinning-small-gif": [CPNull null],
|
|
||||||
@"spinning-regular-gif": [CPNull null],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
CPProgressIndicatorSpinningStyleColors = [];
|
||||||
{
|
|
||||||
if (aBinding === CPValueBinding || aBinding === @"isIndeterminate")
|
|
||||||
return [_CPProgressIndicatorBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:
|
||||||
|
[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]];
|
||||||
|
CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:
|
||||||
|
[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]];
|
||||||
|
CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:
|
||||||
|
[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]];
|
||||||
|
|
||||||
|
CPProgressIndicatorBezelBorderViewPool = [];
|
||||||
|
|
||||||
|
var start = CPProgressIndicatorBarStyle,
|
||||||
|
end = CPProgressIndicatorHUDBarStyle;
|
||||||
|
|
||||||
|
for (; start <= end; ++start)
|
||||||
|
{
|
||||||
|
CPProgressIndicatorBezelBorderViewPool[start] = [];
|
||||||
|
CPProgressIndicatorBezelBorderViewPool[start][CPMiniControlSize] = [];
|
||||||
|
CPProgressIndicatorBezelBorderViewPool[start][CPSmallControlSize] = [];
|
||||||
|
CPProgressIndicatorBezelBorderViewPool[start][CPRegularControlSize] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
CPProgressIndicatorClassName = [self className];
|
||||||
|
CPProgressIndicatorStyleIdentifiers = [];
|
||||||
|
|
||||||
|
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] = @"Bar";
|
||||||
|
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorSpinningStyle] = @"Spinny";
|
||||||
|
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle] = @"HUDBar";
|
||||||
|
|
||||||
|
var regularIdentifier = _CPControlIdentifierForControlSize(CPRegularControlSize),
|
||||||
|
smallIdentifier = _CPControlIdentifierForControlSize(CPSmallControlSize),
|
||||||
|
miniIdentifier = _CPControlIdentifierForControlSize(CPMiniControlSize);
|
||||||
|
|
||||||
|
CPProgressIndicatorStyleSizes = [];
|
||||||
|
|
||||||
|
// Bar Sttyle
|
||||||
|
var prefixes = [
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
var prefix = prefixes[i];
|
||||||
|
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)];
|
||||||
|
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
@@ -112,7 +153,8 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
|||||||
|
|
||||||
_controlSize = CPRegularControlSize;
|
_controlSize = CPRegularControlSize;
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self updateBackgroundColor];
|
||||||
|
[self drawBar];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -248,7 +290,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Not yet implemented.
|
Not yet impemented.
|
||||||
*/
|
*/
|
||||||
- (CPControlTint)controlTint
|
- (CPControlTint)controlTint
|
||||||
{
|
{
|
||||||
@@ -274,12 +316,12 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
|||||||
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 \c YES makes the indicator indeterminate
|
||||||
*/
|
*/
|
||||||
- (void)setIndeterminate:(BOOL)indeterminate
|
- (void)setIndeterminate:(BOOL)isIndeterminate
|
||||||
{
|
{
|
||||||
if (_indeterminate == indeterminate)
|
if (_isIndeterminate == isIndeterminate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_indeterminate = indeterminate;
|
_isIndeterminate = isIndeterminate;
|
||||||
|
|
||||||
[self updateBackgroundColor];
|
[self updateBackgroundColor];
|
||||||
}
|
}
|
||||||
@@ -289,7 +331,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
|||||||
*/
|
*/
|
||||||
- (BOOL)isIndeterminate
|
- (BOOL)isIndeterminate
|
||||||
{
|
{
|
||||||
return _indeterminate;
|
return _isIndeterminate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -303,8 +345,6 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
|||||||
|
|
||||||
_style = aStyle;
|
_style = aStyle;
|
||||||
|
|
||||||
[self setTheme:(_style === CPProgressIndicatorHUDBarStyle) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
|
|
||||||
|
|
||||||
[self updateBackgroundColor];
|
[self updateBackgroundColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +356,9 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
|||||||
if (_style == CPProgressIndicatorSpinningStyle)
|
if (_style == CPProgressIndicatorSpinningStyle)
|
||||||
[self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]];
|
[self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]];
|
||||||
else
|
else
|
||||||
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), [self valueForThemeAttribute:@"default-height"])];
|
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), CPProgressIndicatorStyleSizes[
|
||||||
|
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] +
|
||||||
|
_CPControlIdentifierForControlSize(_controlSize)][0].height)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -366,71 +408,56 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
|||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (void)drawBar
|
- (void)drawBar
|
||||||
{
|
{
|
||||||
[self setNeedsLayout];
|
if (_style == CPProgressIndicatorSpinningStyle)
|
||||||
}
|
return;
|
||||||
|
|
||||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
if (!_barView)
|
||||||
{
|
|
||||||
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
|
|
||||||
{
|
|
||||||
if (aViewName === @"bar-view" && _style !== CPProgressIndicatorSpinningStyle)
|
|
||||||
{
|
{
|
||||||
var width = CGRectGetWidth([self bounds]),
|
_barView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 16.0)];
|
||||||
barWidth = width * ((_doubleValue - _minValue) / (_maxValue - _minValue));
|
[self addSubview:_barView];
|
||||||
|
|
||||||
if (barWidth > 0.0 && barWidth < 4.0)
|
|
||||||
barWidth = 4.0;
|
|
||||||
|
|
||||||
if (_indeterminate)
|
|
||||||
barWidth = width;
|
|
||||||
|
|
||||||
return CGRectMake(0, 0, barWidth, [self valueForThemeAttribute:@"default-height"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
[_barView setBackgroundColor:_CPControlThreePartImagePattern(
|
||||||
|
NO,
|
||||||
|
CPProgressIndicatorStyleSizes,
|
||||||
|
CPProgressIndicatorClassName,
|
||||||
|
@"Bar",
|
||||||
|
CPProgressIndicatorStyleIdentifiers[_style],
|
||||||
|
_CPControlIdentifierForControlSize(_controlSize))];
|
||||||
|
|
||||||
|
var width = CGRectGetWidth([self bounds]),
|
||||||
|
barWidth = width * ((_doubleValue - _minValue) / (_maxValue - _minValue));
|
||||||
|
|
||||||
|
if (barWidth > 0.0 && barWidth < 4.0)
|
||||||
|
barWidth = 4.0;
|
||||||
|
|
||||||
|
[_barView setFrameSize:CGSizeMake(barWidth, 16.0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (void)updateBackgroundColor
|
- (void)updateBackgroundColor
|
||||||
{
|
|
||||||
if ([CPProgressIndicatorSpinningStyleColors count] === 0)
|
|
||||||
{
|
|
||||||
CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [self valueForThemeAttribute:@"spinning-mini-gif"];
|
|
||||||
CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [self valueForThemeAttribute:@"spinning-small-gif"];
|
|
||||||
CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [self valueForThemeAttribute:@"spinning-regular-gif"];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
{
|
||||||
if (YES)//_isBezeled)
|
if (YES)//_isBezeled)
|
||||||
{
|
{
|
||||||
if (_style == CPProgressIndicatorSpinningStyle)
|
if (_style == CPProgressIndicatorSpinningStyle)
|
||||||
{
|
{
|
||||||
// This will cause the bar view to go away due to having a nil rect when _style == CPProgressIndicatorSpinningStyle.
|
[_barView removeFromSuperview];
|
||||||
[self layoutEphemeralSubviewNamed:"bar-view"
|
|
||||||
positioned:CPWindowBelow
|
_barView = nil;
|
||||||
relativeToEphemeralSubviewNamed:nil];
|
|
||||||
|
|
||||||
[self setBackgroundColor:CPProgressIndicatorSpinningStyleColors[_controlSize]];
|
[self setBackgroundColor:CPProgressIndicatorSpinningStyleColors[_controlSize]];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
[self setBackgroundColor:_CPControlThreePartImagePattern(
|
||||||
|
NO,
|
||||||
|
CPProgressIndicatorStyleSizes,
|
||||||
|
CPProgressIndicatorClassName,
|
||||||
|
@"BezelBorder",
|
||||||
|
CPProgressIndicatorStyleIdentifiers[_style],
|
||||||
|
_CPControlIdentifierForControlSize(_controlSize))];
|
||||||
|
|
||||||
var barView = [self layoutEphemeralSubviewNamed:"bar-view"
|
[self drawBar];
|
||||||
positioned:CPWindowBelow
|
|
||||||
relativeToEphemeralSubviewNamed:nil];
|
|
||||||
|
|
||||||
if (_indeterminate)
|
|
||||||
[barView setBackgroundColor:[self currentValueForThemeAttribute:@"indeterminate-bar-color"]];
|
|
||||||
else
|
|
||||||
[barView setBackgroundColor:[self currentValueForThemeAttribute:@"bar-color"]];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -438,97 +465,3 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@implementation CPProgressIndicator (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if (self = [super initWithCoder:aCoder])
|
|
||||||
{
|
|
||||||
_minValue = [aCoder decodeObjectForKey:@"_minValue"];
|
|
||||||
_maxValue = [aCoder decodeObjectForKey:@"_maxValue"];
|
|
||||||
_doubleValue = [aCoder decodeObjectForKey:@"_doubleValue"];
|
|
||||||
_controlSize = [aCoder decodeObjectForKey:@"_controlSize"];
|
|
||||||
_indeterminate = [aCoder decodeObjectForKey:@"_indeterminate"];
|
|
||||||
_style = [aCoder decodeIntForKey:@"_style"];
|
|
||||||
_isAnimating = [aCoder decodeObjectForKey:@"_isAnimating"];
|
|
||||||
_isDisplayedWhenStoppedSet = [aCoder decodeObjectForKey:@"_isDisplayedWhenStoppedSet"];
|
|
||||||
_isDisplayedWhenStopped = [aCoder decodeObjectForKey:@"_isDisplayedWhenStopped"];
|
|
||||||
|
|
||||||
[self updateBackgroundColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
// Don't encode the background colour. It can be recreated based on the flags
|
|
||||||
// and if encoded causes hardcoded image paths in the cib while just wasting space.
|
|
||||||
var backgroundColor = [self backgroundColor];
|
|
||||||
[self setBackgroundColor:nil];
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
[self setBackgroundColor:backgroundColor];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_minValue forKey:@"_minValue"];
|
|
||||||
[aCoder encodeObject:_maxValue forKey:@"_maxValue"];
|
|
||||||
[aCoder encodeObject:_doubleValue forKey:@"_doubleValue"];
|
|
||||||
[aCoder encodeObject:_controlSize forKey:@"_controlSize"];
|
|
||||||
[aCoder encodeObject:_indeterminate forKey:@"_indeterminate"];
|
|
||||||
[aCoder encodeInt:_style forKey:@"_style"];
|
|
||||||
[aCoder encodeObject:_isAnimating forKey:@"_isAnimating"];
|
|
||||||
[aCoder encodeObject:_isDisplayedWhenStoppedSet forKey:@"_isDisplayedWhenStoppedSet"];
|
|
||||||
[aCoder encodeObject:_isDisplayedWhenStopped forKey:@"_isDisplayedWhenStopped"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPProgressIndicatorBinder : CPBinder
|
|
||||||
|
|
||||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var value = aBinding === CPValueBinding ? 0.0 : YES;
|
|
||||||
|
|
||||||
[self _setPlaceholder:value forMarker:CPMultipleValuesMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:value forMarker:CPNoSelectionMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:value forMarker:CPNotApplicableMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:value forMarker:CPNullMarker isDefault:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding === CPValueBinding)
|
|
||||||
return [_source doubleValue];
|
|
||||||
else if (aBinding === @"isIndeterminate")
|
|
||||||
[_source isIndeterminate];
|
|
||||||
else
|
|
||||||
return [super valueForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_setValue:(id)aValue forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding === CPValueBinding)
|
|
||||||
[_source setDoubleValue:aValue];
|
|
||||||
else if (aBinding === @"isIndeterminate")
|
|
||||||
[_source setIndeterminate:aValue];
|
|
||||||
else
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (![self _setValue:aValue forBinding:aBinding])
|
|
||||||
[super setValue:aValue forBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (![self _setValue:aValue forBinding:aBinding])
|
|
||||||
[super setPlaceholderValue:aValue withMarker:aMarker forBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|||||||
+15
-237
@@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -152,14 +150,6 @@ CPRadioImageOffset = 4.0;
|
|||||||
[_radioGroup _setSelectedRadio:self];
|
[_radioGroup _setSelectedRadio:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
|
||||||
{
|
|
||||||
[super sendAction:anAction to:anObject];
|
|
||||||
|
|
||||||
if (_radioGroup)
|
|
||||||
[CPApp sendAction:[_radioGroup action] to:[_radioGroup target] from:_radioGroup];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||||
@@ -183,143 +173,33 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
|||||||
[aCoder encodeObject:_radioGroup forKey:CPRadioRadioGroupKey];
|
[aCoder encodeObject:_radioGroup forKey:CPRadioRadioGroupKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPImage)image
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"image"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPImage)alternateImage
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"image"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
var startedTracking = [super startTrackingAt:aPoint];
|
|
||||||
[self highlight:YES];
|
|
||||||
return startedTracking;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPRadioGroup : CPObject
|
@implementation CPRadioGroup : CPObject
|
||||||
{
|
{
|
||||||
CPArray _radios;
|
CPSet _radios;
|
||||||
CPRadio _selectedRadio;
|
CPRadio _selectedRadio;
|
||||||
|
|
||||||
BOOL _enabled @accessors(getter=enabled);
|
|
||||||
BOOL _hidden @accessors(getter=hidden);
|
|
||||||
|
|
||||||
id _target @accessors(property=target);
|
id _target @accessors(property=target);
|
||||||
SEL _action @accessors(property=action);
|
SEL _action @accessors(property=action);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (self !== [CPRadioGroup class])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self exposeBinding:CPSelectedValueBinding];
|
|
||||||
[self exposeBinding:CPSelectedTagBinding];
|
|
||||||
[self exposeBinding:CPSelectedIndexBinding];
|
|
||||||
|
|
||||||
[self exposeBinding:CPEnabledBinding];
|
|
||||||
[self exposeBinding:CPHiddenBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_radios = [];
|
_radios = [CPSet set];
|
||||||
_selectedRadio = nil;
|
_selectedRadio = nil;
|
||||||
_enabled = YES;
|
|
||||||
_hidden = NO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Selects the radio button at the given index within the group.
|
|
||||||
If index is -1, all of the radio buttons are turned off.
|
|
||||||
*/
|
|
||||||
- (void)selectRadioAtIndex:(int)index
|
|
||||||
{
|
|
||||||
if (index === -1)
|
|
||||||
[self _setSelectedRadio:nil];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var radio = [_radios objectAtIndex:index];
|
|
||||||
|
|
||||||
[self _setSelectedRadio:radio];
|
|
||||||
[radio setState:CPOnState];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Selects the first radio button within the group with the given tag.
|
|
||||||
If a radio button with the given tag is found, selects it
|
|
||||||
and returns YES. Otherwise returns NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)selectRadioWithTag:(int)tag
|
|
||||||
{
|
|
||||||
var index = [_radios indexOfObjectPassingTest:function(radio)
|
|
||||||
{
|
|
||||||
return [radio tag] === tag;
|
|
||||||
}];
|
|
||||||
|
|
||||||
if (index !== CPNotFound)
|
|
||||||
{
|
|
||||||
[self selectRadioAtIndex:index];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the CPRadio that is currently selected within the group,
|
|
||||||
or nil if none are selected.
|
|
||||||
*/
|
|
||||||
- (CPRadio)selectedRadio
|
|
||||||
{
|
|
||||||
return _selectedRadio;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the index of the selected radio within the array of radio buttons.
|
|
||||||
Buttons are numbered going left to right and top to bottom. Returns -1
|
|
||||||
if no radio within the group is selected.
|
|
||||||
*/
|
|
||||||
- (int)selectedRadioIndex
|
|
||||||
{
|
|
||||||
return [_radios indexOfObject:_selectedRadio];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)radios
|
|
||||||
{
|
|
||||||
return _radios;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setEnabled:(BOOL)enabled
|
|
||||||
{
|
|
||||||
[_radios makeObjectsPerformSelector:@selector(setEnabled:) withObject:enabled];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setHidden:(BOOL)hidden
|
|
||||||
{
|
|
||||||
[_radios makeObjectsPerformSelector:@selector(setHidden:) withObject:hidden];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Private
|
|
||||||
|
|
||||||
- (void)_addRadio:(CPRadio)aRadio
|
- (void)_addRadio:(CPRadio)aRadio
|
||||||
{
|
{
|
||||||
if ([_radios indexOfObject:aRadio] === CPNotFound)
|
[_radios addObject:aRadio];
|
||||||
[_radios addObject:aRadio];
|
|
||||||
|
|
||||||
if ([aRadio state] === CPOnState)
|
if ([aRadio state] === CPOnState)
|
||||||
[self _setSelectedRadio:aRadio];
|
[self _setSelectedRadio:aRadio];
|
||||||
@@ -333,30 +213,25 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
|||||||
[_radios removeObject:aRadio];
|
[_radios removeObject:aRadio];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Selects the first radio button within the group with the given tag.
|
|
||||||
If a radio button with the given tag is found, selects it
|
|
||||||
and returns YES. Otherwise returns NO.
|
|
||||||
*/
|
|
||||||
- (void)_selectRadioWithTitle:(CPString)aTitle
|
|
||||||
{
|
|
||||||
var index = [_radios indexOfObjectPassingTest:function(radio)
|
|
||||||
{
|
|
||||||
return [radio title] === aTitle;
|
|
||||||
}];
|
|
||||||
|
|
||||||
[self selectRadioAtIndex:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setSelectedRadio:(CPRadio)aRadio
|
- (void)_setSelectedRadio:(CPRadio)aRadio
|
||||||
{
|
{
|
||||||
if (_selectedRadio === aRadio)
|
if (_selectedRadio === aRadio)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[_selectedRadio setState:CPOffState];
|
[_selectedRadio setState:CPOffState];
|
||||||
|
|
||||||
_selectedRadio = aRadio;
|
_selectedRadio = aRadio;
|
||||||
[_CPRadioGroupSelectionBinder reverseSetValueForObject:self];
|
|
||||||
|
[CPApp sendAction:_action to:_target from:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CPRadio)selectedRadio
|
||||||
|
{
|
||||||
|
return _selectedRadio;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CPArray)radios
|
||||||
|
{
|
||||||
|
return [_radios allObjects];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -386,100 +261,3 @@ var CPRadioGroupRadiosKey = @"CPRadioGroupRadiosKey",
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPRadioGroup (BindingSupport)
|
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding === CPSelectedValueBinding ||
|
|
||||||
aBinding === CPSelectedTagBinding ||
|
|
||||||
aBinding === CPSelectedIndexBinding)
|
|
||||||
{
|
|
||||||
var capitalizedBinding = aBinding.charAt(0).toUpperCase() + aBinding.substr(1);
|
|
||||||
|
|
||||||
return [CPClassFromString(@"_CPRadioGroup" + capitalizedBinding + "Binder") class];
|
|
||||||
}
|
|
||||||
else if ([aBinding hasPrefix:CPEnabledBinding])
|
|
||||||
return [CPMultipleValueAndBinding class];
|
|
||||||
else if ([aBinding hasPrefix:CPHiddenBinding])
|
|
||||||
return [CPMultipleValueOrBinding class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var binderForObject = {};
|
|
||||||
|
|
||||||
@implementation _CPRadioGroupSelectionBinder : CPBinder
|
|
||||||
{
|
|
||||||
CPString _selectionBinding @accessors;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
|
|
||||||
{
|
|
||||||
self = [super initWithBinding:aBinding name:aName to:aDestination keyPath:aKeyPath options:options from:aSource];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
binderForObject[[aSource UID]] = self;
|
|
||||||
_selectionBinding = aName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)reverseSetValueForObject:(id)aSource
|
|
||||||
{
|
|
||||||
var binder = binderForObject[[aSource UID]];
|
|
||||||
[binder reverseSetValueFor:[binder _selectionBinding]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[self setValue:aValue forBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPRadioGroupSelectedIndexBinder : _CPRadioGroupSelectionBinder
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[_source selectRadioAtIndex:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
return [_source selectedRadioIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPRadioGroupSelectedTagBinder : _CPRadioGroupSelectionBinder
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[_source selectRadioWithTag:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
return [[_source selectedRadio] tag];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPRadioGroupSelectedValueBinder : _CPRadioGroupSelectionBinder
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[_source _selectRadioWithTitle:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
return [[_source selectedRadio] title];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|||||||
+7
-18
@@ -21,11 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPObjJRuntime.j>
|
|
||||||
|
|
||||||
@import "CPEvent.j"
|
|
||||||
|
|
||||||
@class CPKeyBinding
|
|
||||||
|
|
||||||
CPDeleteKeyCode = 8;
|
CPDeleteKeyCode = 8;
|
||||||
CPTabKeyCode = 9;
|
CPTabKeyCode = 9;
|
||||||
@@ -235,18 +231,11 @@ CPDeleteForwardKeyCode = 46;
|
|||||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*
|
||||||
Overridden by subclasses to handle a key equivalent.
|
FIXME This description is bad.
|
||||||
|
Based on \c anEvent, the receiver should simulate the event.
|
||||||
If the character code or codes in \c anEvent match the receiver’s key equivalent,
|
@param anEvent the event to simulate
|
||||||
the receiver should respond to the event and return \c YES. The default implementation
|
@return \c YES if the event receiver simulated the event
|
||||||
does nothing and returns \c NO.
|
|
||||||
|
|
||||||
You should extract the characters for a key equivalent using
|
|
||||||
\ref CPEvent::charactersIgnoringModifiers "[anEvent charactersIgnoringModifiers]".
|
|
||||||
|
|
||||||
@param anEvent An event object that represents the key equivalent pressed
|
|
||||||
@return \c YES if theEvent is a key equivalent that the receiver handled, \c NO if it is not a key equivalent that it should handle.
|
|
||||||
*/
|
*/
|
||||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
@@ -369,8 +358,8 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey",
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
[self setNextResponder:[aCoder decodeObjectForKey:CPResponderNextResponderKey]];
|
_nextResponder = [aCoder decodeObjectForKey:CPResponderNextResponderKey];
|
||||||
[self setMenu:[aCoder decodeObjectForKey:CPResponderMenuKey]];
|
_menu = [aCoder decodeObjectForKey:CPResponderMenuKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
@@ -1,557 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPPredicateEditor.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by cacaodev.
|
|
||||||
* Copyright 2011, cacaodev.
|
|
||||||
*
|
|
||||||
* 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 "CPRuleEditor.j"
|
|
||||||
@import "_CPPredicateEditorTree.j"
|
|
||||||
@import "_CPPredicateEditorRowNode.j"
|
|
||||||
@import "CPPredicateEditorRowTemplate.j"
|
|
||||||
|
|
||||||
@implementation CPPredicateEditor : CPRuleEditor
|
|
||||||
{
|
|
||||||
CPArray _allTemplates;
|
|
||||||
CPArray _rootTrees;
|
|
||||||
CPArray _rootHeaderTrees;
|
|
||||||
id _predicateTarget @accessors(property=target);
|
|
||||||
SEL _predicateAction @accessors(property=action);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark public methods
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
@class CPPredicateEditor
|
|
||||||
|
|
||||||
@brief CPPredicateEditor is a subclass of CPRuleEditor that is specialized for editing CPPredicate objects.
|
|
||||||
|
|
||||||
CPPredicateEditor provides a CPPredicate property—objectValue (inherited from CPControl)—that you can get and set directly, and that you can bind using bindings (you typically configure a predicate editor in Interface Builder). CPPredicateEditor depends on another class, CPPredicateEditorRowTemplate, that describes the available predicates and how to display them.
|
|
||||||
|
|
||||||
Unlike CPRuleEditor, CPPredicateEditor does not depend on its delegate to populate its rows (and does not call the populating delegate methods). Instead, its rows are populated from its objectValue property (an instance of CPPredicate). CPPredicateEditor relies on instances CPPredicateEditorRowTemplate, which are responsible for mapping back and forth between the displayed view values and various predicates.
|
|
||||||
|
|
||||||
CPPredicateEditor exposes one property, rowTemplates, which is an array of CPPredicateEditorRowTemplate objects.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the row templates for the receiver.
|
|
||||||
@return The row templates for the receiver.
|
|
||||||
@discussion Until otherwise set, this contains a single compound CPPredicateEditorRowTemplate object.
|
|
||||||
@see setRowTemplates:
|
|
||||||
*/
|
|
||||||
- (CPArray)rowTemplates
|
|
||||||
{
|
|
||||||
return _allTemplates;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Sets the row templates for the receiver.
|
|
||||||
@param rowTemplates An array of CPPredicateEditorRowTemplate objects.
|
|
||||||
@see rowTemplates
|
|
||||||
*/
|
|
||||||
- (void)setRowTemplates:(id)rowTemplates
|
|
||||||
{
|
|
||||||
if (_allTemplates == rowTemplates)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_allTemplates = rowTemplates;
|
|
||||||
|
|
||||||
[self _updateItemsByCompoundTemplates];
|
|
||||||
[self _updateItemsBySimpleTemplates];
|
|
||||||
|
|
||||||
if ([self numberOfRows] > 0)
|
|
||||||
{
|
|
||||||
var predicate = [super predicate];
|
|
||||||
[self _reflectPredicate:predicate];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @cond */
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding == CPValueBinding)
|
|
||||||
return [CPPredicateEditorValueBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)_replacementKeyPathForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding == CPValueBinding)
|
|
||||||
return @"predicate";
|
|
||||||
|
|
||||||
return [super _replacementKeyPathForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_initRuleEditorShared
|
|
||||||
{
|
|
||||||
[super _initRuleEditorShared];
|
|
||||||
|
|
||||||
_rootTrees = [CPArray array];
|
|
||||||
_rootHeaderTrees = [CPArray array];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
var initialTemplate = [[CPPredicateEditorRowTemplate alloc] initWithCompoundTypes:[CPAndPredicateType, CPOrPredicateType]];
|
|
||||||
_allTemplates = [CPArray arrayWithObject:initialTemplate];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)objectValue
|
|
||||||
{
|
|
||||||
return [super predicate];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateItemsBySimpleTemplates
|
|
||||||
{
|
|
||||||
var templates = [CPMutableArray array],
|
|
||||||
count = [_allTemplates count],
|
|
||||||
t;
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
var t = _allTemplates[count];
|
|
||||||
if ([t _rowType] == CPRuleEditorRowTypeSimple)
|
|
||||||
[templates insertObject:t atIndex:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
var trees = [self _constructTreesForTemplates:templates];
|
|
||||||
if ([trees count] > 0)
|
|
||||||
_rootTrees = [self _mergeTree:trees];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateItemsByCompoundTemplates
|
|
||||||
{
|
|
||||||
var templates = [CPMutableArray array],
|
|
||||||
count = [_allTemplates count],
|
|
||||||
t;
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
var t = _allTemplates[count];
|
|
||||||
if ([t _rowType] == CPRuleEditorRowTypeCompound)
|
|
||||||
[templates insertObject:t atIndex:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
var trees = [self _constructTreesForTemplates:templates];
|
|
||||||
if ([trees count] > 0)
|
|
||||||
_rootHeaderTrees = [self _mergeTree:trees];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPArray)_constructTreesForTemplates:(id)templates
|
|
||||||
{
|
|
||||||
var trees = [CPMutableArray array],
|
|
||||||
count = [templates count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var tree = [self _constructTreeForTemplate:templates[i]];
|
|
||||||
[trees addObjectsFromArray:tree];
|
|
||||||
}
|
|
||||||
|
|
||||||
return trees;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPMutableArray)_mergeTree:(CPArray)aTree
|
|
||||||
{
|
|
||||||
var mergedTree = [CPMutableArray array];
|
|
||||||
if (aTree == nil)
|
|
||||||
return mergedTree;
|
|
||||||
|
|
||||||
var icount = [aTree count];
|
|
||||||
for (var i = 0; i < icount; i++)
|
|
||||||
{
|
|
||||||
var anode = [aTree objectAtIndex:i],
|
|
||||||
jcount = [mergedTree count],
|
|
||||||
merged = NO;
|
|
||||||
|
|
||||||
for (var j = 0; j < jcount; j++)
|
|
||||||
{
|
|
||||||
var mergednode = [mergedTree objectAtIndex:j];
|
|
||||||
|
|
||||||
if ([[mergednode title] isEqualToString:[anode title]])
|
|
||||||
{
|
|
||||||
var children1 = [mergednode children],
|
|
||||||
children2 = [anode children],
|
|
||||||
children12 = [children1 arrayByAddingObjectsFromArray:children2],
|
|
||||||
mergedChildren = [self _mergeTree:children12];
|
|
||||||
|
|
||||||
[mergednode setChildren:mergedChildren];
|
|
||||||
merged = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!merged)
|
|
||||||
[mergedTree addObject:anode];
|
|
||||||
}
|
|
||||||
|
|
||||||
return mergedTree;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_constructTreeForTemplate:(CPPredicateEditorRowTemplate)aTemplate
|
|
||||||
{
|
|
||||||
var tree = [CPArray array],
|
|
||||||
templateViews = [aTemplate templateViews],
|
|
||||||
count = [templateViews count];
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
var children = [CPArray array],
|
|
||||||
itemsCount = 0,
|
|
||||||
menuIndex = -1,
|
|
||||||
itemArray,
|
|
||||||
|
|
||||||
templateView = [templateViews objectAtIndex:count],
|
|
||||||
isPopup = [templateView isKindOfClass:[CPPopUpButton class]];
|
|
||||||
|
|
||||||
if (isPopup)
|
|
||||||
{
|
|
||||||
itemArray = [[templateView itemArray] valueForKey:@"title"];
|
|
||||||
itemsCount = [itemArray count];
|
|
||||||
menuIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; menuIndex < itemsCount; menuIndex++)
|
|
||||||
{
|
|
||||||
var item = [_CPPredicateEditorTree new];
|
|
||||||
[item setIndexIntoTemplate:count];
|
|
||||||
[item setTemplate:aTemplate];
|
|
||||||
[item setMenuItemIndex:menuIndex];
|
|
||||||
if (isPopup)
|
|
||||||
[item setTitle:[itemArray objectAtIndex:menuIndex]];
|
|
||||||
|
|
||||||
[children addObject:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
[children makeObjectsPerformSelector:@selector(setChildren:) withObject:tree];
|
|
||||||
tree = children;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Set the Predicate
|
|
||||||
|
|
||||||
- (void)setObjectValue:(id)objectValue
|
|
||||||
{
|
|
||||||
var ov = [self objectValue];
|
|
||||||
if ((ov == nil) != (objectValue == nil) || ![ov isEqual:objectValue])
|
|
||||||
{
|
|
||||||
[self _setPredicate:objectValue];
|
|
||||||
[self _reflectPredicate:objectValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_reflectPredicate:(id)predicate
|
|
||||||
{
|
|
||||||
var animation = _currentAnimation;
|
|
||||||
_currentAnimation = nil;
|
|
||||||
_sendAction = NO;
|
|
||||||
|
|
||||||
if (predicate != nil)
|
|
||||||
{
|
|
||||||
if ((_nestingMode == CPRuleEditorNestingModeSimple || _nestingMode == CPRuleEditorNestingModeCompound)
|
|
||||||
&& [predicate isKindOfClass:[CPComparisonPredicate class]])
|
|
||||||
predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:[CPArray arrayWithObject:predicate]];
|
|
||||||
|
|
||||||
var row = [self _rowObjectFromPredicate:predicate];
|
|
||||||
if (row != nil)
|
|
||||||
[_boundArrayOwner setValue:[CPArray arrayWithObject:row] forKey:_boundArrayKeyPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setAnimation:animation];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_rowObjectFromPredicate:(CPPredicate)predicate
|
|
||||||
{
|
|
||||||
var quality, // TODO: We should use this ref somewhere !
|
|
||||||
type,
|
|
||||||
matchedTemplate = [CPPredicateEditorRowTemplate _bestMatchForPredicate:predicate inTemplates:[self rowTemplates] quality:quality];
|
|
||||||
|
|
||||||
if (matchedTemplate == nil)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
var copyTemplate = [matchedTemplate copy],
|
|
||||||
subpredicates = [matchedTemplate displayableSubpredicatesOfPredicate:predicate];
|
|
||||||
|
|
||||||
if (subpredicates == nil)
|
|
||||||
{
|
|
||||||
[copyTemplate _setComparisonPredicate:predicate];
|
|
||||||
type = CPRuleEditorRowTypeSimple;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[copyTemplate _setCompoundPredicate:predicate];
|
|
||||||
type = CPRuleEditorRowTypeCompound;
|
|
||||||
}
|
|
||||||
|
|
||||||
var row = [self _rowFromTemplate:copyTemplate originalTemplate:matchedTemplate withRowType:type];
|
|
||||||
|
|
||||||
if (subpredicates == nil)
|
|
||||||
return row;
|
|
||||||
|
|
||||||
var count = [subpredicates count],
|
|
||||||
subrows = [CPMutableArray array];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var subrow = [self _rowObjectFromPredicate:subpredicates[i]];
|
|
||||||
if (subrow != nil)
|
|
||||||
[subrows addObject:subrow];
|
|
||||||
}
|
|
||||||
|
|
||||||
[row setValue:subrows forKey:[super subrowsKeyPath]];
|
|
||||||
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_rowFromTemplate:(CPPredicateEditorRowTemplate)aTemplate originalTemplate:(CPPredicateEditorRowTemplate)originalTemplate withRowType:(CPRuleEditorRowType)rowType
|
|
||||||
{
|
|
||||||
var criteria = [CPArray array],
|
|
||||||
values = [CPArray array],
|
|
||||||
templateViews = [aTemplate templateViews],
|
|
||||||
rootItems,
|
|
||||||
count;
|
|
||||||
|
|
||||||
rootItems = (rowType == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
|
|
||||||
|
|
||||||
while ((count = [rootItems count]) > 0)
|
|
||||||
{
|
|
||||||
var treeChild;
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
treeChild = [rootItems objectAtIndex:i];
|
|
||||||
|
|
||||||
var currentView = [templateViews objectAtIndex:[treeChild indexIntoTemplate]],
|
|
||||||
title = [treeChild title];
|
|
||||||
|
|
||||||
if (title == nil || [title isEqual:[currentView titleOfSelectedItem]])
|
|
||||||
{
|
|
||||||
var node = [_CPPredicateEditorRowNode rowNodeFromTree:treeChild];
|
|
||||||
[node applyTemplate:aTemplate withViews:templateViews forOriginalTemplate:originalTemplate];
|
|
||||||
|
|
||||||
[criteria addObject:node];
|
|
||||||
[values addObject:[node displayValue]];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rootItems = [treeChild children];
|
|
||||||
}
|
|
||||||
|
|
||||||
var row = @{
|
|
||||||
@"criteria": criteria,
|
|
||||||
@"displayValues": values,
|
|
||||||
@"rowType": rowType,
|
|
||||||
};
|
|
||||||
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Get the predicate
|
|
||||||
|
|
||||||
- (void)_updatePredicate
|
|
||||||
{
|
|
||||||
[self _updatePredicateFromRows];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updatePredicateFromRows
|
|
||||||
{
|
|
||||||
var rootRowsArray = [super _rootRowsArray],
|
|
||||||
subpredicates = [CPMutableArray array],
|
|
||||||
count,
|
|
||||||
count2 = count = [rootRowsArray count],
|
|
||||||
predicate;
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
var item = [rootRowsArray objectAtIndex:count],
|
|
||||||
subpredicate = [self _predicateFromRowItem:item];
|
|
||||||
|
|
||||||
if (subpredicate != nil)
|
|
||||||
[subpredicates insertObject:subpredicate atIndex:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_nestingMode != CPRuleEditorNestingModeList && count2 == 1)
|
|
||||||
predicate = [subpredicates lastObject];
|
|
||||||
else
|
|
||||||
predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:subpredicates];
|
|
||||||
|
|
||||||
[self _setPredicate:predicate];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_predicateFromRowItem:(id)rowItem
|
|
||||||
{
|
|
||||||
var subpredicates = [CPArray array],
|
|
||||||
rowType = [rowItem valueForKey:_typeKeyPath];
|
|
||||||
|
|
||||||
if (rowType == CPRuleEditorRowTypeCompound)
|
|
||||||
{
|
|
||||||
var subrows = [rowItem valueForKey:_subrowsArrayKeyPath],
|
|
||||||
count = [subrows count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var subrow = [subrows objectAtIndex:i],
|
|
||||||
predicate = [self _predicateFromRowItem:subrow];
|
|
||||||
|
|
||||||
[subpredicates addObject:predicate];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var criteria = [rowItem valueForKey:_itemsKeyPath],
|
|
||||||
displayValues = [rowItem valueForKey:_valuesKeyPath],
|
|
||||||
count = [criteria count],
|
|
||||||
lastItem = [criteria lastObject],
|
|
||||||
template = [lastItem templateForRow],
|
|
||||||
templateViews = [template templateViews];
|
|
||||||
|
|
||||||
for (var j = 0; j < count; j++)
|
|
||||||
{
|
|
||||||
var view = [templateViews objectAtIndex:j],
|
|
||||||
value = [displayValues objectAtIndex:j];
|
|
||||||
[[criteria objectAtIndex:j] setTemplateViews:templateViews];
|
|
||||||
|
|
||||||
if ([view isKindOfClass:[CPPopUpButton class]])
|
|
||||||
[view selectItemWithTitle:value];
|
|
||||||
else if ([view respondsToSelector:@selector(setObjectValue:)])
|
|
||||||
[view setObjectValue:[value objectValue]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [template predicateWithSubpredicates:subpredicates];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPCompoundPredicateType)_compoundPredicateTypeForRootRows
|
|
||||||
{
|
|
||||||
return CPAndPredicateType;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Control delegate
|
|
||||||
|
|
||||||
- (void)_sendRuleAction
|
|
||||||
{
|
|
||||||
[super _sendRuleAction];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_sendsActionOnIncompleteTextChange
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
- (void)_setDefaultTargetAndActionOnView:(CPView)view
|
|
||||||
{
|
|
||||||
if ([view isKindOfClass:[CPControl class]])
|
|
||||||
{
|
|
||||||
[view setTarget:self];
|
|
||||||
[view setAction:@selector(_templateControlValueDidChange:)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
- (void)_templateControlValueDidChange:(id)sender
|
|
||||||
{
|
|
||||||
}
|
|
||||||
- (void)controlTextDidBeginEditing:(CPNotification)notification
|
|
||||||
{
|
|
||||||
}
|
|
||||||
- (void)controlTextDidEndEditing:(CPNotification)notification
|
|
||||||
{
|
|
||||||
}
|
|
||||||
- (void)controlTextDidChange:(CPNotification)notification
|
|
||||||
{
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma mark RuleEditor delegate methods
|
|
||||||
|
|
||||||
- (int)_queryNumberOfChildrenOfItem:(id)rowItem withRowType:(int)type
|
|
||||||
{
|
|
||||||
if (rowItem == nil)
|
|
||||||
{
|
|
||||||
var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
|
|
||||||
return [trees count];
|
|
||||||
}
|
|
||||||
return [[rowItem children] count];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_queryChild:(int)childIndex ofItem:(id)rowItem withRowType:(int)type
|
|
||||||
{
|
|
||||||
if (rowItem == nil)
|
|
||||||
{
|
|
||||||
var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
|
|
||||||
return [_CPPredicateEditorRowNode rowNodeFromTree:trees[childIndex]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[rowItem children] objectAtIndex:childIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_queryValueForItem:(id)rowItem inRow:(int)rowIndex
|
|
||||||
{
|
|
||||||
return [rowItem displayValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPPredicateTemplatesKey = @"CPPredicateTemplates";
|
|
||||||
|
|
||||||
@implementation CPPredicateEditor (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(id)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
var nibTemplates = [aCoder decodeObjectForKey:CPPredicateTemplatesKey];
|
|
||||||
|
|
||||||
if (nibTemplates != nil)
|
|
||||||
[self setRowTemplates:nibTemplates];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(id)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
[aCoder encodeObject:_allTemplates forKey:CPPredicateTemplatesKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPPredicateEditorValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[_source _reflectPredicate:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
[_source _reflectPredicate:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
/*! @endcond */
|
|
||||||
@@ -1,801 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPPredicateEditorRowTemplate.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by cacaodev.
|
|
||||||
* Copyright 2011, cacaodev.
|
|
||||||
*
|
|
||||||
* 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/CPPredicate.j>
|
|
||||||
|
|
||||||
@import "CPCheckBox.j"
|
|
||||||
@import "CPPopUpButton.j"
|
|
||||||
@import "CPMenuItem.j"
|
|
||||||
@import "CPTextField.j"
|
|
||||||
|
|
||||||
// NOTE: CPDatePicker is not implemented yet
|
|
||||||
@class CPDatePicker
|
|
||||||
|
|
||||||
|
|
||||||
CPUndefinedAttributeType = 0;
|
|
||||||
CPInteger16AttributeType = 100;
|
|
||||||
CPInteger32AttributeType = 200;
|
|
||||||
CPInteger64AttributeType = 300;
|
|
||||||
CPDecimalAttributeType = 400;
|
|
||||||
CPDoubleAttributeType = 500;
|
|
||||||
CPFloatAttributeType = 600;
|
|
||||||
CPStringAttributeType = 700;
|
|
||||||
CPBooleanAttributeType = 800;
|
|
||||||
CPDateAttributeType = 900;
|
|
||||||
CPBinaryDataAttributeType = 1000;
|
|
||||||
CPTransformableAttributeType = 1800;
|
|
||||||
|
|
||||||
@implementation CPPredicateEditorRowTemplate : CPObject
|
|
||||||
{
|
|
||||||
int _templateType @accessors(readwrite, getter=_templateType, setter=_setTemplateType:);
|
|
||||||
unsigned _predicateOptions @accessors(readwrite, setter=_setOptions:);
|
|
||||||
unsigned _predicateModifier @accessors(readwrite, setter=_setModifier:);
|
|
||||||
unsigned _leftAttributeType @accessors(readwrite, getter=leftAttributeType, setter=_setLeftAttributeType:);
|
|
||||||
unsigned _rightAttributeType @accessors(readwrite, getter=rightAttributeType, setter=_setRightAttributeType:);
|
|
||||||
BOOL _leftIsWildcard @accessors(property=leftIsWildcard);
|
|
||||||
BOOL _rightIsWildcard @accessors(property=rightIsWildcard);
|
|
||||||
CPArray _views @accessors(setter=setTemplateViews:);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
@class CPPredicateEditorRowTemplate
|
|
||||||
|
|
||||||
@brief CPPredicateEditorRowTemplate describes available predicates and how to display them.
|
|
||||||
|
|
||||||
You can create instances of CPPredicateEditorRowTemplate programmatically or in Interface Builder. By default, a non-compound row template has three views: a popup (or static text field) on the left, a popup or static text field for operators, and either a popup or other view on the right. You can subclass CPPredicateEditorRowTemplate to create a row template with different numbers or types of views.
|
|
||||||
|
|
||||||
CPPredicateEditorRowTemplate is a concrete class, but it has five primitive methods which are called by CPPredicateEditor: -#templateViews, -#matchForPredicate:, -#setPredicate:, -#displayableSubpredicatesOfPredicate:, and -#predicateWithSubpredicates:. CPPredicateEditorRowTemplate implements all of them, but you can override them for custom templates. The primitive methods are used by an instance of CPPredicateEditor as follows.
|
|
||||||
|
|
||||||
First, an instance of CPPredicateEditor is created, and some row templates are set on it—either through a nib file or programmatically. The first thing predicate editor does is ask each of the templates for their views, using templateViews.
|
|
||||||
|
|
||||||
After setting up the predicate editor, you typically send it a CPPredicateEditor#setObjectValue: message to restore a saved predicate. CPPredicateEditor needs to determine which of its templates should display each predicate in the predicate tree. It does this by sending each of its row templates a matchForPredicate: message and choosing the one that returns the highest value.
|
|
||||||
|
|
||||||
After finding the best match for a predicate, CPPredicateEditor copies that template to get fresh views, inserts them into the proper row, and then sets the predicate on the template using setPredicate:. Within that method, the CPPredicateEditorRowTemplate object must set its views' values to represent that predicate.
|
|
||||||
|
|
||||||
CPPredicateEditorRowTemplate next asks the template for the “displayable sub-predicates” of the predicate by sending a -#displayableSubpredicatesOfPredicate: message. If a template represents a predicate in its entirety, or if the predicate has no subpredicates, it can return nil for this. Otherwise, it should return a list of predicates to be made into sub-rows of that template's row. The whole process repeats for each sub-predicate.
|
|
||||||
|
|
||||||
At this point, the user sees the predicate that was saved. If the user then makes some changes to the views of the templates, this causes CPPredicateEditor to recompute its predicate by asking each of the templates to return the predicate represented by the new view values, passing in the subpredicates represented by the sub-rows (an empty array if there are none, or nil if they aren't supported by that predicate type).
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@name Initializing a Template
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Initializes and returns a “pop-up-pop-up-pop-up”-style row template.
|
|
||||||
@param leftExpressions An array of CPExpression objects that represent the left hand side of a predicate.
|
|
||||||
@param rightExpressions An array of CPExpression objects that represent the right hand side of a predicate.
|
|
||||||
@param modifier A modifier for the predicate (see @c CPComparisonPredicateModifier for possible values).
|
|
||||||
@param operators An array of CPNumber objects specifying the operator type (see @c CPPredicateOperatorType for possible values).
|
|
||||||
@param options Options for the predicate (see @c CPComparisonPredicateOptions for possible values).
|
|
||||||
@return A row template of the “pop-up-pop-up-pop-up”-form, with the left and right popups representing the left and right expression arrays -#leftExpressions and -#rightExpressions, and the center popup representing the operators.
|
|
||||||
*/
|
|
||||||
- (id)initWithLeftExpressions:(CPArray)leftExpressions rightExpressions:(CPArray)rightExpressions modifier:(int)modifier operators:(CPArray)operators options:(int)options
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
_templateType = 1;
|
|
||||||
_leftIsWildcard = NO;
|
|
||||||
_rightIsWildcard = NO;
|
|
||||||
_leftAttributeType = 0;
|
|
||||||
_rightAttributeType = 0;
|
|
||||||
_predicateModifier = modifier;
|
|
||||||
_predicateOptions = options;
|
|
||||||
|
|
||||||
var leftView = [self _viewFromExpressions:leftExpressions],
|
|
||||||
rightView = [self _viewFromExpressions:rightExpressions],
|
|
||||||
middleView = [self _viewFromOperatorTypes:operators];
|
|
||||||
|
|
||||||
_views = [[CPArray alloc] initWithObjects:leftView, middleView, rightView];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Initializes and returns a “pop-up-pop-up-view”-style row template.
|
|
||||||
@param leftExpressions An array of CPExpression objects that represent the left hand side of a predicate.
|
|
||||||
@param attributeType An attribute type for the right hand side of a predicate. This value dictates the type of view created, and how the control’s object value is coerced before putting it into a predicate.
|
|
||||||
@param modifier A modifier for the predicate (see @c CPComparisonPredicateModifier for possible values).
|
|
||||||
@param operators An array of CPNumber objects specifying the operator type (see @c CPPredicateOperatorType for possible values).
|
|
||||||
@param options Options for the predicate (see CPComparisonPredicateOptions for possible values).
|
|
||||||
@return A row template initialized using the given arguments.
|
|
||||||
*/
|
|
||||||
- (id)initWithLeftExpressions:(CPArray )leftExpressions rightExpressionAttributeType:(CPAttributeType)attributeType modifier:(CPComparisonPredicateModifier)modifier operators:(CPArray )operators options:(int)options
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
var leftView = [self _viewFromExpressions:leftExpressions],
|
|
||||||
middleView = [self _viewFromOperatorTypes:operators],
|
|
||||||
rightView = [self _viewFromAttributeType:attributeType];
|
|
||||||
|
|
||||||
_templateType = 1;
|
|
||||||
_leftIsWildcard = NO;
|
|
||||||
_rightIsWildcard = YES;
|
|
||||||
_leftAttributeType = 0;
|
|
||||||
_rightAttributeType = attributeType;
|
|
||||||
_predicateModifier = modifier;
|
|
||||||
_predicateOptions = options;
|
|
||||||
_views = [[CPArray alloc] initWithObjects:leftView, middleView, rightView];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Initializes and returns a row template suitable for displaying compound predicates.
|
|
||||||
@param compoundTypes An array of CPNumber objects specifying compound predicate types. See @c CPCompoundPredicateTypes for possible values.
|
|
||||||
@return A row template initialized for displaying compound predicates of the types specified by @a compoundTypes.
|
|
||||||
@discussion CPPredicateEditor contains such a template by default.
|
|
||||||
*/
|
|
||||||
- (id)initWithCompoundTypes:(CPArray )compoundTypes
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
var leftView = [self _viewFromCompoundTypes:compoundTypes],
|
|
||||||
rightView = [[CPPopUpButton alloc] init];
|
|
||||||
|
|
||||||
[rightView addItemWithTitle:@"of the following are true"];
|
|
||||||
|
|
||||||
_templateType = 2;
|
|
||||||
_leftIsWildcard = NO;
|
|
||||||
_rightIsWildcard = NO;
|
|
||||||
_rightAttributeType = 0;
|
|
||||||
_views = [[CPArray alloc] initWithObjects:leftView, rightView];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@name Primitive Methods
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns a positive number if the receiver can represent a given predicate, and 0 if it cannot.
|
|
||||||
@return A positive number if the template can represent predicate, and @c 0 if it cannot.
|
|
||||||
@discussion By default, returns values in the range @c 0 to @c 1.
|
|
||||||
The highest match among all the templates determines which template is responsible for displaying the predicate. You can override this to determine which predicates your custom template handles.
|
|
||||||
*/
|
|
||||||
- (double)matchForPredicate:(CPPredicate)predicate
|
|
||||||
{
|
|
||||||
// How exactly this value (float 0-1) is computed ?
|
|
||||||
if ([self _templateType] == 2 && [predicate isKindOfClass:[CPCompoundPredicate class]])
|
|
||||||
{
|
|
||||||
if ([[self compoundTypes] containsObject:[predicate compoundPredicateType]])
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if ([self _templateType] == 1 && [predicate isKindOfClass:[CPComparisonPredicate class]])
|
|
||||||
{
|
|
||||||
if (!_leftIsWildcard && ![[self leftExpressions] containsObject:[predicate leftExpression]])
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (![[self operators] containsObject:[predicate predicateOperatorType]])
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!_rightIsWildcard && ![[self rightExpressions] containsObject:[predicate rightExpression]])
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the views for the receiver.
|
|
||||||
@return The views for the receiver.
|
|
||||||
@discussion Instances of CPPopUpButton are treated specially by CPPredicateEditor; their menu items are merged into a single popup button, and matching menu item titles are combined. In this way, a single tree is built from the separate templates.
|
|
||||||
*/
|
|
||||||
- (CPArray)templateViews
|
|
||||||
{
|
|
||||||
return _views;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Sets the value of the views according to the given predicate.
|
|
||||||
@param predicate The predicate value for the receiver.
|
|
||||||
@discussion This method is only called if -#matchForPredicate: returned a positive value for the receiver.
|
|
||||||
|
|
||||||
You can override this to set the values of custom views.
|
|
||||||
*/
|
|
||||||
- (void)setPredicate:(CPPredicate)predicate
|
|
||||||
{
|
|
||||||
if (_templateType == 2)
|
|
||||||
[self _setCompoundPredicate:predicate];
|
|
||||||
else
|
|
||||||
[self _setComparisonPredicate:predicate];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the subpredicates that should be made sub-rows of a given predicate.
|
|
||||||
@param predicate A predicate object.
|
|
||||||
@return The subpredicates that should be made sub-rows of @a predicate. For compound predicates (instances of CPCompoundPredicate), the array of subpredicates; for other types of predicate, returns @c nil. If a template represents a predicate in its entirety, or if the predicate has no subpredicates, returns @c nil.
|
|
||||||
@discussion You can override this method to create custom templates that handle complicated compound predicates.
|
|
||||||
*/
|
|
||||||
- (CPArray)displayableSubpredicatesOfPredicate:(CPPredicate)predicate
|
|
||||||
{
|
|
||||||
if ([predicate isKindOfClass:[CPCompoundPredicate class]])
|
|
||||||
{
|
|
||||||
var subpredicates = [predicate subpredicates];
|
|
||||||
if ([subpredicates count] == 0)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return subpredicates;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the predicate represented by the receiver’s views' values and the given sub-predicates.
|
|
||||||
@param subpredicates An array of predicates.
|
|
||||||
@return The predicate represented by the values of the template's views and the given @a subpredicates. You can override this method to return the predicate represented by your custom views.
|
|
||||||
@discussion This method is only called if -#matchForPredicate: returned a positive value for the receiver.
|
|
||||||
|
|
||||||
You can override this method to return the predicate represented by a custom view.
|
|
||||||
*/
|
|
||||||
- (CPPredicate)predicateWithSubpredicates:(CPArray)subpredicates
|
|
||||||
{
|
|
||||||
if (_templateType == 2)
|
|
||||||
{
|
|
||||||
var type = [[_views[0] selectedItem] representedObject];
|
|
||||||
return [[CPCompoundPredicate alloc] initWithType:type subpredicates:subpredicates];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_templateType == 1)
|
|
||||||
{
|
|
||||||
var lhs = [self _leftExpression],
|
|
||||||
rhs = [self _rightExpression],
|
|
||||||
operator = [[_views[1] selectedItem] representedObject];
|
|
||||||
|
|
||||||
return [CPComparisonPredicate predicateWithLeftExpression:lhs
|
|
||||||
rightExpression:rhs
|
|
||||||
modifier:[self modifier]
|
|
||||||
type:operator
|
|
||||||
options:[self options]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@name Information About a Row Template
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the left hand expressions for the receiver.
|
|
||||||
@return The left hand expressions for the receiver.
|
|
||||||
*/
|
|
||||||
- (CPArray)leftExpressions
|
|
||||||
{
|
|
||||||
if (_templateType == 1 && !_leftIsWildcard)
|
|
||||||
{
|
|
||||||
var view = [_views objectAtIndex:0];
|
|
||||||
return [[view itemArray] valueForKey:@"representedObject"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the right hand expressions for the receiver.
|
|
||||||
@return The right hand expressions for the receiver.
|
|
||||||
*/
|
|
||||||
- (CPArray)rightExpressions
|
|
||||||
{
|
|
||||||
if (_templateType == 1 && !_rightIsWildcard)
|
|
||||||
{
|
|
||||||
var view = [_views objectAtIndex:2];
|
|
||||||
return [[view itemArray] valueForKey:@"representedObject"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the compound predicate types for the receiver.
|
|
||||||
@return An array of CPNumber objects specifying compound predicate types. See @c CompoundPredicateTypes for possible values.
|
|
||||||
*/
|
|
||||||
- (CPArray)compoundTypes
|
|
||||||
{
|
|
||||||
if (_templateType == 2)
|
|
||||||
{
|
|
||||||
var view = [_views objectAtIndex:0];
|
|
||||||
return [[view itemArray] valueForKey:@"representedObject"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the comparison predicate modifier for the receiver.
|
|
||||||
@return The comparison predicate modifier for the receiver.
|
|
||||||
*/
|
|
||||||
- (CPComparisonPredicateModifier)modifier
|
|
||||||
{
|
|
||||||
if (_templateType == 1)
|
|
||||||
return _predicateModifier;
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns Returns the array of operators for the receiver.
|
|
||||||
@return The array of operators for the receiver.
|
|
||||||
*/
|
|
||||||
- (CPArray)operators
|
|
||||||
{
|
|
||||||
if (_templateType == 1)
|
|
||||||
{
|
|
||||||
var view = [_views objectAtIndex:1];
|
|
||||||
return [[view itemArray] valueForKey:@"representedObject"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the comparison predicate options for the receiver.
|
|
||||||
@return The comparison predicate options for the receiver. See @c CPComparisonPredicateOptions for possible values. Returns @c 0 if this does not apply (for example, for a compound template initialized with -#initWithCompoundTypes:).
|
|
||||||
*/
|
|
||||||
- (int)options
|
|
||||||
{
|
|
||||||
if (_templateType == 1)
|
|
||||||
return _predicateOptions;
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the attribute type of the receiver’s right expression.
|
|
||||||
@return The attribute type of the receiver’s right expression.
|
|
||||||
*/
|
|
||||||
- (CPAttributeType)rightExpressionAttributeType
|
|
||||||
{
|
|
||||||
return _rightAttributeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Returns the attribute type of the receiver’s left expression.
|
|
||||||
@return The attribute type of the receiver’s left expression.
|
|
||||||
*/
|
|
||||||
- (CPAttributeType)leftExpressionAttributeType
|
|
||||||
{
|
|
||||||
return _leftAttributeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @cond */
|
|
||||||
+ (id)_bestMatchForPredicate:(CPPredicate)predicate inTemplates:(CPArray)templates quality:(double)quality
|
|
||||||
{
|
|
||||||
var count = [templates count],
|
|
||||||
match_value = 0,
|
|
||||||
templateIndex = CPNotFound,
|
|
||||||
i;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var template = [templates objectAtIndex:i],
|
|
||||||
amatch = [template matchForPredicate:predicate];
|
|
||||||
|
|
||||||
if (amatch > match_value)
|
|
||||||
{
|
|
||||||
templateIndex = i;
|
|
||||||
match_value = amatch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (templateIndex == CPNotFound)
|
|
||||||
{
|
|
||||||
[CPException raise:CPRangeException reason:@"Unable to find template matching predicate: " + [predicate predicateFormat]];
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [templates objectAtIndex:templateIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setCompoundPredicate:(CPCompoundPredicate)predicate
|
|
||||||
{
|
|
||||||
var left = [_views objectAtIndex:0],
|
|
||||||
type = [predicate compoundPredicateType],
|
|
||||||
index = [left indexOfItemWithRepresentedObject:type];
|
|
||||||
|
|
||||||
[left selectItemAtIndex:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setComparisonPredicate:(CPComparisonPredicate)predicate
|
|
||||||
{
|
|
||||||
var left = [_views objectAtIndex:0],
|
|
||||||
middle = [_views objectAtIndex:1],
|
|
||||||
right = [_views objectAtIndex:2],
|
|
||||||
leftExpression = [predicate leftExpression],
|
|
||||||
rightExpression = [predicate rightExpression],
|
|
||||||
operator = [predicate predicateOperatorType];
|
|
||||||
|
|
||||||
if (_leftIsWildcard)
|
|
||||||
[left setObjectValue:[leftExpression constantValue]];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var index = [left indexOfItemWithRepresentedObject:leftExpression];
|
|
||||||
[left selectItemAtIndex:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
var op_index = [middle indexOfItemWithRepresentedObject:operator];
|
|
||||||
[middle selectItemAtIndex:op_index];
|
|
||||||
|
|
||||||
if (_rightIsWildcard)
|
|
||||||
[right setObjectValue:[rightExpression constantValue]];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var index = [right indexOfItemWithRepresentedObject:rightExpression];
|
|
||||||
[right selectItemAtIndex:index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPExpression)_leftExpression
|
|
||||||
{
|
|
||||||
return [self _expressionFromView:_views[0] forAttributeType:_leftAttributeType];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPExpression)_rightExpression
|
|
||||||
{
|
|
||||||
return [self _expressionFromView:_views[2] forAttributeType:_rightAttributeType];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPExpression)_expressionFromView:(CPView)aView forAttributeType:(CPAttributeType)attributeType
|
|
||||||
{
|
|
||||||
if (attributeType == 0)
|
|
||||||
return [[aView selectedItem] representedObject];
|
|
||||||
|
|
||||||
var value;
|
|
||||||
if (attributeType >= CPInteger16AttributeType && attributeType <= CPFloatAttributeType)
|
|
||||||
value = [aView intValue];
|
|
||||||
else if (attributeType == CPBooleanAttributeType)
|
|
||||||
value = [aView state];
|
|
||||||
else
|
|
||||||
value = [aView stringValue];
|
|
||||||
|
|
||||||
return [CPExpression expressionForConstantValue:value];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)_rowType
|
|
||||||
{
|
|
||||||
return (_templateType - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)copy
|
|
||||||
{
|
|
||||||
return [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:self]];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)_operatorsForAttributeType:(CPAttributeType)attributeType
|
|
||||||
{
|
|
||||||
var operators_array = [CPMutableArray array];
|
|
||||||
|
|
||||||
switch (attributeType)
|
|
||||||
{
|
|
||||||
case CPInteger16AttributeType : [operators_array addObjects:4,5,0,2,1,3];
|
|
||||||
break;
|
|
||||||
case CPInteger32AttributeType : [operators_array addObjects:4,5,0,2,1,3];
|
|
||||||
break;
|
|
||||||
case CPInteger64AttributeType : [operators_array addObjects:4,5,0,2,1,3];
|
|
||||||
break;
|
|
||||||
case CPDecimalAttributeType : [operators_array addObjects:4,5,0,2,1,3];
|
|
||||||
break;
|
|
||||||
case CPDoubleAttributeType : [operators_array addObjects:4,5,0,2,1,3];
|
|
||||||
break;
|
|
||||||
case CPFloatAttributeType : [operators_array addObjects:4,5,0,2,1,3];
|
|
||||||
break;
|
|
||||||
case CPStringAttributeType : [operators_array addObjects:99,4,5,8,9];
|
|
||||||
break;
|
|
||||||
case CPBooleanAttributeType : [operators_array addObjects:4,5];
|
|
||||||
break;
|
|
||||||
case CPDateAttributeType : [operators_array addObjects:4,5,0,2,1,3];
|
|
||||||
break;
|
|
||||||
default : CPLogConsole("Cannot create operators for an CPAttributeType " + attributeType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return operators_array;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)_templateType
|
|
||||||
{
|
|
||||||
return _templateType;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_displayValueForPredicateOperator:(int)operator
|
|
||||||
{
|
|
||||||
var value;
|
|
||||||
|
|
||||||
switch (operator)
|
|
||||||
{
|
|
||||||
case CPLessThanPredicateOperatorType : value = @"is less than";
|
|
||||||
break;
|
|
||||||
case CPLessThanOrEqualToPredicateOperatorType : value = @"is less than or equal to";
|
|
||||||
break;
|
|
||||||
case CPGreaterThanPredicateOperatorType : value = @"is greater than";
|
|
||||||
break;
|
|
||||||
case CPGreaterThanOrEqualToPredicateOperatorType : value = @"is greater than or equal to";
|
|
||||||
break;
|
|
||||||
case CPEqualToPredicateOperatorType : value = @"is";
|
|
||||||
break;
|
|
||||||
case CPNotEqualToPredicateOperatorType : value = @"is not";
|
|
||||||
break;
|
|
||||||
case CPMatchesPredicateOperatorType : value = @"matches";
|
|
||||||
break;
|
|
||||||
case CPLikePredicateOperatorType : value = @"is like";
|
|
||||||
break;
|
|
||||||
case CPBeginsWithPredicateOperatorType : value = @"begins with";
|
|
||||||
break;
|
|
||||||
case CPEndsWithPredicateOperatorType : value = @"ends with";
|
|
||||||
break;
|
|
||||||
case CPInPredicateOperatorType : value = @"in";
|
|
||||||
break;
|
|
||||||
case CPContainsPredicateOperatorType : value = @"contains";
|
|
||||||
break;
|
|
||||||
case CPBetweenPredicateOperatorType : value = @"between";
|
|
||||||
break;
|
|
||||||
default : CPLogConsole(@"unknown predicate operator %d" + operator);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_displayValueForCompoundPredicateType:(unsigned int)predicateType
|
|
||||||
{
|
|
||||||
var value;
|
|
||||||
switch (predicateType)
|
|
||||||
{
|
|
||||||
case CPNotPredicateType: value = @"None";
|
|
||||||
break;
|
|
||||||
case CPAndPredicateType: value = @"All";
|
|
||||||
break;
|
|
||||||
case CPOrPredicateType: value = @"Any";
|
|
||||||
break;
|
|
||||||
default : value = [CPString stringWithFormat:@"unknown compound predicate type %d",predicateType];
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_displayValueForConstantValue:(id)value
|
|
||||||
{
|
|
||||||
return [value description]; // number, date, string, ... localize
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_displayValueForKeyPath:(CPString)keyPath
|
|
||||||
{
|
|
||||||
return keyPath; // localize
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPPopUpButton)_viewFromExpressions:(CPArray)expressions
|
|
||||||
{
|
|
||||||
var popup = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 100, 18)],
|
|
||||||
count = [expressions count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var exp = expressions[i],
|
|
||||||
type = [exp expressionType],
|
|
||||||
title;
|
|
||||||
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case CPKeyPathExpressionType: title = [self _displayValueForKeyPath:[exp keyPath]];
|
|
||||||
break;
|
|
||||||
case CPConstantValueExpressionType: title = [self _displayValueForConstantValue:[exp constantValue]];
|
|
||||||
break;
|
|
||||||
default: [CPException raise:CPInvalidArgumentException reason:@"Invalid Expression type " + type];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = [[CPMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""];
|
|
||||||
[item setRepresentedObject:exp];
|
|
||||||
[popup addItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
[popup sizeToFit];
|
|
||||||
|
|
||||||
return popup;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPPopUpButton)_viewFromOperatorTypes:(CPArray)operators
|
|
||||||
{
|
|
||||||
var popup = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 100, 18)],
|
|
||||||
count = [operators count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var op = operators[i],
|
|
||||||
title = [self _displayValueForPredicateOperator:op],
|
|
||||||
item = [[CPMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""];
|
|
||||||
|
|
||||||
[item setRepresentedObject:op];
|
|
||||||
[popup addItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
[popup sizeToFit];
|
|
||||||
|
|
||||||
return popup;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)_viewFromCompoundTypes:(CPArray)compoundTypes
|
|
||||||
{
|
|
||||||
var popup = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 100, 18)],
|
|
||||||
count = [compoundTypes count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var type = compoundTypes[i],
|
|
||||||
title = [self _displayValueForCompoundPredicateType:type],
|
|
||||||
item = [[CPMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""];
|
|
||||||
|
|
||||||
[item setRepresentedObject:type];
|
|
||||||
[popup addItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
[popup sizeToFit];
|
|
||||||
|
|
||||||
return popup;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)_viewFromAttributeType:(CPAttributeType)attributeType
|
|
||||||
{
|
|
||||||
var view;
|
|
||||||
|
|
||||||
if (attributeType >= CPInteger16AttributeType && attributeType <= CPFloatAttributeType)
|
|
||||||
{
|
|
||||||
view = [self _textFieldWithFrame:CGRectMake(0, 0, 50, 26)];
|
|
||||||
}
|
|
||||||
else if (attributeType == CPStringAttributeType)
|
|
||||||
{
|
|
||||||
view = [self _textFieldWithFrame:CGRectMake(0, 0, 150, 26)];
|
|
||||||
}
|
|
||||||
else if (attributeType == CPBooleanAttributeType)
|
|
||||||
{
|
|
||||||
view = [[CPCheckBox alloc] initWithFrame:CGRectMake(0, 0, 50, 26)];
|
|
||||||
}
|
|
||||||
else if (attributeType == CPDateAttributeType)
|
|
||||||
view = [[CPDatePicker alloc] initWithFrame:CGRectMake(0, 0, 150, 26)];
|
|
||||||
else
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
[view setTag:attributeType];
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPTextField)_textFieldWithFrame:(CGRect)frame
|
|
||||||
{
|
|
||||||
var textField = [[CPTextField alloc] initWithFrame:frame];
|
|
||||||
[textField setBezeled:YES];
|
|
||||||
[textField setBezelStyle:CPTextFieldSquareBezel];
|
|
||||||
[textField setBordered:YES];
|
|
||||||
[textField setEditable:YES];
|
|
||||||
[textField setFont:[CPFont systemFontOfSize:10]];
|
|
||||||
[textField setSendsActionOnEndEditing:YES];
|
|
||||||
|
|
||||||
return textField;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setOptions:(unsigned int)options
|
|
||||||
{
|
|
||||||
_predicateOptions = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setModifier:(unsigned int)modifier
|
|
||||||
{
|
|
||||||
_predicateModifier = modifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
if (_templateType == 2)
|
|
||||||
return [CPString stringWithFormat:@"<%@ %p %@>",[self className],self,[[self compoundTypes] componentsJoinedByString:@", "]];
|
|
||||||
else if (_templateType == 1 && _rightIsWildcard)
|
|
||||||
return [CPString stringWithFormat:@"<%@ %p [%@] [%@] %d>",[self className],self,[[self leftExpressions] componentsJoinedByString:@", "],[[self operators] componentsJoinedByString:@", "],[self rightExpressionAttributeType]];
|
|
||||||
else
|
|
||||||
return [CPString stringWithFormat:@"<%@ %p [%@] [%@] [%@]>",[self className],self,[[self leftExpressions] componentsJoinedByString:@", "],[[self operators] componentsJoinedByString:@", "],[[self rightExpressions] componentsJoinedByString:@", "]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
- (void)_setLeftExpressionObject:(id)object
|
|
||||||
{
|
|
||||||
}
|
|
||||||
- (void)_setRightExpressionObject:(id)object
|
|
||||||
{
|
|
||||||
}
|
|
||||||
- (BOOL)_predicateIsNoneAreTrue:(id)predicate
|
|
||||||
{
|
|
||||||
}
|
|
||||||
- (id)_viewFromExpressionObject:(id)object
|
|
||||||
{
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPPredicateTemplateTypeKey = @"CPPredicateTemplateType",
|
|
||||||
CPPredicateTemplateOptionsKey = @"CPPredicateTemplateOptions",
|
|
||||||
CPPredicateTemplateModifierKey = @"CPPredicateTemplateModifier",
|
|
||||||
CPPredicateTemplateLeftAttributeTypeKey = @"CPPredicateTemplateLeftAttributeType",
|
|
||||||
CPPredicateTemplateRightAttributeTypeKey = @"CPPredicateTemplateRightAttributeType",
|
|
||||||
CPPredicateTemplateLeftIsWildcardKey = @"CPPredicateTemplateLeftIsWildcard",
|
|
||||||
CPPredicateTemplateRightIsWildcardKey = @"CPPredicateTemplateRightIsWildcard",
|
|
||||||
CPPredicateTemplateViewsKey = @"CPPredicateTemplateViews";
|
|
||||||
|
|
||||||
@implementation CPPredicateEditorRowTemplate (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)coder
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
_templateType = [coder decodeIntForKey:CPPredicateTemplateTypeKey];
|
|
||||||
_predicateOptions = [coder decodeIntForKey:CPPredicateTemplateOptionsKey];
|
|
||||||
_predicateModifier = [coder decodeIntForKey:CPPredicateTemplateModifierKey];
|
|
||||||
_leftAttributeType = [coder decodeIntForKey:CPPredicateTemplateLeftAttributeTypeKey];
|
|
||||||
_rightAttributeType = [coder decodeIntForKey:CPPredicateTemplateRightAttributeTypeKey];
|
|
||||||
_leftIsWildcard = [coder decodeBoolForKey:CPPredicateTemplateLeftIsWildcardKey];
|
|
||||||
_rightIsWildcard = [coder decodeBoolForKey:CPPredicateTemplateRightIsWildcardKey];
|
|
||||||
_views = [coder decodeObjectForKey:CPPredicateTemplateViewsKey];
|
|
||||||
|
|
||||||
// In Xcode 4, when the menu item title == template's expression keypath, representedObject is empty.
|
|
||||||
// So we need to regenerate expressions from titles.
|
|
||||||
if (_templateType == 1 && _leftIsWildcard == NO)
|
|
||||||
{
|
|
||||||
var itemArray = [_views[0] itemArray],
|
|
||||||
count = [itemArray count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var item = itemArray[i];
|
|
||||||
if ([item representedObject] == nil)
|
|
||||||
{
|
|
||||||
var exp = [CPExpression expressionForKeyPath:[item title]];
|
|
||||||
[item setRepresentedObject:exp];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)coder
|
|
||||||
{
|
|
||||||
[coder encodeInt:_templateType forKey:CPPredicateTemplateTypeKey];
|
|
||||||
[coder encodeInt:_predicateOptions forKey:CPPredicateTemplateOptionsKey];
|
|
||||||
[coder encodeInt:_predicateModifier forKey:CPPredicateTemplateModifierKey];
|
|
||||||
[coder encodeInt:_leftAttributeType forKey:CPPredicateTemplateLeftAttributeTypeKey];
|
|
||||||
[coder encodeInt:_rightAttributeType forKey:CPPredicateTemplateRightAttributeTypeKey];
|
|
||||||
[coder encodeBool:_leftIsWildcard forKey:CPPredicateTemplateLeftIsWildcardKey];
|
|
||||||
[coder encodeBool:_rightIsWildcard forKey:CPPredicateTemplateRightIsWildcardKey];
|
|
||||||
[coder encodeObject:_views forKey:CPPredicateTemplateViewsKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
/*! @endcond */
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPRuleEditor_Constants.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by cacaodev.
|
|
||||||
* Copyright 2011, cacaodev.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
CPRuleEditorPredicateLeftExpression = "CPRuleEditorPredicateLeftExpression";
|
|
||||||
CPRuleEditorPredicateRightExpression = "CPRuleEditorPredicateRightExpression";
|
|
||||||
CPRuleEditorPredicateComparisonModifier = "CPRuleEditorPredicateComparisonModifier";
|
|
||||||
CPRuleEditorPredicateOptions = "CPRuleEditorPredicateOptions";
|
|
||||||
CPRuleEditorPredicateOperatorType = "CPRuleEditorPredicateOperatorType";
|
|
||||||
CPRuleEditorPredicateCustomSelector = "CPRuleEditorPredicateCustomSelector";
|
|
||||||
CPRuleEditorPredicateCompoundType = "CPRuleEditorPredicateCompoundType";
|
|
||||||
|
|
||||||
CPRuleEditorRowsDidChangeNotification = "CPRuleEditorRowsDidChangeNotification";
|
|
||||||
CPRuleEditorRulesDidChangeNotification = "CPRuleEditorRulesDidChangeNotification";
|
|
||||||
|
|
||||||
CPRuleEditorNestingModeSingle = 0; // Only a single row is allowed. Plus/minus buttons will not be shown
|
|
||||||
CPRuleEditorNestingModeList = 1; // Allows a single list, with no nesting and no compound rows
|
|
||||||
CPRuleEditorNestingModeCompound = 2; // Unlimited nesting and compound rows; this is the default
|
|
||||||
CPRuleEditorNestingModeSimple = 3; // One compound row at the top with subrows beneath it, and no further nesting allowed
|
|
||||||
|
|
||||||
CPRuleEditorRowTypeSimple = 0;
|
|
||||||
CPRuleEditorRowTypeCompound = 1;
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
@import <Foundation/CPString.j>
|
|
||||||
|
|
||||||
@implementation _CPPredicateEditorRowNode : CPObject
|
|
||||||
{
|
|
||||||
_CPPredicateEditorTree tree @accessors;
|
|
||||||
CPMutableArray templateViews @accessors;
|
|
||||||
CPMutableArray copiedTemplateContainer @accessors;
|
|
||||||
CPArray children @accessors(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)rowNodeFromTree:(id)aTree
|
|
||||||
{
|
|
||||||
return [_CPPredicateEditorRowNode _rowNodeFromTree:aTree withTemplateTable:{}];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)_rowNodeFromTree:(id)aTree withTemplateTable:(id)templateTable
|
|
||||||
{
|
|
||||||
var views,
|
|
||||||
copiedContainer;
|
|
||||||
|
|
||||||
var uuid = [[aTree template] UID],
|
|
||||||
cachedNode = templateTable[uuid],
|
|
||||||
node = [[_CPPredicateEditorRowNode alloc] init];
|
|
||||||
|
|
||||||
[node setTree:aTree];
|
|
||||||
|
|
||||||
if (!cachedNode)
|
|
||||||
{
|
|
||||||
views = [CPMutableArray array];
|
|
||||||
copiedContainer = [CPMutableArray array];
|
|
||||||
templateTable[uuid] = node;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
views = [cachedNode templateViews];
|
|
||||||
copiedContainer = [cachedNode copiedTemplateContainer];
|
|
||||||
}
|
|
||||||
|
|
||||||
[node setTemplateViews:views];
|
|
||||||
[node setCopiedTemplateContainer:copiedContainer];
|
|
||||||
|
|
||||||
var nodeChildren = [CPMutableArray array],
|
|
||||||
treeChildren = [aTree children],
|
|
||||||
count = [treeChildren count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var treeChild = treeChildren[i],
|
|
||||||
child = [_CPPredicateEditorRowNode _rowNodeFromTree:treeChild withTemplateTable:templateTable];
|
|
||||||
|
|
||||||
[nodeChildren addObject:child];
|
|
||||||
}
|
|
||||||
|
|
||||||
[node setChildren:nodeChildren];
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)applyTemplate:(id)template withViews:(id)views forOriginalTemplate:(id)originalTemplate
|
|
||||||
{
|
|
||||||
var t = [tree template],
|
|
||||||
count = [children count];
|
|
||||||
|
|
||||||
if (t !== template)
|
|
||||||
{
|
|
||||||
[templateViews setArray:views];
|
|
||||||
[copiedTemplateContainer removeAllObjects];
|
|
||||||
[copiedTemplateContainer addObject:template];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
[children[i] applyTemplate:template withViews:views forOriginalTemplate:originalTemplate];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)node
|
|
||||||
{
|
|
||||||
if (![node isKindOfClass:[_CPPredicateEditorRowNode class]])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return (tree === [node tree]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)copyTemplateIfNecessary
|
|
||||||
{
|
|
||||||
if ([copiedTemplateContainer count] === 0)
|
|
||||||
{
|
|
||||||
var copy = [[tree template] copy];
|
|
||||||
[copiedTemplateContainer addObject:copy];
|
|
||||||
[templateViews addObjectsFromArray:[copy templateViews]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)templateView
|
|
||||||
{
|
|
||||||
[self copyTemplateIfNecessary];
|
|
||||||
return [templateViews objectAtIndex:[tree indexIntoTemplate]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)templateForRow
|
|
||||||
{
|
|
||||||
[self copyTemplateIfNecessary];
|
|
||||||
return [copiedTemplateContainer lastObject];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)title
|
|
||||||
{
|
|
||||||
return [tree title];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)displayValue
|
|
||||||
{
|
|
||||||
var title = [self title];
|
|
||||||
|
|
||||||
if (title !== nil)
|
|
||||||
return title;
|
|
||||||
|
|
||||||
return [self templateView];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
return [CPString stringWithFormat:@"<%@ %@ %@ tree:%@ tviews:%@", [self className],[self UID], [self title], [tree UID], [templateViews description]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
@import <Foundation/CPString.j>
|
|
||||||
|
|
||||||
@implementation _CPPredicateEditorTree : CPObject
|
|
||||||
{
|
|
||||||
CPPredicateEditorRowTemplate template @accessors;
|
|
||||||
CPString title @accessors(copy);
|
|
||||||
CPArray children @accessors(copy);
|
|
||||||
CPInteger indexIntoTemplate @accessors;
|
|
||||||
CPInteger menuItemIndex @accessors;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)copy
|
|
||||||
{
|
|
||||||
var tree = [[_CPPredicateEditorTree alloc] init];
|
|
||||||
[tree setTemplate:template];
|
|
||||||
[tree setTitle:title];
|
|
||||||
[tree setMenuItemIndex:menuItemIndex];
|
|
||||||
[tree setIndexIntoTemplate:indexIntoTemplate];
|
|
||||||
[tree setChildren:children];
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
return [CPString stringWithFormat:@"<%@: %p (%@) [%d-%d] T:%p at:%d> [\r%@\r]", [self className], self, title, indexIntoTemplate, menuItemIndex, template, [template rightExpressionAttributeType], children];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
@import <Foundation/CPDictionary.j>
|
|
||||||
@import <Foundation/CPString.j>
|
|
||||||
@import <Foundation/CPURLConnection.j>
|
|
||||||
@import <Foundation/CPURLRequest.j>
|
|
||||||
|
|
||||||
var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)?");
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorLocalizer : CPObject
|
|
||||||
{
|
|
||||||
CPDictionary _dictionary @accessors(property=dictionary);
|
|
||||||
CPURLConnection connection;
|
|
||||||
CPURLRequest request;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)loadContentOfURL:(CPURL)aURL
|
|
||||||
{
|
|
||||||
request = [CPURLRequest requestWithURL:aURL];
|
|
||||||
connection = [CPURLConnection connectionWithRequest:request delegate:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reloadIfNeeded
|
|
||||||
{
|
|
||||||
if (connection !== nil) // Connection waiting
|
|
||||||
{
|
|
||||||
connection = nil;
|
|
||||||
|
|
||||||
var data = [CPURLConnection sendSynchronousRequest:request returningResponse:NULL];
|
|
||||||
[self loadContent:[data rawString]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)rawString
|
|
||||||
{
|
|
||||||
if (connection !== nil && rawString !== nil)
|
|
||||||
[self loadContent:rawString];
|
|
||||||
|
|
||||||
connection = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)loadContent:(CPString)aContent
|
|
||||||
{
|
|
||||||
var dict = @{},
|
|
||||||
lines = [aContent componentsSeparatedByString:"\n"],
|
|
||||||
count = [lines count];
|
|
||||||
|
|
||||||
for (var i = 0 ; i < count ; i++)
|
|
||||||
{
|
|
||||||
var line = [lines objectAtIndex:i];
|
|
||||||
|
|
||||||
if (line.length > 1)
|
|
||||||
{
|
|
||||||
var match = LocalizerStringsRegex.exec(line);
|
|
||||||
|
|
||||||
if (match && match.length >= 3)
|
|
||||||
[dict setObject:match[2] forKey:match[1]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_dictionary = [CPDictionary dictionaryWithDictionary:dict];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)localizedStringForString:(CPString)aString
|
|
||||||
{
|
|
||||||
[self reloadIfNeeded];
|
|
||||||
|
|
||||||
if (_dictionary !== nil && aString !== nil)
|
|
||||||
{
|
|
||||||
var localized = [_dictionary objectForKey:aString];
|
|
||||||
|
|
||||||
if (localized !== nil)
|
|
||||||
return localized;
|
|
||||||
}
|
|
||||||
|
|
||||||
return aString;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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 "CPBezierPath.j"
|
|
||||||
@import "CPPopUpButton.j"
|
|
||||||
|
|
||||||
@class _CPRuleEditorViewSlice
|
|
||||||
|
|
||||||
var GRADIENT_START_COLOR = "#fcfcfc",
|
|
||||||
GRADIENT_END_COLOR = "#dfdfdf",
|
|
||||||
BORDER_COLOR = "#BDBDBD";
|
|
||||||
|
|
||||||
var GRADIENT_NORMAL,
|
|
||||||
GRADIENT_HIGHLIGHTED,
|
|
||||||
GRADIENT_PROPERTY;
|
|
||||||
|
|
||||||
if (CPBrowserIsEngine(CPWebKitBrowserEngine))
|
|
||||||
{
|
|
||||||
GRADIENT_NORMAL = "-webkit-gradient(linear, left top, left bottom, from(" + GRADIENT_START_COLOR + "), to(" + GRADIENT_END_COLOR + "))",
|
|
||||||
GRADIENT_HIGHLIGHTED = "-webkit-gradient(linear, left top, left bottom, from(" + GRADIENT_END_COLOR + "), to(" + GRADIENT_START_COLOR + "))";
|
|
||||||
GRADIENT_PROPERTY = "background";
|
|
||||||
}
|
|
||||||
else if (CPBrowserIsEngine(CPGeckoBrowserEngine))
|
|
||||||
{
|
|
||||||
GRADIENT_NORMAL = "-moz-linear-gradient(top, " + GRADIENT_START_COLOR + ", " + GRADIENT_END_COLOR + ")",
|
|
||||||
GRADIENT_HIGHLIGHTED = "-moz-linear-gradient(top, " + GRADIENT_END_COLOR + ", " + GRADIENT_START_COLOR + ")";
|
|
||||||
GRADIENT_PROPERTY = "background";
|
|
||||||
}
|
|
||||||
else if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
{
|
|
||||||
GRADIENT_NORMAL = "progid:DXImageTransform.Microsoft.gradient(startColorstr='" + GRADIENT_START_COLOR + "', endColorstr='" + GRADIENT_END_COLOR + "')";
|
|
||||||
GRADIENT_HIGHLIGHTED = "progid:DXImageTransform.Microsoft.gradient(startColorstr='" + GRADIENT_END_COLOR + "', endColorstr='" + GRADIENT_START_COLOR + "')";
|
|
||||||
GRADIENT_PROPERTY = "filter";
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
GRADIENT_NORMAL = GRADIENT_START_COLOR;
|
|
||||||
GRADIENT_HIGHLIGHTED = GRADIENT_END_COLOR;
|
|
||||||
GRADIENT_PROPERTY = "background";
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorPopUpButton : CPPopUpButton
|
|
||||||
{
|
|
||||||
CPInteger radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_sharedInit
|
|
||||||
{
|
|
||||||
[self setBordered:NO];
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var style = _DOMElement.style;
|
|
||||||
style.border = "1px solid " + BORDER_COLOR;
|
|
||||||
style[GRADIENT_PROPERTY] = GRADIENT_NORMAL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
[self _sharedInit];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
[self _sharedInit];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)hitTest:(CGPoint)point
|
|
||||||
{
|
|
||||||
if (!CGRectContainsPoint([self frame], point) || ![self sliceIsEditable])
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)sliceIsEditable
|
|
||||||
{
|
|
||||||
var superview = [self superview];
|
|
||||||
return ![superview isKindOfClass:[_CPRuleEditorViewSlice]] || [superview isEditable];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)trackMouse:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
if (![self sliceIsEditable])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return [super trackMouse:theEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGRect)contentRectForBounds:(CGRect)bounds
|
|
||||||
{
|
|
||||||
var contentRect = [super contentRectForBounds:bounds];
|
|
||||||
contentRect.origin.x += radius;
|
|
||||||
contentRect.size.width -= 2 * radius;
|
|
||||||
|
|
||||||
return contentRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
radius = FLOOR(CGRectGetHeight([self bounds]) / 2);
|
|
||||||
var style = _DOMElement.style,
|
|
||||||
radiusCSS = radius + "px";
|
|
||||||
|
|
||||||
style.borderRadius = radiusCSS;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[super layoutSubviews];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
bounds = [self bounds],
|
|
||||||
arrow_width = FLOOR(CGRectGetHeight(bounds) / 3.5);
|
|
||||||
|
|
||||||
CGContextTranslateCTM(context, CGRectGetWidth(bounds) - radius - arrow_width, CGRectGetHeight(bounds) / 2);
|
|
||||||
|
|
||||||
var arrowsPath = [CPBezierPath bezierPath];
|
|
||||||
[arrowsPath moveToPoint:CGPointMake(0, 1)];
|
|
||||||
[arrowsPath lineToPoint:CGPointMake(arrow_width, 1)];
|
|
||||||
[arrowsPath lineToPoint:CGPointMake(arrow_width / 2, arrow_width + 1)];
|
|
||||||
[arrowsPath closePath];
|
|
||||||
|
|
||||||
CGContextSetFillColor(context, [CPColor colorWithWhite:101 / 255 alpha:1]);
|
|
||||||
[arrowsPath fill];
|
|
||||||
|
|
||||||
CGContextScaleCTM(context, 1 , -1);
|
|
||||||
[arrowsPath fill];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorButton : CPButton
|
|
||||||
{
|
|
||||||
CPInteger radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_sharedInit
|
|
||||||
{
|
|
||||||
[self setBordered:NO];
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var style = _DOMElement.style;
|
|
||||||
style.border = "1px solid " + BORDER_COLOR;
|
|
||||||
style[GRADIENT_PROPERTY] = GRADIENT_NORMAL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
[self _sharedInit];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
[self _sharedInit];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
radius = FLOOR(CGRectGetHeight([self bounds]) / 2);
|
|
||||||
|
|
||||||
var style = _DOMElement.style,
|
|
||||||
radiusCSS = radius + "px";
|
|
||||||
|
|
||||||
style.borderRadius = radiusCSS;
|
|
||||||
style[GRADIENT_PROPERTY] = ([self isHighlighted]) ? GRADIENT_HIGHLIGHTED : GRADIENT_NORMAL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[super layoutSubviews];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "CPView.j"
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorViewSlice : CPView
|
|
||||||
{
|
|
||||||
CPRuleEditor _ruleEditor;
|
|
||||||
int _indentation @accessors(property=indentation);
|
|
||||||
int _rowIndex @accessors(property=rowIndex);
|
|
||||||
CGRect _animationTargetRect @accessors(property=_animationTargetRect);
|
|
||||||
BOOL _selected @accessors(getter=_isSelected, setter=_setSelected:);
|
|
||||||
BOOL _lastSelected @accessors(getter=_isLastSelected, setter=_setLastSelected:);
|
|
||||||
CPColor _backgroundColor @accessors(property=backgroundColor);
|
|
||||||
BOOL _editable @accessors(getter=isEditable, setter=setEditable:);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeFromSuperview
|
|
||||||
{
|
|
||||||
[super removeFromSuperview];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frame ruleEditorView:(id)editor
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:frame])
|
|
||||||
{
|
|
||||||
_ruleEditor = editor;
|
|
||||||
_selected = NO;
|
|
||||||
_lastSelected = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setSelected:(BOOL)select
|
|
||||||
{
|
|
||||||
if (select == _selected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var selector = select ? @selector(setThemeState:) : @selector(unsetThemeState:);
|
|
||||||
[[self subviews] makeObjectsPerformSelector:selector withObject:CPThemeStateSelectedDataView];
|
|
||||||
_selected = select;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)rect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
bounds = [self bounds],
|
|
||||||
maxX = CGRectGetWidth(bounds),
|
|
||||||
maxY = CGRectGetHeight(bounds);
|
|
||||||
|
|
||||||
// Draw background
|
|
||||||
if ([self _isSelected])
|
|
||||||
_backgroundColor = [_ruleEditor _selectedRowColor];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var colors = [_ruleEditor _backgroundColors],
|
|
||||||
count = [colors count];
|
|
||||||
_backgroundColor = [colors objectAtIndex:(_rowIndex % count)];
|
|
||||||
}
|
|
||||||
|
|
||||||
CGContextSetFillColor(context, _backgroundColor);
|
|
||||||
CGContextFillRect(context, rect);
|
|
||||||
|
|
||||||
// Draw Top Border
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
CGContextMoveToPoint(context, 0, 0);
|
|
||||||
CGContextAddLineToPoint(context, maxX, 0);
|
|
||||||
CGContextSetStrokeColor(context, [_ruleEditor _sliceTopBorderColor]);
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
|
|
||||||
// Draw Bottom Border
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
CGContextMoveToPoint(context, 0, maxY);
|
|
||||||
CGContextAddLineToPoint(context, maxX, maxY);
|
|
||||||
|
|
||||||
var bottomColor = (_rowIndex == [_ruleEditor _lastRow]) ? [_ruleEditor _sliceLastBottomBorderColor] : [_ruleEditor _sliceBottomBorderColor];
|
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, bottomColor);
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
if (_editable)
|
|
||||||
[_ruleEditor _mouseDownOnSlice:self withEvent:theEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseUp:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
if (_editable)
|
|
||||||
[_ruleEditor _mouseUpOnSlice:self withEvent:theEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
- (void)rightMouseDown:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
[_ruleEditor _rightMouseDownOnSlice:self withEvent:theEvent];
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// =========
|
|
||||||
// ! DEBUG
|
|
||||||
// =========
|
|
||||||
- (CPString)description
|
|
||||||
{
|
|
||||||
return [CPString stringWithFormat:@"<%@ %p index:%d indentation:%d>",[self className],self,[self rowIndex],[self indentation]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,533 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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 "CPRuleEditor_Constants.j"
|
|
||||||
@import "_CPRuleEditorViewSlice.j"
|
|
||||||
@import "_CPRuleEditorPopUpButton.j"
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
var CONTROL_HEIGHT = 16.,
|
|
||||||
BUTTON_HEIGHT = 16.;
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorViewSliceRow : _CPRuleEditorViewSlice
|
|
||||||
{
|
|
||||||
CPMutableArray _ruleOptionViews;
|
|
||||||
CPMutableArray _ruleOptionFrames;
|
|
||||||
CPMutableArray _correspondingRuleItems;
|
|
||||||
CPMutableArray _ruleOptionInitialViewFrames;
|
|
||||||
CPButton _addButton;
|
|
||||||
CPButton _subtractButton;
|
|
||||||
|
|
||||||
CPRuleEditorRowType _rowType @accessors;
|
|
||||||
CPRuleEditorRowType _plusButtonRowType;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frame ruleEditorView:(id)editor
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:frame ruleEditorView:editor])
|
|
||||||
[self _initShared];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_initShared
|
|
||||||
{
|
|
||||||
_correspondingRuleItems = [[CPMutableArray alloc] init];
|
|
||||||
_ruleOptionFrames = [[CPMutableArray alloc] init];
|
|
||||||
_ruleOptionInitialViewFrames = [[CPMutableArray alloc] init];
|
|
||||||
_ruleOptionViews = [[CPMutableArray alloc] init];
|
|
||||||
_editable = [_ruleEditor isEditable];
|
|
||||||
|
|
||||||
_addButton = [self _createAddRowButton];
|
|
||||||
_subtractButton = [self _createDeleteRowButton];
|
|
||||||
[_addButton setToolTip:[_ruleEditor _toolTipForAddSimpleRowButton]];
|
|
||||||
[_subtractButton setToolTip:[_ruleEditor _toolTipForDeleteRowButton]];
|
|
||||||
[_addButton setHidden:!_editable];
|
|
||||||
[_subtractButton setHidden:!_editable];
|
|
||||||
[self addSubview:_addButton];
|
|
||||||
[self addSubview:_subtractButton];
|
|
||||||
|
|
||||||
[self setAutoresizingMask:CPViewWidthSizable];
|
|
||||||
|
|
||||||
var center = [CPNotificationCenter defaultCenter];
|
|
||||||
[center addObserver:self selector:@selector(_textDidChange:) name:CPControlTextDidChangeNotification object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPButton)_createRowButton
|
|
||||||
{
|
|
||||||
var button = [[_CPRuleEditorButton alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[button setFont:[CPFont boldFontWithName:@"Apple Symbol" size:12.0]];
|
|
||||||
[button setTextColor:[CPColor colorWithWhite:150 / 255 alpha:1]];
|
|
||||||
[button setAlignment:CPCenterTextAlignment];
|
|
||||||
[button setAutoresizingMask:CPViewMinXMargin];
|
|
||||||
[button setImagePosition:CPImageOnly];
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPButton)_createAddRowButton
|
|
||||||
{
|
|
||||||
var button = [self _createRowButton];
|
|
||||||
|
|
||||||
[button setImage:[_ruleEditor _addImage]];
|
|
||||||
[button setAction:@selector(_addOption:)];
|
|
||||||
[button setTarget:self];
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPButton)_createDeleteRowButton
|
|
||||||
{
|
|
||||||
var button = [self _createRowButton];
|
|
||||||
|
|
||||||
[button setImage:[_ruleEditor _removeImage]];
|
|
||||||
[button setAction:@selector(_deleteOption:)];
|
|
||||||
[button setTarget:self];
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPMenuItem)_createMenuItemWithTitle:(CPString )title
|
|
||||||
{
|
|
||||||
title = [[_ruleEditor standardLocalizer] localizedStringForString:title];
|
|
||||||
var mItem = [[CPMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""];
|
|
||||||
return mItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPPopUpButton)_createPopUpButtonWithItems:(CPArray)itemsArray selectedItemIndex:(int)index
|
|
||||||
{
|
|
||||||
var title = [[itemsArray objectAtIndex:index] title],
|
|
||||||
font = [_ruleEditor font],
|
|
||||||
width = [title sizeWithFont:font].width + 20,
|
|
||||||
rect = CGRectMake(0, 0, (width - width % 40) + 80, CONTROL_HEIGHT),
|
|
||||||
|
|
||||||
popup = [[_CPRuleEditorPopUpButton alloc] initWithFrame:rect];
|
|
||||||
|
|
||||||
[popup setTextColor:[CPColor colorWithWhite:101 / 255 alpha:1]];
|
|
||||||
[popup setValue:font forThemeAttribute:@"font"];
|
|
||||||
|
|
||||||
var count = [itemsArray count];
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
[popup addItem:[itemsArray objectAtIndex:i]];
|
|
||||||
|
|
||||||
[popup selectItemAtIndex:index];
|
|
||||||
|
|
||||||
return popup;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPMenuItem)_createMenuSeparatorItem
|
|
||||||
{
|
|
||||||
return [CPMenuItem separatorItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (_CPRuleEditorTextField)_createStaticTextFieldWithStringValue:(CPString)text
|
|
||||||
{
|
|
||||||
var textField = [[_CPRuleEditorTextField alloc] initWithFrame:CGRectMakeZero()],
|
|
||||||
ruleEditorFont = [_ruleEditor font],
|
|
||||||
font = [CPFont fontWithName:[ruleEditorFont familyName] size:[ruleEditorFont size] + 2],
|
|
||||||
localizedText = [[_ruleEditor standardLocalizer] localizedStringForString:text],
|
|
||||||
size = [localizedText sizeWithFont:font];
|
|
||||||
|
|
||||||
[textField setFrameSize:CGSizeMake(size.width + 4, CONTROL_HEIGHT + 2)];
|
|
||||||
|
|
||||||
[textField setValue:font forThemeAttribute:@"font"];
|
|
||||||
[textField setStringValue:localizedText];
|
|
||||||
|
|
||||||
return textField;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_addOption:(id)sender
|
|
||||||
{
|
|
||||||
if (_rowIndex == [_ruleEditor numberOfRows] - 1)
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
|
|
||||||
var type = _plusButtonRowType;
|
|
||||||
if ([_ruleEditor nestingMode] == CPRuleEditorNestingModeCompound && ([[CPApp currentEvent] modifierFlags] & CPAlternateKeyMask))
|
|
||||||
type = CPRuleEditorRowTypeCompound;
|
|
||||||
|
|
||||||
[_ruleEditor _addOptionFromSlice:self ofRowType:type];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_deleteOption:(id)sender
|
|
||||||
{
|
|
||||||
[_ruleEditor _deleteSlice:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_ruleOptionPopupChangedAction:(CPMenuItem )sender
|
|
||||||
{
|
|
||||||
var layoutdict = [sender representedObject],
|
|
||||||
newItem = [layoutdict objectForKey:@"item"],
|
|
||||||
indexInCriteria = [layoutdict objectForKey:@"indexInCriteria"],
|
|
||||||
oldItem = [_correspondingRuleItems objectAtIndex:indexInCriteria];
|
|
||||||
|
|
||||||
if (![newItem isEqual:oldItem])
|
|
||||||
{
|
|
||||||
[_correspondingRuleItems replaceObjectAtIndex:indexInCriteria withObject:newItem];
|
|
||||||
[_ruleEditor _changedItem:oldItem toItem:newItem inRow:_rowIndex atCriteriaIndex:indexInCriteria];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)validateMenuItem:(CPMenuItem )menuItem
|
|
||||||
{
|
|
||||||
return [_ruleEditor _validateItem:menuItem value:[[menuItem representedObject] valueForKey:@"item"] inRow:_rowIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_emptyRulePartSubviews
|
|
||||||
{
|
|
||||||
var count = [_ruleOptionViews count];
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
[_ruleOptionViews[count] removeFromSuperview];
|
|
||||||
|
|
||||||
[_ruleOptionViews removeAllObjects];
|
|
||||||
[_ruleOptionFrames removeAllObjects];
|
|
||||||
[_ruleOptionInitialViewFrames removeAllObjects];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_reconfigureSubviews
|
|
||||||
{
|
|
||||||
var ruleItems,
|
|
||||||
criteria,
|
|
||||||
repObject,
|
|
||||||
menuItem,
|
|
||||||
ruleView,
|
|
||||||
criterion,
|
|
||||||
parent,
|
|
||||||
numberOfCriteria,
|
|
||||||
numberOfChildren,
|
|
||||||
firstResponderIndex,
|
|
||||||
|
|
||||||
ruleItems = [CPMutableArray array],
|
|
||||||
responder = [[self window] firstResponder];
|
|
||||||
|
|
||||||
[self _emptyRulePartSubviews];
|
|
||||||
|
|
||||||
criteria = [_ruleEditor criteriaForRow:_rowIndex];
|
|
||||||
numberOfCriteria = [criteria count];
|
|
||||||
|
|
||||||
firstResponderIndex = numberOfCriteria - 1;
|
|
||||||
if (responder)
|
|
||||||
firstResponderIndex = [_ruleOptionViews indexOfObjectIdenticalTo:responder];
|
|
||||||
|
|
||||||
for (var i = 0; i < numberOfCriteria; i++)
|
|
||||||
{
|
|
||||||
ruleView = nil;
|
|
||||||
parent = nil;
|
|
||||||
criterion = [criteria objectAtIndex:i];
|
|
||||||
|
|
||||||
if (i > 0)
|
|
||||||
parent = [criteria objectAtIndex:i - 1];
|
|
||||||
|
|
||||||
var childItems = [],
|
|
||||||
childValues = [];
|
|
||||||
|
|
||||||
[_ruleEditor _getAllAvailableItems:childItems values:childValues asChildrenOfItem:parent inRow:_rowIndex];
|
|
||||||
|
|
||||||
numberOfChildren = [childItems count];
|
|
||||||
if (numberOfChildren > 1)
|
|
||||||
{
|
|
||||||
var menuItems = [CPMutableArray arrayWithCapacity:numberOfChildren],
|
|
||||||
selectedIndex = [childItems indexOfObject:criterion];
|
|
||||||
|
|
||||||
if (selectedIndex == CPNotFound)
|
|
||||||
break;
|
|
||||||
|
|
||||||
for (var j = 0; j < numberOfChildren; ++j)
|
|
||||||
{
|
|
||||||
var childItem = [childItems objectAtIndex:j],
|
|
||||||
childValue = [childValues objectAtIndex:j];
|
|
||||||
|
|
||||||
if ([childValue isKindOfClass:[CPMenuItem class]])
|
|
||||||
{
|
|
||||||
[[childValue menu] removeItem:childValue];
|
|
||||||
menuItem = childValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ([childValue isEqualToString:@""])
|
|
||||||
menuItem = [self _createMenuSeparatorItem];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
menuItem = [self _createMenuItemWithTitle:childValue];
|
|
||||||
[menuItem setTarget:self];
|
|
||||||
[menuItem setAction:@selector(_ruleOptionPopupChangedAction:)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repObject = @{
|
|
||||||
@"item": childItem,
|
|
||||||
@"value": childValue,
|
|
||||||
@"indexInCriteria": i
|
|
||||||
};
|
|
||||||
[menuItem setRepresentedObject:repObject];
|
|
||||||
[menuItems addObject:menuItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleView = [self _createPopUpButtonWithItems:menuItems selectedItemIndex:selectedIndex];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var value = [childValues objectAtIndex:0],
|
|
||||||
type = [value valueType];
|
|
||||||
|
|
||||||
if (type === 0)
|
|
||||||
ruleView = [self _createStaticTextFieldWithStringValue:value];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (type !== 1)
|
|
||||||
{
|
|
||||||
[CPException raise:CPInternalInconsistencyException reason:@"Display value must be a string or a menu item"];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleView = value;
|
|
||||||
[ruleView setTarget:self];
|
|
||||||
[ruleView setAction:@selector(_sendRuleAction:)];
|
|
||||||
if ([ruleView respondsToSelector:@selector(setDelegate:)])
|
|
||||||
[ruleView setDelegate:self];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ruleView != nil)
|
|
||||||
{
|
|
||||||
[_ruleOptionViews addObject:ruleView];
|
|
||||||
var frame = [ruleView frame];
|
|
||||||
[_ruleOptionInitialViewFrames addObject:frame];
|
|
||||||
[_ruleOptionFrames addObject:frame];
|
|
||||||
|
|
||||||
if (!criterion)
|
|
||||||
criterion = [CPNull null];
|
|
||||||
|
|
||||||
[ruleItems addObject:criterion];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[_correspondingRuleItems setArray:ruleItems];
|
|
||||||
|
|
||||||
if (!_editable)
|
|
||||||
[self _updateEnabledStateForSubviews];
|
|
||||||
|
|
||||||
[self _relayoutSubviewsWidthChanged:YES];
|
|
||||||
|
|
||||||
if (firstResponderIndex != CPNotFound)
|
|
||||||
{
|
|
||||||
var aView = [_ruleOptionViews objectAtIndex:firstResponderIndex];
|
|
||||||
[[self window] makeFirstResponder:aView]; // This is not working. bug in CPPopUpButton firstResponder ?
|
|
||||||
}
|
|
||||||
|
|
||||||
//[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateEnabledStateForSubviews
|
|
||||||
{
|
|
||||||
[_ruleOptionViews makeObjectsPerformSelector:@selector(setEnabled:) withObject:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
[self _relayoutSubviewsWidthChanged:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_relayoutSubviewsWidthChanged:(BOOL)widthChanged
|
|
||||||
{
|
|
||||||
var optionViewOriginX,
|
|
||||||
leftHorizontalPadding,
|
|
||||||
leftButtonMinX,
|
|
||||||
rowHeight = [_ruleEditor rowHeight],
|
|
||||||
count = [_ruleOptionViews count],
|
|
||||||
sliceFrame = [self frame],
|
|
||||||
|
|
||||||
buttonFrame = CGRectMake(CGRectGetWidth(sliceFrame) - BUTTON_HEIGHT - [self _rowButtonsRightHorizontalPadding], ([_ruleEditor rowHeight] - BUTTON_HEIGHT) / 2 - 2, BUTTON_HEIGHT, BUTTON_HEIGHT);
|
|
||||||
|
|
||||||
[_addButton setFrame:buttonFrame];
|
|
||||||
buttonFrame.origin.x -= BUTTON_HEIGHT + [self _rowButtonsInterviewHorizontalPadding];
|
|
||||||
[_subtractButton setFrame:buttonFrame];
|
|
||||||
|
|
||||||
if (widthChanged)
|
|
||||||
{
|
|
||||||
optionViewOriginX = [self _leftmostViewFixedHorizontalPadding] + [self _indentationHorizontalPadding] * _indentation;
|
|
||||||
leftHorizontalPadding = [self _rowButtonsLeftHorizontalPadding];
|
|
||||||
leftButtonMinX = CGRectGetMinX(buttonFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var ruleOptionView = _ruleOptionViews[i],
|
|
||||||
optionFrame = _ruleOptionFrames[i];
|
|
||||||
|
|
||||||
optionFrame.origin.y = (rowHeight - CGRectGetHeight(optionFrame)) / 2 - 2;
|
|
||||||
|
|
||||||
if (widthChanged)
|
|
||||||
{
|
|
||||||
optionFrame.origin.x = optionViewOriginX;
|
|
||||||
if (i == count - 1 && ![self _isRulePopup:ruleOptionView])
|
|
||||||
{
|
|
||||||
var initialFrame = _ruleOptionInitialViewFrames[i];
|
|
||||||
optionFrame.size.width = MIN(CGRectGetWidth(initialFrame), leftButtonMinX - leftHorizontalPadding - optionViewOriginX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ruleOptionView setFrame:optionFrame];
|
|
||||||
[self addSubview:ruleOptionView];
|
|
||||||
|
|
||||||
if (widthChanged)
|
|
||||||
optionViewOriginX += CGRectGetWidth(optionFrame) + [self _interviewHorizontalPadding];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updateButtonVisibilities
|
|
||||||
{
|
|
||||||
[_addButton setHidden:[_ruleEditor _shouldHideAddButtonForSlice:self]];
|
|
||||||
[_subtractButton setHidden:[_ruleEditor _shouldHideSubtractButtonForSlice:self]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_configurePlusButtonByRowType:(CPRuleEditorRowType)type
|
|
||||||
{
|
|
||||||
[self _setRowTypeToAddFromPlusButton:type];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setEditable:(BOOL)value
|
|
||||||
{
|
|
||||||
[super setEditable:value]
|
|
||||||
// [self _updateEnabledStateForSubviews];
|
|
||||||
[self _updateButtonVisibilities];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)_alignmentGridWidth
|
|
||||||
{
|
|
||||||
return [_ruleEditor _alignmentGridWidth];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)_indentationHorizontalPadding
|
|
||||||
{
|
|
||||||
return 30.;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)_interviewHorizontalPadding
|
|
||||||
{
|
|
||||||
return 6.;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)_leftmostViewFixedHorizontalPadding
|
|
||||||
{
|
|
||||||
return 7.;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)_minimumVerticalPopupPadding
|
|
||||||
{
|
|
||||||
return 2.;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)_rowButtonsInterviewHorizontalPadding
|
|
||||||
{
|
|
||||||
return 6.;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)_rowButtonsLeftHorizontalPadding
|
|
||||||
{
|
|
||||||
return 10.;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (float)_rowButtonsRightHorizontalPadding
|
|
||||||
{
|
|
||||||
return ([[_ruleEditor superview] isKindOfClass:[CPClipView class]]) ? 18. : 10.;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setRowTypeToAddFromPlusButton:(int)type
|
|
||||||
{
|
|
||||||
_plusButtonRowType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setNeedsDisplay:(BOOL)flag
|
|
||||||
{
|
|
||||||
[super setNeedsDisplay:flag];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_nestingModeShouldHideAddButton
|
|
||||||
{
|
|
||||||
return [_ruleEditor _applicableNestingMode] == CPRuleEditorNestingModeSingle;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_nestingModeShouldHideSubtractButton
|
|
||||||
{
|
|
||||||
return [_ruleEditor _applicableNestingMode] == CPRuleEditorNestingModeSingle;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)containsDisplayValue:(id)value
|
|
||||||
{
|
|
||||||
return [[_ruleEditor displayValuesForRow:_rowIndex] containsObject:value];
|
|
||||||
// Ou alors avec _correspondingRuleItems
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidMoveToWindow
|
|
||||||
{
|
|
||||||
[self layoutSubviews];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)rect
|
|
||||||
{
|
|
||||||
[super drawRect:rect];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_isRulePopup:(CPView)view
|
|
||||||
{
|
|
||||||
if ([view isKindOfClass:[_CPRuleEditorPopUpButton class]])
|
|
||||||
return YES;
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_sendRuleAction:(id)sender
|
|
||||||
{
|
|
||||||
[_ruleEditor _updatePredicate];
|
|
||||||
[_ruleEditor _sendRuleAction];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_textDidChange:(CPNotification)aNotif
|
|
||||||
{
|
|
||||||
if ([[aNotif object] superview] == self && [_ruleEditor _sendsActionOnIncompleteTextChange])
|
|
||||||
[self _sendRuleAction:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorTextField : CPTextField
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:frame];
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
[self setDrawsBackground:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)hitTest:(CGPoint)point
|
|
||||||
{
|
|
||||||
if (!CGRectContainsPoint([self frame], point))
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return [self superview];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,24 +1,3 @@
|
|||||||
/*
|
|
||||||
* CPSavePanel.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 "CPPanel.j"
|
@import "CPPanel.j"
|
||||||
|
|
||||||
|
|||||||
+3
-37
@@ -1,51 +1,17 @@
|
|||||||
/*
|
|
||||||
* CPScreen.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Francisco Tolmasky.
|
|
||||||
* Copyright 2009, 280 North, Inc.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
|
||||||
/*!
|
|
||||||
@ingroup appkit
|
|
||||||
@class CPScreen
|
|
||||||
|
|
||||||
A CPScreen object describes the attributes of a display device available
|
|
||||||
to Cappuccino.
|
|
||||||
*/
|
|
||||||
@implementation CPScreen : CPObject
|
@implementation CPScreen : CPObject
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the position and size of the visible area of the receiving screen.
|
|
||||||
This will normally be smaller than the full size of the screen to account
|
|
||||||
for system UI elements. For example, on a Mac the top of the visible frame
|
|
||||||
is placed below the bottom of the menu bar.
|
|
||||||
|
|
||||||
@return the visible screen rectangle
|
|
||||||
*/
|
|
||||||
- (CGRect)visibleFrame
|
- (CGRect)visibleFrame
|
||||||
{
|
{
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
return CGRectMake(window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight);
|
return _CGRectMake(window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight);
|
||||||
#else
|
#else
|
||||||
return CGRectMakeZero();
|
return _CGRectMakeZero();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+615
-1085
File diff suppressed because it is too large
Load Diff
+128
-352
@@ -5,9 +5,6 @@
|
|||||||
* Created by Francisco Tolmasky.
|
* Created by Francisco Tolmasky.
|
||||||
* Copyright 2008, 280 North, Inc.
|
* Copyright 2008, 280 North, Inc.
|
||||||
*
|
*
|
||||||
* Modified to match Lion style by Antoine Mercadal 2011
|
|
||||||
* <antoine.mercadal@archipelproject.org>
|
|
||||||
*
|
|
||||||
* 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
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
@@ -23,14 +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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../Foundation/Foundation.h"
|
|
||||||
|
|
||||||
@import "CPAnimation.j"
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPWindow_Constants.j"
|
|
||||||
@import "CPViewAnimation.j"
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
// CPScroller Constants
|
// CPScroller Constants
|
||||||
CPScrollerNoPart = 0;
|
CPScrollerNoPart = 0;
|
||||||
@@ -57,25 +48,12 @@ var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScroll
|
|||||||
NAMES_FOR_PARTS = {},
|
NAMES_FOR_PARTS = {},
|
||||||
PARTS_FOR_NAMES = {};
|
PARTS_FOR_NAMES = {};
|
||||||
|
|
||||||
var _CACHED_THEME_SCROLLER = nil; // This is used by the class methods to pull the theme attributes.
|
|
||||||
|
|
||||||
NAMES_FOR_PARTS[CPScrollerDecrementLine] = @"decrement-line";
|
NAMES_FOR_PARTS[CPScrollerDecrementLine] = @"decrement-line";
|
||||||
NAMES_FOR_PARTS[CPScrollerIncrementLine] = @"increment-line";
|
NAMES_FOR_PARTS[CPScrollerIncrementLine] = @"increment-line";
|
||||||
NAMES_FOR_PARTS[CPScrollerKnobSlot] = @"knob-slot";
|
NAMES_FOR_PARTS[CPScrollerKnobSlot] = @"knob-slot";
|
||||||
NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||||
|
|
||||||
|
|
||||||
CPScrollerStyleLegacy = 0;
|
|
||||||
CPScrollerStyleOverlay = 1;
|
|
||||||
|
|
||||||
CPScrollerKnobStyleDefault = 0;
|
|
||||||
CPScrollerKnobStyleDark = 1;
|
|
||||||
CPScrollerKnobStyleLight = 2;
|
|
||||||
|
|
||||||
CPThemeStateScrollViewLegacy = CPThemeState("scroller-style-legacy");
|
|
||||||
CPThemeStateScrollerKnobLight = CPThemeState("scroller-knob-light");
|
|
||||||
CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|
||||||
|
|
||||||
@implementation CPScroller : CPControl
|
@implementation CPScroller : CPControl
|
||||||
{
|
{
|
||||||
CPControlSize _controlSize;
|
CPControlSize _controlSize;
|
||||||
@@ -90,19 +68,8 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
CPScrollerPart _trackingPart;
|
CPScrollerPart _trackingPart;
|
||||||
float _trackingFloatValue;
|
float _trackingFloatValue;
|
||||||
CGPoint _trackingStartPoint;
|
CGPoint _trackingStartPoint;
|
||||||
|
|
||||||
CPViewAnimation _animationScroller;
|
|
||||||
|
|
||||||
BOOL _allowFadingOut @accessors(getter=allowFadingOut);
|
|
||||||
int _style;
|
|
||||||
CPTimer _timerFadeOut;
|
|
||||||
BOOL _isMouseOver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Class methods
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)defaultThemeClass
|
||||||
{
|
{
|
||||||
return "scroller";
|
return "scroller";
|
||||||
@@ -110,49 +77,50 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return @{
|
return [CPDictionary dictionaryWithJSObject:{
|
||||||
@"scroller-width": 7.0,
|
@"scroller-width": 15.0,
|
||||||
@"knob-slot-color": [CPNull null],
|
@"knob-slot-color": [CPColor lightGrayColor],
|
||||||
@"decrement-line-color": [CPNull null],
|
@"decrement-line-color": [CPNull null],
|
||||||
@"increment-line-color": [CPNull null],
|
@"increment-line-color": [CPNull null],
|
||||||
@"knob-color": [CPNull null],
|
@"knob-color": [CPColor grayColor],
|
||||||
@"decrement-line-size": CGSizeMakeZero(),
|
@"decrement-line-size":_CGSizeMakeZero(),
|
||||||
@"increment-line-size": CGSizeMakeZero(),
|
@"increment-line-size":_CGSizeMakeZero(),
|
||||||
@"track-inset": CGInsetMakeZero(),
|
@"track-inset":_CGInsetMakeZero(),
|
||||||
@"knob-inset": CGInsetMakeZero(),
|
@"knob-inset": _CGInsetMakeZero(),
|
||||||
@"minimum-knob-length": 21.0,
|
@"minimum-knob-length":21.0
|
||||||
@"track-border-overlay": 9.0
|
}]
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (float)scrollerWidth
|
|
||||||
|
// Calculating Layout
|
||||||
|
|
||||||
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
return [self scrollerWidthInStyle:CPScrollerStyleLegacy];
|
self = [super initWithFrame:aFrame];
|
||||||
|
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
_controlSize = CPRegularControlSize;
|
||||||
|
_partRects = [];
|
||||||
|
|
||||||
|
[self setFloatValue:0.0];
|
||||||
|
[self setKnobProportion:1.0];
|
||||||
|
|
||||||
|
_hitPart = CPScrollerNoPart;
|
||||||
|
|
||||||
|
[self _calculateIsVertical];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determining CPScroller Size
|
||||||
/*!
|
/*!
|
||||||
Returns the CPScroller's width for a CPRegularControlSize.
|
Returns the CPScroller's width for a CPRegularControlSize.
|
||||||
*/
|
*/
|
||||||
+ (float)scrollerWidthInStyle:(int)aStyle
|
+ (float)scrollerWidth
|
||||||
{
|
{
|
||||||
if (!_CACHED_THEME_SCROLLER)
|
return [[[CPScroller alloc] init] currentValueForThemeAttribute:@"scroller-width"];
|
||||||
_CACHED_THEME_SCROLLER = [[self alloc] init];
|
|
||||||
|
|
||||||
if (aStyle == CPScrollerStyleLegacy)
|
|
||||||
return [_CACHED_THEME_SCROLLER valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
|
|
||||||
|
|
||||||
return [_CACHED_THEME_SCROLLER currentValueForThemeAttribute:@"scroller-width"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the CPScroller's overlay value.
|
|
||||||
*/
|
|
||||||
+ (float)scrollerOverlay
|
|
||||||
{
|
|
||||||
if (!_CACHED_THEME_SCROLLER)
|
|
||||||
_CACHED_THEME_SCROLLER = [[self alloc] init];
|
|
||||||
|
|
||||||
return [_CACHED_THEME_SCROLLER currentValueForThemeAttribute:@"track-border-overlay"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -164,92 +132,6 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
return [self scrollerWidth];
|
return [self scrollerWidth];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Initialization
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
{
|
|
||||||
_controlSize = CPRegularControlSize;
|
|
||||||
_partRects = [];
|
|
||||||
|
|
||||||
[self setFloatValue:0.0];
|
|
||||||
[self setKnobProportion:1.0];
|
|
||||||
|
|
||||||
_hitPart = CPScrollerNoPart;
|
|
||||||
_allowFadingOut = YES;
|
|
||||||
_isMouseOver = NO;
|
|
||||||
_style = CPScrollerStyleOverlay;
|
|
||||||
var paramAnimFadeOut = @{
|
|
||||||
CPViewAnimationTargetKey: self,
|
|
||||||
CPViewAnimationEffectKey: CPViewAnimationFadeOutEffect,
|
|
||||||
};
|
|
||||||
|
|
||||||
_animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
|
|
||||||
[_animationScroller setViewAnimations:[paramAnimFadeOut]];
|
|
||||||
[_animationScroller setDelegate:self];
|
|
||||||
[self setAlphaValue:0.0];
|
|
||||||
|
|
||||||
// We have to choose an orientation. If for some bizarre reason width === height,
|
|
||||||
// punt and choose vertical.
|
|
||||||
[self _setIsVertical:CGRectGetHeight(aFrame) >= CGRectGetWidth(aFrame)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Getters / Setters
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the scroller's style
|
|
||||||
*/
|
|
||||||
- (void)style
|
|
||||||
{
|
|
||||||
return _style;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the scroller's control size
|
|
||||||
@param aStyle the scroller style: CPScrollerStyleLegacy or CPScrollerStyleOverlay
|
|
||||||
*/
|
|
||||||
- (void)setStyle:(id)aStyle
|
|
||||||
{
|
|
||||||
if (_style != nil && _style === aStyle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_style = aStyle;
|
|
||||||
|
|
||||||
if (_style === CPScrollerStyleLegacy)
|
|
||||||
{
|
|
||||||
[self fadeIn];
|
|
||||||
[self setThemeState:CPThemeStateScrollViewLegacy];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_allowFadingOut = YES;
|
|
||||||
[self unsetThemeState:CPThemeStateScrollViewLegacy];
|
|
||||||
}
|
|
||||||
|
|
||||||
//[self _adjustScrollerSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setObjectValue:(id)aValue
|
|
||||||
{
|
|
||||||
[super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the scroller's control size
|
|
||||||
*/
|
|
||||||
- (CPControlSize)controlSize
|
|
||||||
{
|
|
||||||
return _controlSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the scroller's size.
|
Sets the scroller's size.
|
||||||
@param aControlSize the scroller's size
|
@param aControlSize the scroller's size
|
||||||
@@ -265,6 +147,27 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the scroller's control size
|
||||||
|
*/
|
||||||
|
- (CPControlSize)controlSize
|
||||||
|
{
|
||||||
|
return _controlSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setObjectValue:(id)aValue
|
||||||
|
{
|
||||||
|
[super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setKnobProportion:(float)aProportion
|
||||||
|
{
|
||||||
|
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
||||||
|
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
|
[self setNeedsLayout];
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Return's the knob's proportion
|
Return's the knob's proportion
|
||||||
*/
|
*/
|
||||||
@@ -273,55 +176,22 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
return _knobProportion;
|
return _knobProportion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (id)currentValueForThemeAttribute:(CPString)anAttributeName
|
||||||
Set the knob's proportion
|
|
||||||
@param aProportion the desired proportion
|
|
||||||
*/
|
|
||||||
- (void)setKnobProportion:(float)aProportion
|
|
||||||
{
|
{
|
||||||
if (!_IS_NUMERIC(aProportion))
|
var themeState = _themeState;
|
||||||
[CPException raise:CPInvalidArgumentException reason:"aProportion must be numeric"];
|
|
||||||
|
|
||||||
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
|
||||||
|
themeState &= ~CPThemeStateHighlighted;
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
return [self valueForThemeAttribute:anAttributeName inState:themeState];
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculating Layout
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Privates
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
- (void)_adjustScrollerSize
|
|
||||||
{
|
|
||||||
var frame = [self frame],
|
|
||||||
scrollerWidth = [self currentValueForThemeAttribute:@"scroller-width"];
|
|
||||||
|
|
||||||
if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
|
|
||||||
frame.size.width = scrollerWidth;
|
|
||||||
|
|
||||||
if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
|
|
||||||
frame.size.height = scrollerWidth;
|
|
||||||
|
|
||||||
[self setFrame:frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
- (void)_performFadeOut:(CPTimer)aTimer
|
|
||||||
{
|
|
||||||
[self fadeOut];
|
|
||||||
_timerFadeOut = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Utilities
|
|
||||||
|
|
||||||
- (CGRect)rectForPart:(CPScrollerPart)aPart
|
- (CGRect)rectForPart:(CPScrollerPart)aPart
|
||||||
{
|
{
|
||||||
if (aPart == CPScrollerNoPart)
|
if (aPart == CPScrollerNoPart)
|
||||||
return CGRectMakeZero();
|
return _CGRectMakeZero();
|
||||||
|
|
||||||
return _partRects[aPart];
|
return _partRects[aPart];
|
||||||
}
|
}
|
||||||
@@ -338,9 +208,6 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
// 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 (![self hasThemeState:CPThemeStateSelected])
|
|
||||||
return CPScrollerNoPart;
|
|
||||||
|
|
||||||
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
|
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
|
||||||
return CPScrollerKnob;
|
return CPScrollerKnob;
|
||||||
|
|
||||||
@@ -391,8 +258,8 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
|
|
||||||
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])
|
||||||
{
|
{
|
||||||
@@ -400,27 +267,27 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
|
incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
|
||||||
effectiveDecrementLineHeight = decrementLineSize.height + trackInset.top,
|
effectiveDecrementLineHeight = decrementLineSize.height + trackInset.top,
|
||||||
effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
|
effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
|
||||||
slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
|
slotHeight = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
|
||||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||||
knobWidth = width - knobInset.left - knobInset.right,
|
knobWidth = width - knobInset.left - knobInset.right,
|
||||||
knobHeight = MAX(minimumKnobLength, (slotSize * _knobProportion)),
|
knobHeight = MAX(minimumKnobLength, (slotHeight * _knobProportion)),
|
||||||
knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight) * [self floatValue];
|
knobLocation = effectiveDecrementLineHeight + (slotHeight - knobHeight) * [self floatValue];
|
||||||
|
|
||||||
_partRects[CPScrollerDecrementPage] = CGRectMake(0.0, effectiveDecrementLineHeight, width, knobLocation - effectiveDecrementLineHeight);
|
_partRects[CPScrollerDecrementPage] = _CGRectMake(0.0, effectiveDecrementLineHeight, width, knobLocation - effectiveDecrementLineHeight);
|
||||||
_partRects[CPScrollerKnob] = CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
|
_partRects[CPScrollerKnob] = _CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
|
||||||
_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, slotSize);
|
_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)
|
if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
|
||||||
_partRects[CPScrollerKnob] = CGRectMakeZero();
|
_partRects[CPScrollerKnob] = _CGRectMakeZero();
|
||||||
|
|
||||||
if (height < decrementLineSize.height + incrementLineSize.height - 2)
|
if (height < decrementLineSize.height + incrementLineSize.height - 2)
|
||||||
{
|
{
|
||||||
_partRects[CPScrollerIncrementLine] = CGRectMakeZero();
|
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
|
||||||
_partRects[CPScrollerDecrementLine] = CGRectMakeZero();
|
_partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
|
||||||
_partRects[CPScrollerKnobSlot] = CGRectMake(trackInset.left, 0, width - trackInset.left - trackInset.right, height);
|
_partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, 0, width - trackInset.left - trackInset.right, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -429,27 +296,27 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
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,
|
||||||
slotSize = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
|
slotWidth = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
|
||||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||||
knobWidth = MAX(minimumKnobLength, (slotSize * _knobProportion)),
|
knobWidth = MAX(minimumKnobLength, (slotWidth * _knobProportion)),
|
||||||
knobHeight = height - knobInset.top - knobInset.bottom,
|
knobHeight = height - knobInset.top - knobInset.bottom,
|
||||||
knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth) * [self floatValue];
|
knobLocation = effectiveDecrementLineWidth + (slotWidth - knobWidth) * [self floatValue];
|
||||||
|
|
||||||
_partRects[CPScrollerDecrementPage] = CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
|
_partRects[CPScrollerDecrementPage] = _CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
|
||||||
_partRects[CPScrollerKnob] = CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
|
_partRects[CPScrollerKnob] = _CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
|
||||||
_partRects[CPScrollerIncrementPage] = CGRectMake(knobLocation + knobWidth, 0.0, width - (knobLocation + knobWidth) - effectiveIncrementLineWidth, height);
|
_partRects[CPScrollerIncrementPage] = _CGRectMake(knobLocation + knobWidth, 0.0, width - (knobLocation + knobWidth) - effectiveIncrementLineWidth, height);
|
||||||
_partRects[CPScrollerKnobSlot] = CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotSize, 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)
|
if (width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
|
||||||
_partRects[CPScrollerKnob] = CGRectMakeZero();
|
_partRects[CPScrollerKnob] = _CGRectMakeZero();
|
||||||
|
|
||||||
if (width < decrementLineSize.width + incrementLineSize.width - 2)
|
if (width < decrementLineSize.width + incrementLineSize.width - 2)
|
||||||
{
|
{
|
||||||
_partRects[CPScrollerIncrementLine] = CGRectMakeZero();
|
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
|
||||||
_partRects[CPScrollerDecrementLine] = CGRectMakeZero();
|
_partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
|
||||||
_partRects[CPScrollerKnobSlot] = CGRectMake(0.0, 0.0, width, slotSize);
|
_partRects[CPScrollerKnobSlot] = _CGRectMake(0.0, 0.0, width, slotHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -463,35 +330,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
return _usableParts;
|
return _usableParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
// Drawing the Parts
|
||||||
Display the scroller
|
|
||||||
*/
|
|
||||||
- (void)fadeIn
|
|
||||||
{
|
|
||||||
if (_isMouseOver && _knobProportion != 1.0)
|
|
||||||
[self setThemeState:CPThemeStateSelected];
|
|
||||||
|
|
||||||
if (_timerFadeOut)
|
|
||||||
[_timerFadeOut invalidate];
|
|
||||||
|
|
||||||
[self setAlphaValue:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Start the fade out anination
|
|
||||||
*/
|
|
||||||
- (void)fadeOut
|
|
||||||
{
|
|
||||||
if ([self hasThemeState:CPThemeStateScrollViewLegacy])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[_animationScroller startAnimation];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Drawing
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Draws the specified arrow and sets the highlight.
|
Draws the specified arrow and sets the highlight.
|
||||||
@param anArrow the arrow to draw
|
@param anArrow the arrow to draw
|
||||||
@@ -517,7 +356,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
|
|
||||||
- (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];
|
||||||
|
|
||||||
@@ -531,7 +370,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
|
|
||||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||||
{
|
{
|
||||||
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
[view setHitTests:NO];
|
[view setHitTests:NO];
|
||||||
|
|
||||||
@@ -540,12 +379,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
|
|
||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
{
|
{
|
||||||
[self _adjustScrollerSize];
|
|
||||||
[self checkSpaceForParts];
|
[self checkSpaceForParts];
|
||||||
|
|
||||||
var index = 0,
|
var index = 0,
|
||||||
count = PARTS_ARRANGEMENT.length,
|
count = PARTS_ARRANGEMENT.length;
|
||||||
view;
|
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (; index < count; ++index)
|
||||||
{
|
{
|
||||||
@@ -606,7 +443,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
{
|
{
|
||||||
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];
|
||||||
@@ -621,8 +458,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
|
|
||||||
[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];
|
||||||
|
|
||||||
if (type === CPLeftMouseDragged)
|
[self sendAction:[self action] to:[self target]];
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -651,20 +487,20 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
|
|
||||||
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],
|
||||||
knobWidth = ![self isVertical] ? CGRectGetWidth(knobRect) : CGRectGetHeight(knobRect),
|
knobWidth = ![self isVertical] ? _CGRectGetWidth(knobRect) : _CGRectGetHeight(knobRect),
|
||||||
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
|
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
|
||||||
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;
|
||||||
|
|
||||||
@@ -685,11 +521,11 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
{
|
{
|
||||||
_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;
|
||||||
@@ -705,13 +541,18 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_setIsVertical:(BOOL)isVertical
|
- (void)_calculateIsVertical
|
||||||
{
|
{
|
||||||
_isVertical = isVertical;
|
// Recalculate isVertical.
|
||||||
|
var bounds = [self bounds],
|
||||||
|
width = _CGRectGetWidth(bounds),
|
||||||
|
height = _CGRectGetHeight(bounds);
|
||||||
|
|
||||||
if (_isVertical)
|
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
||||||
|
|
||||||
|
if (_isVertical === 1)
|
||||||
[self setThemeState:CPThemeStateVertical];
|
[self setThemeState:CPThemeStateVertical];
|
||||||
else
|
else if (_isVertical === 0)
|
||||||
[self unsetThemeState:CPThemeStateVertical];
|
[self unsetThemeState:CPThemeStateVertical];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,20 +564,6 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Overrides
|
|
||||||
|
|
||||||
- (id)currentValueForThemeAttribute:(CPString)anAttributeName
|
|
||||||
{
|
|
||||||
var themeState = _themeState;
|
|
||||||
|
|
||||||
if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
|
|
||||||
themeState &= ~CPThemeStateHighlighted;
|
|
||||||
|
|
||||||
return [self valueForThemeAttribute:anAttributeName inState:themeState];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
if (![self isEnabled])
|
if (![self isEnabled])
|
||||||
@@ -746,65 +573,19 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
|
|
||||||
switch (_hitPart)
|
switch (_hitPart)
|
||||||
{
|
{
|
||||||
case CPScrollerKnob:
|
case CPScrollerKnob: return [self trackKnob:anEvent];
|
||||||
return [self trackKnob:anEvent];
|
|
||||||
|
|
||||||
case CPScrollerDecrementLine:
|
case CPScrollerDecrementLine:
|
||||||
case CPScrollerIncrementLine:
|
case CPScrollerIncrementLine:
|
||||||
case CPScrollerDecrementPage:
|
case CPScrollerDecrementPage:
|
||||||
case CPScrollerIncrementPage:
|
case CPScrollerIncrementPage: return [self trackScrollButtons:anEvent];
|
||||||
return [self trackScrollButtons:anEvent];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseEntered:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
[super mouseEntered:anEvent];
|
|
||||||
|
|
||||||
if (_timerFadeOut)
|
|
||||||
[_timerFadeOut invalidate];
|
|
||||||
|
|
||||||
if (![self isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
_allowFadingOut = NO;
|
|
||||||
_isMouseOver = YES;
|
|
||||||
|
|
||||||
if ([self alphaValue] > 0 && _knobProportion != 1.0)
|
|
||||||
[self setThemeState:CPThemeStateSelected];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseExited:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
[super mouseExited:anEvent];
|
|
||||||
|
|
||||||
if ([self isHidden] || ![self isEnabled] || !_isMouseOver)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_allowFadingOut = YES;
|
|
||||||
_isMouseOver = NO;
|
|
||||||
|
|
||||||
if (_timerFadeOut)
|
|
||||||
[_timerFadeOut invalidate];
|
|
||||||
|
|
||||||
_timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Delegates
|
|
||||||
|
|
||||||
- (void)animationDidEnd:(CPAnimation)animation
|
|
||||||
{
|
|
||||||
[self unsetThemeState:CPThemeStateSelected];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPScrollerControlSizeKey = @"CPScrollerControlSize",
|
var CPScrollerControlSizeKey = "CPScrollerControlSize",
|
||||||
CPScrollerIsVerticalKey = @"CPScrollerIsVerticalKey",
|
CPScrollerKnobProportionKey = "CPScrollerKnobProportion";
|
||||||
CPScrollerKnobProportionKey = @"CPScrollerKnobProportion",
|
|
||||||
CPScrollerStyleKey = @"CPScrollerStyleKey";
|
|
||||||
|
|
||||||
@implementation CPScroller (CPCoding)
|
@implementation CPScroller (CPCoding)
|
||||||
|
|
||||||
@@ -817,7 +598,6 @@ var CPScrollerControlSizeKey = @"CPScrollerControlSize",
|
|||||||
_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];
|
||||||
|
|
||||||
@@ -825,22 +605,20 @@ var CPScrollerControlSizeKey = @"CPScrollerControlSize",
|
|||||||
|
|
||||||
_hitPart = CPScrollerNoPart;
|
_hitPart = CPScrollerNoPart;
|
||||||
|
|
||||||
_allowFadingOut = YES;
|
[self _calculateIsVertical];
|
||||||
_isMouseOver = NO;
|
|
||||||
|
|
||||||
var paramAnimFadeOut = @{
|
// Adjust the size of the scroller if the size from cib
|
||||||
CPViewAnimationTargetKey: self,
|
// isn't equal to the scrollerWidth
|
||||||
CPViewAnimationEffectKey: CPViewAnimationFadeOutEffect,
|
var frame = [self frame],
|
||||||
};
|
scrollerWidth = [CPScroller scrollerWidth];
|
||||||
|
|
||||||
_animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
|
if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
|
||||||
[_animationScroller setViewAnimations:[paramAnimFadeOut]];
|
frame.size.width = scrollerWidth;
|
||||||
[_animationScroller setDelegate:self];
|
|
||||||
[self setAlphaValue:0.0];
|
|
||||||
|
|
||||||
[self setStyle:[aCoder decodeIntForKey:CPScrollerStyleKey]];
|
if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
|
||||||
|
frame.size.height = scrollerWidth;
|
||||||
|
|
||||||
[self _setIsVertical:[aCoder decodeBoolForKey:CPScrollerIsVerticalKey]];
|
[self setFrame:frame];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -851,9 +629,7 @@ var CPScrollerControlSizeKey = @"CPScrollerControlSize",
|
|||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
[aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
|
[aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
|
||||||
[aCoder encodeInt:_isVertical forKey:CPScrollerIsVerticalKey];
|
|
||||||
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
|
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
|
||||||
[aCoder encodeInt:_style forKey:CPScrollerStyleKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+78
-70
@@ -21,23 +21,27 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
@import "CPMenu.j"
|
|
||||||
@import "CPMenuItem.j"
|
|
||||||
@import "CPTextField.j"
|
@import "CPTextField.j"
|
||||||
|
|
||||||
@class CPUserDefaults
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
CPSearchFieldRecentsTitleMenuItemTag = 1000;
|
CPSearchFieldRecentsTitleMenuItemTag = 1000;
|
||||||
CPSearchFieldRecentsMenuItemTag = 1001;
|
CPSearchFieldRecentsMenuItemTag = 1001;
|
||||||
CPSearchFieldClearRecentsMenuItemTag = 1002;
|
CPSearchFieldClearRecentsMenuItemTag = 1002;
|
||||||
CPSearchFieldNoRecentsMenuItemTag = 1003;
|
CPSearchFieldNoRecentsMenuItemTag = 1003;
|
||||||
|
CPSearchFieldSeparatorMenuItemTag = 1004;
|
||||||
|
|
||||||
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification";
|
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 = @" ";
|
var RECENT_SEARCH_PREFIX = @" ";
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPSearchField
|
@class CPSearchField
|
||||||
@@ -67,14 +71,16 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
return @"searchfield"
|
return @"searchfield"
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPDictionary)themeAttributes
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
return @{
|
if (self != [CPSearchField class])
|
||||||
@"image-search": [CPNull null],
|
return;
|
||||||
@"image-find": [CPNull null],
|
|
||||||
@"image-cancel": [CPNull null],
|
var bundle = [CPBundle bundleForClass:self];
|
||||||
@"image-cancel-pressed": [CPNull null]
|
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
|
- (id)initWithFrame:(CGRect)frame
|
||||||
@@ -104,6 +110,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
[self setBezelStyle:CPTextFieldRoundedBezel];
|
[self setBezelStyle:CPTextFieldRoundedBezel];
|
||||||
[self setBordered:YES];
|
[self setBordered:YES];
|
||||||
[self setEditable:YES];
|
[self setEditable:YES];
|
||||||
|
[self setDelegate:self];
|
||||||
[self setContinuous:YES];
|
[self setContinuous:YES];
|
||||||
|
|
||||||
var bounds = [self bounds],
|
var bounds = [self bounds],
|
||||||
@@ -119,18 +126,6 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
_canResignFirstResponder = YES;
|
_canResignFirstResponder = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewWillMoveToSuperview:(CPView)aView
|
|
||||||
{
|
|
||||||
[super viewWillMoveToSuperview:aView];
|
|
||||||
|
|
||||||
// First we remove any observer that may have been in place to avoid memory leakage.
|
|
||||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPControlTextDidChangeNotification object:self];
|
|
||||||
|
|
||||||
// Register the observe here if we need to.
|
|
||||||
if (aView)
|
|
||||||
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_searchFieldTextDidChange:) name:CPControlTextDidChangeNotification object:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Managing Buttons
|
// Managing Buttons
|
||||||
/*!
|
/*!
|
||||||
Sets the button used to display the search-button image
|
Sets the button used to display the search-button image
|
||||||
@@ -165,10 +160,10 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
- (void)resetSearchButton
|
- (void)resetSearchButton
|
||||||
{
|
{
|
||||||
var button = [self searchButton],
|
var button = [self searchButton],
|
||||||
searchButtonImage = (_searchMenuTemplate === nil) ? [self valueForThemeAttribute:@"image-search"] : [self valueForThemeAttribute:@"image-find"];
|
searchButtonImage = (_searchMenuTemplate === nil) ? CPSearchFieldSearchImage : CPSearchFieldFindImage;
|
||||||
|
|
||||||
[button setBordered:NO];
|
[button setBordered:NO];
|
||||||
[button setImageScaling:CPImageScaleAxesIndependently];
|
[button setImageScaling:CPScaleToFit];
|
||||||
[button setImage:searchButtonImage];
|
[button setImage:searchButtonImage];
|
||||||
[button setAutoresizingMask:CPViewMaxXMargin];
|
[button setAutoresizingMask:CPViewMaxXMargin];
|
||||||
}
|
}
|
||||||
@@ -187,8 +182,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
[_cancelButton setFrame:[self cancelButtonRectForBounds:[self bounds]]];
|
[_cancelButton setFrame:[self cancelButtonRectForBounds:[self bounds]]];
|
||||||
[_cancelButton setAutoresizingMask:CPViewMinXMargin];
|
[_cancelButton setAutoresizingMask:CPViewMinXMargin];
|
||||||
[_cancelButton setTarget:self];
|
[_cancelButton setTarget:self];
|
||||||
[_cancelButton setAction:@selector(cancelOperation:)];
|
[_cancelButton setAction:@selector(_searchFieldCancel:)];
|
||||||
[_cancelButton setButtonType:CPMomentaryChangeButton];
|
|
||||||
[self _updateCancelButtonVisibility];
|
[self _updateCancelButtonVisibility];
|
||||||
[self addSubview:_cancelButton];
|
[self addSubview:_cancelButton];
|
||||||
}
|
}
|
||||||
@@ -205,18 +199,18 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Resets the cancel button to its default attributes.
|
Resets the cancel button to its default attributes.
|
||||||
This method resets the target, action, regular image, and pressed image. 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.
|
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
|
- (void)resetCancelButton
|
||||||
{
|
{
|
||||||
var button = [self cancelButton];
|
var button = [self cancelButton];
|
||||||
[button setBordered:NO];
|
[button setBordered:NO];
|
||||||
[button setImageScaling:CPImageScaleAxesIndependently];
|
[button setImageScaling:CPScaleToFit];
|
||||||
[button setImage:[self valueForThemeAttribute:@"image-cancel"]];
|
[button setImage:CPSearchFieldCancelImage];
|
||||||
[button setAlternateImage:[self valueForThemeAttribute:@"image-cancel-pressed"]];
|
[button setAlternateImage:CPSearchFieldCancelPressedImage];
|
||||||
[button setAutoresizingMask:CPViewMinXMargin];
|
[button setAutoresizingMask:CPViewMinXMargin];
|
||||||
[button setTarget:self];
|
[button setTarget:self];
|
||||||
[button setAction:@selector(cancelOperation:)];
|
[button setAction:@selector(_searchFieldCancel:)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom Layout
|
// Custom Layout
|
||||||
@@ -229,22 +223,22 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
- (CGRect)searchTextRectForBounds:(CGRect)rect
|
- (CGRect)searchTextRectForBounds:(CGRect)rect
|
||||||
{
|
{
|
||||||
var leftOffset = 0,
|
var leftOffset = 0,
|
||||||
width = CGRectGetWidth(rect),
|
width = _CGRectGetWidth(rect),
|
||||||
bounds = [self bounds];
|
bounds = [self bounds];
|
||||||
|
|
||||||
if (_searchButton)
|
if (_searchButton)
|
||||||
{
|
{
|
||||||
var searchBounds = [self searchButtonRectForBounds:bounds];
|
var searchBounds = [self searchButtonRectForBounds:bounds];
|
||||||
leftOffset = CGRectGetMaxX(searchBounds) + 2;
|
leftOffset = _CGRectGetMaxX(searchBounds) + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_cancelButton)
|
if (_cancelButton)
|
||||||
{
|
{
|
||||||
var cancelRect = [self cancelButtonRectForBounds:bounds];
|
var cancelRect = [self cancelButtonRectForBounds:bounds];
|
||||||
width = CGRectGetMinX(cancelRect) - leftOffset;
|
width = _CGRectGetMinX(cancelRect) - leftOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CGRectMake(leftOffset, CGRectGetMinY(rect), width, CGRectGetHeight(rect));
|
return _CGRectMake(leftOffset, _CGRectGetMinY(rect), width, _CGRectGetHeight(rect));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -254,9 +248,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
*/
|
*/
|
||||||
- (CGRect)searchButtonRectForBounds:(CGRect)rect
|
- (CGRect)searchButtonRectForBounds:(CGRect)rect
|
||||||
{
|
{
|
||||||
var size = [[self valueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero();
|
return _CGRectMake(5, (_CGRectGetHeight(rect) - BUTTON_DEFAULT_HEIGHT) / 2, SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT);
|
||||||
|
|
||||||
return CGRectMake(5, (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -266,9 +258,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
*/
|
*/
|
||||||
- (CGRect)cancelButtonRectForBounds:(CGRect)rect
|
- (CGRect)cancelButtonRectForBounds:(CGRect)rect
|
||||||
{
|
{
|
||||||
var size = [[self valueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero();
|
return _CGRectMake(_CGRectGetWidth(rect) - CANCEL_BUTTON_DEFAULT_WIDTH - 5, (_CGRectGetHeight(rect) - CANCEL_BUTTON_DEFAULT_WIDTH) / 2, BUTTON_DEFAULT_HEIGHT, BUTTON_DEFAULT_HEIGHT);
|
||||||
|
|
||||||
return CGRectMake(CGRectGetWidth(rect) - size.width - 5, (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managing Menu Templates
|
// Managing Menu Templates
|
||||||
@@ -413,7 +403,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
|
|
||||||
+ (double)_keyboardDelayForPartialSearchString:(CPString)string
|
+ (double)_keyboardDelayForPartialSearchString:(CPString)string
|
||||||
{
|
{
|
||||||
return (6 - MIN([string length], 4)) / 10;
|
return (6 - MIN([string length],4))/10;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPMenu)menu
|
- (CPMenu)menu
|
||||||
@@ -431,7 +421,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
[_cancelButton setHidden:([[self stringValue] length] === 0)];
|
[_cancelButton setHidden:([[self stringValue] length] === 0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_searchFieldTextDidChange:(CPNotification)aNotification
|
- (void)controlTextDidChange:(CPNotification)aNotification
|
||||||
{
|
{
|
||||||
if (![self sendsWholeSearchString])
|
if (![self sendsWholeSearchString])
|
||||||
{
|
{
|
||||||
@@ -482,7 +472,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
- (CPView)hitTest:(CGPoint)aPoint
|
- (CPView)hitTest:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
// Make sure a hit anywhere within the search field returns the search field itself
|
// Make sure a hit anywhere within the search field returns the search field itself
|
||||||
if (CGRectContainsPoint([self frame], aPoint))
|
if (_CGRectContainsPoint([self frame], aPoint))
|
||||||
return self;
|
return self;
|
||||||
else
|
else
|
||||||
return nil;
|
return nil;
|
||||||
@@ -498,19 +488,14 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
var location = [anEvent locationInWindow],
|
var location = [anEvent locationInWindow],
|
||||||
point = [self convertPoint:location fromView:nil];
|
point = [self convertPoint:location fromView:nil];
|
||||||
|
|
||||||
if (CGRectContainsPoint([self searchButtonRectForBounds:[self bounds]], point))
|
if (_CGRectContainsPoint([self searchButtonRectForBounds:[self bounds]], point))
|
||||||
{
|
{
|
||||||
if (_searchMenuTemplate == nil)
|
if (_searchMenuTemplate == nil)
|
||||||
{
|
[self _sendAction:self];
|
||||||
if ([_searchButton target] && [_searchButton action])
|
|
||||||
[_searchButton mouseDown:anEvent];
|
|
||||||
else
|
|
||||||
[self _sendAction:self];
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
[self _showMenu];
|
[self _showMenu];
|
||||||
}
|
}
|
||||||
else if (CGRectContainsPoint([self cancelButtonRectForBounds:[self bounds]], point))
|
else if (_CGRectContainsPoint([self cancelButtonRectForBounds:[self bounds]], point))
|
||||||
[_cancelButton mouseDown:anEvent];
|
[_cancelButton mouseDown:anEvent];
|
||||||
else
|
else
|
||||||
[super mouseDown:anEvent];
|
[super mouseDown:anEvent];
|
||||||
@@ -544,7 +529,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
[item setTarget:self];
|
[item setTarget:self];
|
||||||
[template addItem:item];
|
[template addItem:item];
|
||||||
|
|
||||||
Be sure that your custom items do not use tags in the range 1000-1003 inclusive.
|
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
|
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
|
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.
|
in the search menu template and send [searchField setSearchMenuTemplate:template] to update the menu.
|
||||||
@@ -643,7 +628,13 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
if ([menu numberOfItems] > 0)
|
if ([menu numberOfItems] > 0)
|
||||||
[self _addSeparatorToMenu:menu];
|
[self _addSeparatorToMenu:menu];
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
case CPSearchFieldSeparatorMenuItemTag:
|
||||||
|
item = [CPMenuItem separatorItem];
|
||||||
|
[item setEnabled:NO];
|
||||||
|
[menu addItem:item];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
[item setEnabled:([item isEnabled] && [item action] != nil && [item target] != nil)];
|
[item setEnabled:([item isEnabled] && [item action] != nil && [item target] != nil)];
|
||||||
[menu addItem:item];
|
[menu addItem:item];
|
||||||
@@ -679,7 +670,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var aFrame = [[self superview] convertRect:[self frame] toView:nil],
|
var aFrame = [[self superview] convertRect:[self frame] toView:nil],
|
||||||
location = CGPointMake(aFrame.origin.x + 10, aFrame.origin.y + aFrame.size.height - 4);
|
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];
|
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];
|
||||||
|
|
||||||
@@ -693,11 +684,10 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
[_partialStringTimer invalidate];
|
[_partialStringTimer invalidate];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)cancelOperation:(id)sender
|
- (void)_searchFieldCancel:(id)sender
|
||||||
{
|
{
|
||||||
[self setObjectValue:@""];
|
[self setObjectValue:@""];
|
||||||
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
[self _sendPartialString];
|
||||||
|
|
||||||
[self _updateCancelButtonVisibility];
|
[self _updateCancelButtonVisibility];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,36 +715,54 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
|
|
||||||
- (void)_registerForAutosaveNotification
|
- (void)_registerForAutosaveNotification
|
||||||
{
|
{
|
||||||
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateAutosavedRecents:) name:CPAutosavedRecentsChangedNotification object:_recentsAutosaveName];
|
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateAutosavedRecents:) name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_deregisterForAutosaveNotification
|
- (void)_deregisterForAutosaveNotification
|
||||||
{
|
{
|
||||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPAutosavedRecentsChangedNotification object:_recentsAutosaveName];
|
[[CPNotificationCenter defaultCenter] removeObserver:self name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_autosaveRecentSearchList
|
- (void)_autosaveRecentSearchList
|
||||||
{
|
{
|
||||||
if (_recentsAutosaveName != nil)
|
if (_recentsAutosaveName != nil)
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPAutosavedRecentsChangedNotification object:_recentsAutosaveName];
|
[[CPNotificationCenter defaultCenter] postNotificationName:@"CPAutosavedRecentsChangedNotification" object:_recentsAutosaveName];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_updateAutosavedRecents:(id)notification
|
- (void)_updateAutosavedRecents:(id)notification
|
||||||
{
|
{
|
||||||
var name = [notification object];
|
var list = [self recentSearches],
|
||||||
[[CPUserDefaults standardUserDefaults] setObject:_recentSearches forKey:name];
|
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
|
- (void)_loadRecentSearchList
|
||||||
{
|
{
|
||||||
var name = [self recentsAutosaveName];
|
var list,
|
||||||
|
name = [self recentsAutosaveName];
|
||||||
|
|
||||||
if (name === nil)
|
if (name === nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var list = [[CPUserDefaults standardUserDefaults] objectForKey:name];
|
var bundle_name = [[[CPBundle mainBundle] infoDictionary] objectForKey:"CPBundleName"],
|
||||||
|
cookie_name = [bundle_name lowercaseString] + "." + name,
|
||||||
|
|
||||||
if (list !== nil)
|
cookie = [[CPCookie alloc] initWithName:cookie_name];
|
||||||
|
|
||||||
|
if (cookie != nil)
|
||||||
|
{
|
||||||
|
var cookie_value = [cookie value];
|
||||||
|
list = (cookie_value != @"") ? [cookie_value componentsSeparatedByString:@","] : [CPArray array];
|
||||||
_recentSearches = list;
|
_recentSearches = list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -794,7 +802,7 @@ var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
|
|||||||
{
|
{
|
||||||
if (self = [super initWithCoder:coder])
|
if (self = [super initWithCoder:coder])
|
||||||
{
|
{
|
||||||
[self setRecentsAutosaveName:[coder decodeObjectForKey:CPRecentsAutosaveNameKey]];
|
_recentsAutosaveName = [coder decodeObjectForKey:CPRecentsAutosaveNameKey];
|
||||||
_sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
|
_sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
|
||||||
_sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
|
_sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
|
||||||
_maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
|
_maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
|
||||||
|
|||||||
@@ -1,24 +1,3 @@
|
|||||||
/*
|
|
||||||
* CPSecureTextField.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 "CPTextField.j"
|
@import "CPTextField.j"
|
||||||
|
|
||||||
|
|||||||
+12
-152
@@ -23,10 +23,7 @@
|
|||||||
@import <Foundation/CPArray.j>
|
@import <Foundation/CPArray.j>
|
||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPWindow_Constants.j"
|
|
||||||
@import "_CPImageAndTextView.j"
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
CPSegmentSwitchTrackingSelectOne = 0;
|
CPSegmentSwitchTrackingSelectOne = 0;
|
||||||
CPSegmentSwitchTrackingSelectAny = 1;
|
CPSegmentSwitchTrackingSelectAny = 1;
|
||||||
@@ -58,33 +55,8 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return @{
|
return [CPDictionary dictionaryWithObjects:[CPCenterTextAlignment, CPCenterVerticalTextAlignment, CPImageLeft, CPScaleNone, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null], 1.0, 24.0]
|
||||||
@"alignment": CPCenterTextAlignment,
|
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"]];
|
||||||
@"vertical-alignment": CPCenterVerticalTextAlignment,
|
|
||||||
@"image-position": CPImageLeft,
|
|
||||||
@"image-scaling": CPImageScaleNone,
|
|
||||||
@"bezel-inset": CGInsetMakeZero(),
|
|
||||||
@"content-inset": CGInsetMakeZero(),
|
|
||||||
@"left-segment-bezel-color": [CPNull null],
|
|
||||||
@"right-segment-bezel-color": [CPNull null],
|
|
||||||
@"center-segment-bezel-color": [CPNull null],
|
|
||||||
@"divider-bezel-color": [CPNull null],
|
|
||||||
@"divider-thickness": 1.0,
|
|
||||||
@"default-height": 24.0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if ([self _isSelectionBinding:aBinding])
|
|
||||||
return [_CPSegmentedControlBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)_isSelectionBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
return (aBinding === CPSelectedIndexBinding || aBinding === CPSelectedLabelBinding || aBinding === CPSelectedTagBinding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aRect
|
- (id)initWithFrame:(CGRect)aRect
|
||||||
@@ -104,19 +76,6 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
|
|
||||||
{
|
|
||||||
if ([[self class] _isSelectionBinding:aBinding] && _trackingMode !== CPSegmentSwitchTrackingSelectOne)
|
|
||||||
CPLog.warn("Binding " + aBinding + " needs CPSegmentSwitchTrackingSelectOne tracking mode");
|
|
||||||
else
|
|
||||||
[super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_reverseSetBinding
|
|
||||||
{
|
|
||||||
[_CPSegmentedControlBinder reverseSetValueForObject:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the tag of the selected segment.
|
Returns the tag of the selected segment.
|
||||||
*/
|
*/
|
||||||
@@ -135,9 +94,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
if (_segments.length == aCount)
|
if (_segments.length == aCount)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var height = CGRectGetHeight([self bounds]),
|
var height = CGRectGetHeight([self bounds]);
|
||||||
dividersBefore = MAX(0, _segments.length - 1),
|
|
||||||
dividersAfter = MAX(0, aCount - 1);
|
|
||||||
|
|
||||||
if (_segments.length < aCount)
|
if (_segments.length < aCount)
|
||||||
{
|
{
|
||||||
@@ -156,16 +113,6 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
if (_selectedSegment >= _segments.length)
|
if (_selectedSegment >= _segments.length)
|
||||||
_selectedSegment = -1;
|
_selectedSegment = -1;
|
||||||
|
|
||||||
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
|
|
||||||
frame = [self frame],
|
|
||||||
widthOfAllSegments = 0,
|
|
||||||
dividerExtraSpace = ([_segments count] - 1) * thickness;
|
|
||||||
|
|
||||||
for (var i = 0; i < [_segments count]; i++)
|
|
||||||
widthOfAllSegments += [_segments[i] width];
|
|
||||||
|
|
||||||
[self setFrameSize:CGSizeMake(widthOfAllSegments + dividerExtraSpace, frame.size.height)];
|
|
||||||
|
|
||||||
[self tileWithChangedSegment:0];
|
[self tileWithChangedSegment:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,21 +162,6 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_selectSegmentWithLabel:(CPString)aLabel
|
|
||||||
{
|
|
||||||
var index = 0;
|
|
||||||
|
|
||||||
for (; index < _segments.length; ++index)
|
|
||||||
if (_segments[index].label == aLabel)
|
|
||||||
{
|
|
||||||
[self setSelectedSegment:index];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specifying Tracking Mode
|
// Specifying Tracking Mode
|
||||||
|
|
||||||
- (BOOL)isTracking
|
- (BOOL)isTracking
|
||||||
@@ -419,19 +351,16 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Enables/disables the specified segment.
|
Enables/diables the specified segment.
|
||||||
@param isEnabled \c YES enables the segment
|
@param isEnabled \c YES enables the segment
|
||||||
@param aSegment the segment to enable/disable
|
@param aSegment the segment to enable/disble
|
||||||
@throws CPRangeException if \c aSegment is out of bounds
|
@throws CPRangeException if \c aSegment is out of bounds
|
||||||
*/
|
*/
|
||||||
- (void)setEnabled:(BOOL)shouldBeEnabled forSegment:(unsigned)aSegment
|
- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
|
||||||
{
|
{
|
||||||
if ([_segments[aSegment] enabled] === shouldBeEnabled)
|
[_segments[aSegment] setEnabled:isEnabled];
|
||||||
return;
|
|
||||||
|
|
||||||
[_segments[aSegment] setEnabled:shouldBeEnabled];
|
if (isEnabled)
|
||||||
|
|
||||||
if (shouldBeEnabled)
|
|
||||||
_themeStates[aSegment] &= ~CPThemeStateDisabled;
|
_themeStates[aSegment] &= ~CPThemeStateDisabled;
|
||||||
else
|
else
|
||||||
_themeStates[aSegment] |= CPThemeStateDisabled;
|
_themeStates[aSegment] |= CPThemeStateDisabled;
|
||||||
@@ -521,7 +450,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
}
|
}
|
||||||
else if (aName === "right-segment-bezel")
|
else if (aName === "right-segment-bezel")
|
||||||
{
|
{
|
||||||
return CGRectMake(CGRectGetWidth([self bounds]) - contentInset.right,
|
return CPRectMake(CGRectGetWidth([self bounds]) - contentInset.right,
|
||||||
bezelInset.top,
|
bezelInset.top,
|
||||||
contentInset.right,
|
contentInset.right,
|
||||||
height);
|
height);
|
||||||
@@ -564,9 +493,9 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||||
{
|
{
|
||||||
if ([aName hasPrefix:@"segment-content"])
|
if ([aName hasPrefix:@"segment-content"])
|
||||||
return [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
|
return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
@@ -678,7 +607,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
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 font];
|
font = [self valueForThemeAttribute:@"font" inState:themeState];
|
||||||
|
|
||||||
if (!segmentWidth)
|
if (!segmentWidth)
|
||||||
{
|
{
|
||||||
@@ -857,7 +786,6 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var highlighted = [self testSegment:location] === _trackingSegment;
|
var highlighted = [self testSegment:location] === _trackingSegment;
|
||||||
|
|
||||||
if (highlighted != _trackingHighlighted)
|
if (highlighted != _trackingHighlighted)
|
||||||
{
|
{
|
||||||
_trackingHighlighted = highlighted;
|
_trackingHighlighted = highlighted;
|
||||||
@@ -944,74 +872,6 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPSegmentedControlBindersMap = {},
|
|
||||||
CPSegmentedControlNoSelectionPlaceholder = "CPSegmentedControlNoSelectionPlaceholder";
|
|
||||||
|
|
||||||
@implementation _CPSegmentedControlBinder : CPBinder
|
|
||||||
{
|
|
||||||
CPString _selectionBinding @accessors(readonly, getter=selectionBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)reverseSetValueForObject:(id)aSource
|
|
||||||
{
|
|
||||||
var binder = CPSegmentedControlBindersMap[[aSource UID]];
|
|
||||||
[binder reverseSetValueFor:[binder selectionBinding]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
|
|
||||||
{
|
|
||||||
self = [super initWithBinding:aBinding name:aName to:aDestination keyPath:aKeyPath options:options from:aSource];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
CPSegmentedControlBindersMap[[aSource UID]] = self;
|
|
||||||
_selectionBinding = aName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
|
||||||
{
|
|
||||||
[super _updatePlaceholdersWithOptions:options];
|
|
||||||
|
|
||||||
[self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPMultipleValuesMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNoSelectionMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNotApplicableMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNullMarker isDefault:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aValue == CPSegmentedControlNoSelectionPlaceholder)
|
|
||||||
[_source setSelected:NO forSegment:[_source selectedSegment]];
|
|
||||||
else
|
|
||||||
[self setValue:aValue forBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding == CPSelectedIndexBinding)
|
|
||||||
[_source setSelectedSegment:aValue];
|
|
||||||
else if (aBinding == CPSelectedTagBinding)
|
|
||||||
[_source selectSegmentWithTag:aValue];
|
|
||||||
else if (aBinding == CPSelectedLabelBinding)
|
|
||||||
[_source _selectSegmentWithLabel:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var selectedIndex = [_source selectedSegment];
|
|
||||||
|
|
||||||
if (aBinding == CPSelectedIndexBinding)
|
|
||||||
return selectedIndex;
|
|
||||||
else if (aBinding == CPSelectedTagBinding)
|
|
||||||
return [_source tagForSegment:selectedIndex];
|
|
||||||
else if (aBinding == CPSelectedLabelBinding)
|
|
||||||
return [_source labelForSegment:selectedIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPSegmentItem : CPObject
|
@implementation _CPSegmentItem : CPObject
|
||||||
{
|
{
|
||||||
|
|||||||
+29
-8
@@ -31,9 +31,11 @@
|
|||||||
*/
|
*/
|
||||||
@implementation CPShadow : CPObject
|
@implementation CPShadow : CPObject
|
||||||
{
|
{
|
||||||
CGSize _offset @accessors(property=shadowOffset);
|
CPSize _offset;
|
||||||
float _blurRadius @accessors(property=shadowBlurRadius);
|
float _blurRadius;
|
||||||
CPColor _color @accessors(property=shadowColor);
|
CPColor _color;
|
||||||
|
|
||||||
|
CPString _cssString;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -49,7 +51,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* @ignore */
|
/* @ignore */
|
||||||
- (id)_initWithOffset:(CGSize)anOffset blurRadius:(float)aBlurRadius color:(CPColor)aColor
|
- (id)_initWithOffset:(CPSize)anOffset blurRadius:(float)aBlurRadius color:(CPColor)aColor
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
@@ -58,16 +60,35 @@
|
|||||||
_offset = anOffset;
|
_offset = anOffset;
|
||||||
_blurRadius = aBlurRadius;
|
_blurRadius = aBlurRadius;
|
||||||
_color = aColor;
|
_color = aColor;
|
||||||
|
|
||||||
|
_cssString = [_color cssString] + " " + Math.round(anOffset.width) + @"px " + Math.round(anOffset.height) + @"px " + Math.round(_blurRadius) + @"px";
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)set
|
/*!
|
||||||
|
Returns the shadow's offset.
|
||||||
|
*/
|
||||||
|
- (CGSize)shadowOffset
|
||||||
{
|
{
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
return _offset;
|
||||||
|
}
|
||||||
|
|
||||||
CGContextSetShadowWithColor(context, _offset, _blurRadius, _color);
|
/*!
|
||||||
|
Returns the shadow's blur radius
|
||||||
|
*/
|
||||||
|
- (float)shadowBlurRadius
|
||||||
|
{
|
||||||
|
return _blurRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the shadow's color.
|
||||||
|
*/
|
||||||
|
- (CPColor)shadowColor
|
||||||
|
{
|
||||||
|
return _color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -75,7 +96,7 @@
|
|||||||
*/
|
*/
|
||||||
- (CPString)cssString
|
- (CPString)cssString
|
||||||
{
|
{
|
||||||
return [_color cssString] + " " + ROUND(_offset.width) + @"px " + ROUND(_offset.height) + @"px " + ROUND(_blurRadius) + @"px";
|
return _cssString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
+81
-54
@@ -24,43 +24,71 @@
|
|||||||
|
|
||||||
@import "CGGeometry.j"
|
@import "CGGeometry.j"
|
||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
|
@import "CPImageView.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
|
|
||||||
CPLightShadow = 0;
|
CPLightShadow = 0;
|
||||||
CPHeavyShadow = 1;
|
CPHeavyShadow = 1;
|
||||||
|
|
||||||
CPThemeStateShadowViewLight = CPThemeState("shadowview-style-light");
|
var CPShadowViewLightBackgroundColor = nil,
|
||||||
CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
|
CPShadowViewHeavyBackgroundColor = nil;
|
||||||
|
|
||||||
|
var LIGHT_LEFT_INSET = 3.0,
|
||||||
|
LIGHT_RIGHT_INSET = 3.0,
|
||||||
|
LIGHT_TOP_INSET = 3.0,
|
||||||
|
LIGHT_BOTTOM_INSET = 5.0,
|
||||||
|
|
||||||
|
HEAVY_LEFT_INSET = 7.0,
|
||||||
|
HEAVY_RIGHT_INSET = 7.0,
|
||||||
|
HEAVY_TOP_INSET = 5.0,
|
||||||
|
HEAVY_BOTTOM_INSET = 5.0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@implementation CPShadowView : CPView
|
@implementation CPShadowView : CPView
|
||||||
{
|
{
|
||||||
CPShadowWeight _weight;
|
CPShadowWeight _weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
return "shadow-view";
|
if (self != [CPShadowView class])
|
||||||
}
|
return;
|
||||||
|
|
||||||
+ (id)themeAttributes
|
var bundle = [CPBundle bundleForClass:[self class]];
|
||||||
{
|
|
||||||
return @{
|
|
||||||
@"bezel-color": [CPNull null],
|
|
||||||
@"content-inset": CGInsetMakeZero()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CGRect)frameForContentFrame:(CGRect)aFrame withWeight:(CPShadowWeight)aWeight
|
CPShadowViewLightBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||||
{
|
[
|
||||||
var shadowView = [CPShadowView new],
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopLeft.png"] size:CGSizeMake(9.0, 9.0)],
|
||||||
inset = [shadowView valueForThemeAttribute:@"content-inset" inState:(aWeight == CPLightShadow) ? CPThemeStateShadowViewLight : CPThemeStateShadowViewHeavy];
|
[[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)],
|
||||||
|
|
||||||
return CGRectMake(CGRectGetMinX(aFrame) - inset.left, CGRectGetMinY(aFrame) - inset.top, CGRectGetWidth(aFrame) + inset.left + inset.right, CGRectGetHeight(aFrame) + inset.top + inset.bottom);
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightLeft.png"] size:CGSizeMake(9.0, 1.0)],
|
||||||
|
nil,
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightRight.png"] size:CGSizeMake(9.0, 1.0)],
|
||||||
|
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomLeft.png"] size:CGSizeMake(9.0, 9.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottom.png"] size:CGSizeMake(1.0, 9.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomRight.png"] size:CGSizeMake(9.0, 9.0)]
|
||||||
|
]]];
|
||||||
|
|
||||||
|
CPShadowViewHeavyBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||||
|
[
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopLeft.png"] size:CGSizeMake(17.0, 17.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTop.png"] size:CGSizeMake(1.0, 17.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopRight.png"] size:CGSizeMake(17.0, 17.0)],
|
||||||
|
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyLeft.png"] size:CGSizeMake(17.0, 1.0)],
|
||||||
|
nil,
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyRight.png"] size:CGSizeMake(17.0, 1.0)],
|
||||||
|
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomLeft.png"] size:CGSizeMake(17.0, 17.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottom.png"] size:CGSizeMake(1.0, 17.0)],
|
||||||
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomRight.png"] size:CGSizeMake(17.0, 17.0)]
|
||||||
|
]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)shadowViewEnclosingView:(CPView)aView
|
+ (id)shadowViewEnclosingView:(CPView)aView
|
||||||
@@ -70,25 +98,20 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
|
|||||||
|
|
||||||
+ (id)shadowViewEnclosingView:(CPView)aView withWeight:(CPShadowWeight)aWeight
|
+ (id)shadowViewEnclosingView:(CPView)aView withWeight:(CPShadowWeight)aWeight
|
||||||
{
|
{
|
||||||
var shadowView = [[self alloc] initWithFrame:[aView frame]];
|
var shadowView = [[CPShadowView alloc] initWithFrame:[aView frame]];
|
||||||
|
[shadowView setWeight:aWeight];
|
||||||
|
|
||||||
if (shadowView)
|
var size = [shadowView frame].size,
|
||||||
{
|
width = size.width - [shadowView leftInset] - [shadowView rightInset],
|
||||||
[shadowView setWeight:aWeight];
|
height = size.height - [shadowView topInset] - [shadowView bottomInset],
|
||||||
|
enclosingView = [aView superview];
|
||||||
|
|
||||||
var size = [shadowView frame].size,
|
[shadowView setHitTests:[aView hitTests]];
|
||||||
inset = [shadowView currentValueForThemeAttribute:@"content-inset"],
|
[shadowView setAutoresizingMask:[aView autoresizingMask]];
|
||||||
width = size.width - inset.left - inset.right,
|
[aView removeFromSuperview];
|
||||||
height = size.height - inset.top - inset.bottom,
|
[shadowView addSubview:aView];
|
||||||
enclosingView = [aView superview];
|
[aView setFrame:CGRectMake([shadowView leftInset], [shadowView topInset], width, height)]
|
||||||
|
[enclosingView addSubview:shadowView];
|
||||||
[shadowView setHitTests:[aView hitTests]];
|
|
||||||
[shadowView setAutoresizingMask:[aView autoresizingMask]];
|
|
||||||
[aView removeFromSuperview];
|
|
||||||
[shadowView addSubview:aView];
|
|
||||||
[aView setFrame:CGRectMake(inset.left, inset.top, width, height)];
|
|
||||||
[enclosingView addSubview:shadowView];
|
|
||||||
}
|
|
||||||
|
|
||||||
return shadowView;
|
return shadowView;
|
||||||
}
|
}
|
||||||
@@ -99,7 +122,9 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
[self setWeight:CPLightShadow];
|
_weight = CPLightShadow;
|
||||||
|
|
||||||
|
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
|
||||||
|
|
||||||
[self setHitTests:NO];
|
[self setHitTests:NO];
|
||||||
}
|
}
|
||||||
@@ -115,45 +140,54 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
|
|||||||
_weight = aWeight;
|
_weight = aWeight;
|
||||||
|
|
||||||
if (_weight == CPLightShadow)
|
if (_weight == CPLightShadow)
|
||||||
[self setThemeState:CPThemeStateShadowViewLight];
|
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
|
||||||
else
|
|
||||||
[self setThemeState:CPThemeStateShadowViewHeavy];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
else
|
||||||
|
[self setBackgroundColor:CPShadowViewHeavyBackgroundColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)leftInset
|
- (float)leftInset
|
||||||
{
|
{
|
||||||
return [self currentValueForThemeAttribute:@"content-inset"].left;
|
return _weight == CPLightShadow ? LIGHT_LEFT_INSET : HEAVY_LEFT_INSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)rightInset
|
- (float)rightInset
|
||||||
{
|
{
|
||||||
return [self currentValueForThemeAttribute:@"content-inset"].right;
|
return _weight == CPLightShadow ? LIGHT_RIGHT_INSET : HEAVY_RIGHT_INSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)topInset
|
- (float)topInset
|
||||||
{
|
{
|
||||||
return [self currentValueForThemeAttribute:@"content-inset"].top;
|
return _weight == CPLightShadow ? LIGHT_TOP_INSET : HEAVY_TOP_INSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)bottomInset
|
- (float)bottomInset
|
||||||
{
|
{
|
||||||
return [self currentValueForThemeAttribute:@"content-inset"].bottom;
|
return _weight == CPLightShadow ? LIGHT_BOTTOM_INSET : HEAVY_BOTTOM_INSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)horizontalInset
|
- (float)horizontalInset
|
||||||
{
|
{
|
||||||
var currentContentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
if (_weight == CPLightShadow)
|
||||||
|
return LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET;
|
||||||
|
|
||||||
return currentContentInset.left + currentContentInset.right;
|
return HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)verticalInset
|
- (float)verticalInset
|
||||||
{
|
{
|
||||||
var currentContentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
if (_weight == CPLightShadow)
|
||||||
|
return LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET;
|
||||||
|
|
||||||
return currentContentInset.top + currentContentInset.bottom;
|
return HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (CGRect)frameForContentFrame:(CGRect)aFrame withWeight:(CPShadowWeight)aWeight
|
||||||
|
{
|
||||||
|
if (aWeight == CPLightShadow)
|
||||||
|
return CGRectMake(_CGRectGetMinX(aFrame) - LIGHT_LEFT_INSET, _CGRectGetMinY(aFrame) - LIGHT_TOP_INSET, _CGRectGetWidth(aFrame) + LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET, _CGRectGetHeight(aFrame) + LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET);
|
||||||
|
else
|
||||||
|
return CGRectMake(_CGRectGetMinX(aFrame) - HEAVY_LEFT_INSET, _CGRectGetMinY(aFrame) - HEAVY_TOP_INSET, _CGRectGetWidth(aFrame) + HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET, _CGRectGetHeight(aFrame) + HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)frameForContentFrame:(CGRect)aFrame
|
- (CGRect)frameForContentFrame:(CGRect)aFrame
|
||||||
@@ -166,11 +200,4 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
|
|||||||
[self setFrame:[self frameForContentFrame:aFrame]];
|
[self setFrame:[self frameForContentFrame:aFrame]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
[super layoutSubviews];
|
|
||||||
|
|
||||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+30
-40
@@ -21,7 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPWindow_Constants.j"
|
|
||||||
|
|
||||||
|
|
||||||
/*! SLIDER STATES */
|
/*! SLIDER STATES */
|
||||||
@@ -40,8 +39,6 @@ CPCircularSlider = 1;
|
|||||||
double _altIncrementValue;
|
double _altIncrementValue;
|
||||||
|
|
||||||
BOOL _isVertical;
|
BOOL _isVertical;
|
||||||
|
|
||||||
CGSize _dragOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)defaultThemeClass
|
||||||
@@ -51,12 +48,8 @@ CPCircularSlider = 1;
|
|||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return @{
|
return [CPDictionary dictionaryWithObjects:[[CPNull null], _CGSizeMakeZero(), 0.0, [CPNull null]]
|
||||||
@"knob-color": [CPNull null],
|
forKeys:[@"knob-color", @"knob-size", @"track-width", @"track-color"]];
|
||||||
@"knob-size": CGSizeMakeZero(),
|
|
||||||
@"track-width": 0.0,
|
|
||||||
@"track-color": [CPNull null],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
@@ -162,16 +155,16 @@ CPCircularSlider = 1;
|
|||||||
var trackWidth = [self currentValueForThemeAttribute:@"track-width"];
|
var trackWidth = [self currentValueForThemeAttribute:@"track-width"];
|
||||||
|
|
||||||
if (trackWidth <= 0)
|
if (trackWidth <= 0)
|
||||||
return CGRectMakeZero();
|
return _CGRectMakeZero();
|
||||||
|
|
||||||
if ([self isVertical])
|
if ([self isVertical])
|
||||||
{
|
{
|
||||||
bounds.origin.x = (CGRectGetWidth(bounds) - trackWidth) / 2.0;
|
bounds.origin.x = (_CGRectGetWidth(bounds) - trackWidth) / 2.0;
|
||||||
bounds.size.width = trackWidth;
|
bounds.size.width = trackWidth;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bounds.origin.y = (CGRectGetHeight(bounds) - trackWidth) / 2.0;
|
bounds.origin.y = (_CGRectGetHeight(bounds) - trackWidth) / 2.0;
|
||||||
bounds.size.height = trackWidth;
|
bounds.size.height = trackWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,13 +177,13 @@ CPCircularSlider = 1;
|
|||||||
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])
|
||||||
@@ -203,13 +196,13 @@ 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 = ((_maxValue - [self doubleValue]) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
knobRect.origin.x = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (CGRectGetWidth(trackRect) - knobSize.width);
|
knobRect.origin.x = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetWidth(trackRect) - knobSize.width);
|
||||||
knobRect.origin.y = CGRectGetMidY(trackRect) - knobSize.height / 2.0;
|
knobRect.origin.y = _CGRectGetMidY(trackRect) - knobSize.height / 2.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return knobRect;
|
return knobRect;
|
||||||
@@ -260,8 +253,8 @@ CPCircularSlider = 1;
|
|||||||
{
|
{
|
||||||
// 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);
|
||||||
|
|
||||||
@@ -306,55 +299,55 @@ CPCircularSlider = 1;
|
|||||||
|
|
||||||
if ([self hasThemeState:CPThemeStateCircular])
|
if ([self hasThemeState:CPThemeStateCircular])
|
||||||
{
|
{
|
||||||
var knobWidth = CGRectGetWidth(knobRect);
|
var knobWidth = _CGRectGetWidth(knobRect);
|
||||||
|
|
||||||
trackRect.origin.x += knobWidth / 2;
|
trackRect.origin.x += knobWidth / 2;
|
||||||
trackRect.size.width -= knobWidth;
|
trackRect.size.width -= knobWidth;
|
||||||
|
|
||||||
var minValue = [self minValue],
|
var minValue = [self minValue],
|
||||||
dx = aPoint.x - CGRectGetMidX(trackRect),
|
dx = aPoint.x - _CGRectGetMidX(trackRect),
|
||||||
dy = aPoint.y - CGRectGetMidY(trackRect);
|
dy = aPoint.y - _CGRectGetMidY(trackRect);
|
||||||
|
|
||||||
return MAX(0.0, MIN(1.0, 1.0 - (3 * PI_2 - ATAN2(dy, dx)) % PI2 / PI2)) * ([self maxValue] - minValue) + minValue;
|
return MAX(0.0, MIN(1.0, 1.0 - (3 * PI_2 - ATAN2(dy, dx)) % PI2 / PI2)) * ([self maxValue] - minValue) + minValue;
|
||||||
}
|
}
|
||||||
else if ([self isVertical])
|
else if ([self isVertical])
|
||||||
{
|
{
|
||||||
var knobHeight = CGRectGetHeight(knobRect);
|
var knobHeight = _CGRectGetHeight(knobRect);
|
||||||
|
|
||||||
trackRect.origin.y += knobHeight / 2;
|
trackRect.origin.y += knobHeight / 2;
|
||||||
trackRect.size.height -= knobHeight;
|
trackRect.size.height -= knobHeight;
|
||||||
|
|
||||||
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, (_CGRectGetMaxY(trackRect) - aPoint.y) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var knobWidth = CGRectGetWidth(knobRect);
|
var knobWidth = _CGRectGetWidth(knobRect);
|
||||||
|
|
||||||
trackRect.origin.x += knobWidth / 2;
|
trackRect.origin.x += knobWidth / 2;
|
||||||
trackRect.size.width -= knobWidth;
|
trackRect.size.width -= knobWidth;
|
||||||
|
|
||||||
var minValue = [self minValue];
|
var minValue = [self minValue];
|
||||||
|
|
||||||
return MAX(0.0, MIN(1.0, (aPoint.x - CGRectGetMinX(trackRect)) / CGRectGetWidth(trackRect))) * ([self maxValue] - minValue) + minValue;
|
return MAX(0.0, MIN(1.0, (aPoint.x - _CGRectGetMinX(trackRect)) / _CGRectGetWidth(trackRect))) * ([self maxValue] - minValue) + minValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
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]];
|
||||||
}
|
}
|
||||||
@@ -373,7 +366,7 @@ CPCircularSlider = 1;
|
|||||||
|
|
||||||
- (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;
|
||||||
}
|
}
|
||||||
@@ -389,14 +382,9 @@ CPCircularSlider = 1;
|
|||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isContinuous
|
|
||||||
{
|
|
||||||
return (_sendActionOn & CPLeftMouseDraggedMask) !== 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ignore
|
@ignore
|
||||||
should we have _continuous?
|
shoudl we have _continuous?
|
||||||
*/
|
*/
|
||||||
- (void)setContinuous:(BOOL)flag
|
- (void)setContinuous:(BOOL)flag
|
||||||
{
|
{
|
||||||
@@ -437,6 +425,8 @@ var CPSliderMinValueKey = "CPSliderMinValueKey",
|
|||||||
{
|
{
|
||||||
_altIncrementValue = [aCoder decodeDoubleForKey:CPSliderAltIncrValueKey];
|
_altIncrementValue = [aCoder decodeDoubleForKey:CPSliderAltIncrValueKey];
|
||||||
|
|
||||||
|
[self setContinuous:YES];
|
||||||
|
|
||||||
[self _recalculateIsVertical];
|
[self _recalculateIsVertical];
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
|
|||||||
+156
-172
@@ -20,14 +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 "CPColorPicker.j"
|
|
||||||
@import "CPSlider.j"
|
|
||||||
@import "CPTextField.j"
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
@import "CPColorPicker.j"
|
||||||
@global CPColorPickerViewWidth
|
|
||||||
@global CPColorPickerViewHeight
|
|
||||||
@global CPSliderColorPickerMode
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -66,166 +60,166 @@
|
|||||||
|
|
||||||
- (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
|
||||||
{
|
{
|
||||||
var aFrame = CGRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
||||||
|
|
||||||
_contentView = [[CPView alloc] initWithFrame:aFrame];
|
_contentView = [[CPView alloc] initWithFrame:aFrame];
|
||||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[_contentView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||||
|
|
||||||
_rgbLabel = [[CPTextField alloc] initWithFrame:CGRectMake(0, 10, 100, 20)];
|
_rgbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 10, 100, 20)];
|
||||||
[_rgbLabel setStringValue:"Red, Green, Blue"];
|
[_rgbLabel setStringValue: "Red, Green, Blue"];
|
||||||
[_rgbLabel setTextColor:[CPColor blackColor]];
|
[_rgbLabel setTextColor:[CPColor blackColor]];
|
||||||
|
|
||||||
_redLabel = [[CPTextField alloc] initWithFrame:CGRectMake(0, 37, 15, 20)];
|
_redLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 35, 15, 20)];
|
||||||
[_redLabel setStringValue:"R"];
|
[_redLabel setStringValue: "R"];
|
||||||
[_redLabel setTextColor:[CPColor blackColor]];
|
[_redLabel setTextColor:[CPColor blackColor]];
|
||||||
|
|
||||||
_redSlider = [[CPSlider alloc] initWithFrame:CGRectMake(15, 35, aFrame.size.width - 70, 20)];
|
_redSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 35, aFrame.size.width - 70, 20)];
|
||||||
[_redSlider setMaxValue:1.0];
|
[_redSlider setMaxValue: 1.0];
|
||||||
[_redSlider setMinValue:0.0];
|
[_redSlider setMinValue: 0.0];
|
||||||
[_redSlider setTarget:self];
|
[_redSlider setTarget: self];
|
||||||
[_redSlider setAction:@selector(sliderChanged:)];
|
[_redSlider setAction: @selector(sliderChanged:)];
|
||||||
[_redSlider setAutoresizingMask:CPViewWidthSizable];
|
[_redSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
// red value input box
|
// red value input box
|
||||||
_redValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 30, 45, 28)];
|
_redValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 30, 45, 29)];
|
||||||
[_redValue setAutoresizingMask:CPViewMinXMargin];
|
[_redValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_redValue setEditable:YES];
|
[_redValue setEditable: YES];
|
||||||
[_redValue setBezeled:YES];
|
[_redValue setBezeled: YES];
|
||||||
[_redValue setDelegate:self];
|
[_redValue setDelegate: self];
|
||||||
[_contentView addSubview:_redValue];
|
[_contentView addSubview: _redValue];
|
||||||
|
|
||||||
_greenLabel = [[CPTextField alloc] initWithFrame:CGRectMake(0, 63, 15, 20)];
|
_greenLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 58, 15, 20)];
|
||||||
[_greenLabel setStringValue:"G"];
|
[_greenLabel setStringValue: "G"];
|
||||||
[_greenLabel setTextColor:[CPColor blackColor]];
|
[_greenLabel setTextColor:[CPColor blackColor]];
|
||||||
|
|
||||||
_greenSlider = [[CPSlider alloc] initWithFrame:CGRectMake(15, 61, aFrame.size.width - 70, 20)];
|
_greenSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 58, aFrame.size.width - 70, 20)];
|
||||||
[_greenSlider setMaxValue:1.0];
|
[_greenSlider setMaxValue: 1.0];
|
||||||
[_greenSlider setMinValue:0.0];
|
[_greenSlider setMinValue: 0.0];
|
||||||
[_greenSlider setTarget:self];
|
[_greenSlider setTarget: self];
|
||||||
[_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, 56, 45, 28)];
|
_greenValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 53, 45, 29)];
|
||||||
[_greenValue setAutoresizingMask:CPViewMinXMargin];
|
[_greenValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_greenValue setEditable:YES];
|
[_greenValue setEditable: YES];
|
||||||
[_greenValue setBezeled:YES];
|
[_greenValue setBezeled: YES];
|
||||||
[_greenValue setDelegate:self];
|
[_greenValue setDelegate: self];
|
||||||
[_contentView addSubview:_greenValue];
|
[_contentView addSubview: _greenValue];
|
||||||
|
|
||||||
_blueLabel = [[CPTextField alloc] initWithFrame:CGRectMake(0, 90, 15, 20)];
|
_blueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 81, 15, 20)];
|
||||||
[_blueLabel setStringValue:"B"];
|
[_blueLabel setStringValue: "B"];
|
||||||
[_blueLabel setTextColor:[CPColor blackColor]];
|
[_blueLabel setTextColor:[CPColor blackColor]];
|
||||||
|
|
||||||
_blueSlider = [[CPSlider alloc] initWithFrame:CGRectMake(15, 87, aFrame.size.width - 70, 20)];
|
_blueSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 81, aFrame.size.width - 70, 20)];
|
||||||
[_blueSlider setMaxValue:1.0];
|
[_blueSlider setMaxValue: 1.0];
|
||||||
[_blueSlider setMinValue:0.0];
|
[_blueSlider setMinValue: 0.0];
|
||||||
[_blueSlider setTarget:self];
|
[_blueSlider setTarget: self];
|
||||||
[_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, 82, 45, 28)];
|
_blueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 76, 45, 29)];
|
||||||
[_blueValue setAutoresizingMask:CPViewMinXMargin];
|
[_blueValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_blueValue setEditable:YES];
|
[_blueValue setEditable: YES];
|
||||||
[_blueValue setBezeled:YES];
|
[_blueValue setBezeled: YES];
|
||||||
[_blueValue setDelegate:self];
|
[_blueValue setDelegate: self];
|
||||||
[_contentView addSubview:_blueValue];
|
[_contentView addSubview: _blueValue];
|
||||||
|
|
||||||
_hsbLabel = [[CPTextField alloc] initWithFrame:CGRectMake(0, 120, 190, 20)];
|
_hsbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 120, 190, 20)];
|
||||||
[_hsbLabel setStringValue:"Hue, Saturation, Brightness"];
|
[_hsbLabel setStringValue: "Hue, Saturation, Brightness"];
|
||||||
[_hsbLabel setTextColor:[CPColor blackColor]];
|
[_hsbLabel setTextColor:[CPColor blackColor]];
|
||||||
|
|
||||||
_hueLabel = [[CPTextField alloc] initWithFrame:CGRectMake(0, 145, 15, 20)];
|
_hueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 145, 15, 20)];
|
||||||
[_hueLabel setStringValue:"H"];
|
[_hueLabel setStringValue: "H"];
|
||||||
[_hueLabel setTextColor:[CPColor blackColor]];
|
[_hueLabel setTextColor:[CPColor blackColor]];
|
||||||
|
|
||||||
_hueSlider = [[CPSlider alloc] initWithFrame:CGRectMake(15, 143, aFrame.size.width - 70, 20)];
|
_hueSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 145, aFrame.size.width - 70, 20)];
|
||||||
[_hueSlider setMaxValue:0.999];
|
[_hueSlider setMaxValue: 359.0];
|
||||||
[_hueSlider setMinValue:0.0];
|
[_hueSlider setMinValue: 0.0];
|
||||||
[_hueSlider setTarget:self];
|
[_hueSlider setTarget: self];
|
||||||
[_hueSlider setAction:@selector(sliderChanged:)];
|
[_hueSlider setAction: @selector(sliderChanged:)];
|
||||||
[_hueSlider setAutoresizingMask:CPViewWidthSizable];
|
[_hueSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
// hue value input box
|
// hue value input box
|
||||||
_hueValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 138, 45, 28)];
|
_hueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 140, 45, 29)];
|
||||||
[_hueValue setAutoresizingMask:CPViewMinXMargin];
|
[_hueValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_hueValue setEditable:YES];
|
[_hueValue setEditable: YES];
|
||||||
[_hueValue setBezeled:YES];
|
[_hueValue setBezeled: YES];
|
||||||
[_hueValue setDelegate:self];
|
[_hueValue setDelegate: self];
|
||||||
[_contentView addSubview:_hueValue];
|
[_contentView addSubview: _hueValue];
|
||||||
|
|
||||||
_saturationLabel = [[CPTextField alloc] initWithFrame:CGRectMake(0, 170, 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]];
|
||||||
|
|
||||||
_saturationSlider = [[CPSlider alloc] initWithFrame:CGRectMake(15, 168, aFrame.size.width - 70, 20)];
|
_saturationSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 168, aFrame.size.width - 70, 20)];
|
||||||
[_saturationSlider setMaxValue:1.0];
|
[_saturationSlider setMaxValue: 100.0];
|
||||||
[_saturationSlider setMinValue:0.0];
|
[_saturationSlider setMinValue: 0.0];
|
||||||
[_saturationSlider setTarget:self];
|
[_saturationSlider setTarget: self];
|
||||||
[_saturationSlider setAction:@selector(sliderChanged:)];
|
[_saturationSlider setAction: @selector(sliderChanged:)];
|
||||||
[_saturationSlider setAutoresizingMask:CPViewWidthSizable];
|
[_saturationSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
// saturation value input box
|
// saturation value input box
|
||||||
_saturationValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 164, 45, 28)];
|
_saturationValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 163, 45, 29)];
|
||||||
[_saturationValue setAutoresizingMask:CPViewMinXMargin];
|
[_saturationValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_saturationValue setEditable:YES];
|
[_saturationValue setEditable: YES];
|
||||||
[_saturationValue setBezeled:YES];
|
[_saturationValue setBezeled: YES];
|
||||||
[_saturationValue setDelegate:self];
|
[_saturationValue setDelegate: self];
|
||||||
[_contentView addSubview:_saturationValue];
|
[_contentView addSubview: _saturationValue];
|
||||||
|
|
||||||
_brightnessLabel = [[CPTextField alloc] initWithFrame:CGRectMake(0, 196, 15, 20)];
|
_brightnessLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 191, 15, 20)];
|
||||||
[_brightnessLabel setStringValue:"B"];
|
[_brightnessLabel setStringValue: "B"];
|
||||||
[_brightnessLabel setTextColor:[CPColor blackColor]];
|
[_brightnessLabel setTextColor:[CPColor blackColor]];
|
||||||
|
|
||||||
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(15, 194, aFrame.size.width - 70, 20)];
|
_brightnessSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 191, aFrame.size.width - 70, 20)];
|
||||||
[_brightnessSlider setMaxValue:1.0];
|
[_brightnessSlider setMaxValue: 100.0];
|
||||||
[_brightnessSlider setMinValue:0.0];
|
[_brightnessSlider setMinValue: 0.0];
|
||||||
[_brightnessSlider setTarget:self];
|
[_brightnessSlider setTarget: self];
|
||||||
[_brightnessSlider setAction:@selector(sliderChanged:)];
|
[_brightnessSlider setAction: @selector(sliderChanged:)];
|
||||||
[_brightnessSlider setAutoresizingMask:CPViewWidthSizable];
|
[_brightnessSlider setAutoresizingMask: CPViewWidthSizable];
|
||||||
|
|
||||||
// brightness value input box
|
// brightness value input box
|
||||||
_brightnessValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 190, 45, 28)];
|
_brightnessValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 186, 45, 29)];
|
||||||
[_brightnessValue setAutoresizingMask:CPViewMinXMargin];
|
[_brightnessValue setAutoresizingMask: CPViewMinXMargin];
|
||||||
[_brightnessValue setEditable:YES];
|
[_brightnessValue setEditable: YES];
|
||||||
[_brightnessValue setBezeled:YES];
|
[_brightnessValue setBezeled: YES];
|
||||||
[_brightnessValue setDelegate:self];
|
[_brightnessValue setDelegate: self];
|
||||||
[_contentView addSubview:_brightnessValue];
|
[_contentView addSubview: _brightnessValue];
|
||||||
|
|
||||||
_hexLabel = [[CPTextField alloc] initWithFrame:CGRectMake(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, 28)];
|
_hexValue = [[CPTextField alloc] initWithFrame: CGRectMake(32, 225, 80, 29)];
|
||||||
[_hexValue setEditable:YES];
|
[_hexValue setEditable: YES];
|
||||||
[_hexValue setBezeled:YES];
|
[_hexValue setBezeled: YES];
|
||||||
[_hexValue setDelegate:self];
|
[_hexValue setDelegate: self];
|
||||||
[_contentView addSubview:_hexValue];
|
[_contentView addSubview: _hexValue];
|
||||||
|
|
||||||
[_contentView addSubview:_rgbLabel];
|
[_contentView addSubview: _rgbLabel];
|
||||||
[_contentView addSubview:_redLabel];
|
[_contentView addSubview: _redLabel];
|
||||||
[_contentView addSubview:_greenLabel];
|
[_contentView addSubview: _greenLabel];
|
||||||
[_contentView addSubview:_blueLabel];
|
[_contentView addSubview: _blueLabel];
|
||||||
[_contentView addSubview:_redSlider];
|
[_contentView addSubview: _redSlider];
|
||||||
[_contentView addSubview:_greenSlider];
|
[_contentView addSubview: _greenSlider];
|
||||||
[_contentView addSubview:_blueSlider];
|
[_contentView addSubview: _blueSlider];
|
||||||
|
|
||||||
[_contentView addSubview:_hsbLabel];
|
[_contentView addSubview: _hsbLabel];
|
||||||
[_contentView addSubview:_hueLabel];
|
[_contentView addSubview: _hueLabel];
|
||||||
[_contentView addSubview:_saturationLabel];
|
[_contentView addSubview: _saturationLabel];
|
||||||
[_contentView addSubview:_brightnessLabel];
|
[_contentView addSubview: _brightnessLabel];
|
||||||
[_contentView addSubview:_hueSlider];
|
[_contentView addSubview: _hueSlider];
|
||||||
[_contentView addSubview:_saturationSlider];
|
[_contentView addSubview: _saturationSlider];
|
||||||
[_contentView addSubview:_brightnessSlider];
|
[_contentView addSubview: _brightnessSlider];
|
||||||
|
|
||||||
[_contentView addSubview:_hexLabel];
|
[_contentView addSubview: _hexLabel];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPView)provideNewView:(BOOL)initialRequest
|
- (CPView)provideNewView:(BOOL)initialRequest
|
||||||
@@ -246,52 +240,49 @@
|
|||||||
return (mode == CPSliderColorPickerMode) ? YES : NO;
|
return (mode == CPSliderColorPickerMode) ? YES : NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)sliderChanged:(id)sender
|
-(void)sliderChanged:(id)sender
|
||||||
{
|
{
|
||||||
var newColor,
|
var newColor,
|
||||||
colorPanel = [self colorPanel],
|
colorPanel = [self colorPanel],
|
||||||
alpha = [colorPanel opacity];
|
alpha = [colorPanel opacity];
|
||||||
|
|
||||||
switch (sender)
|
switch(sender)
|
||||||
{
|
{
|
||||||
case _hueSlider:
|
case _hueSlider:
|
||||||
case _saturationSlider:
|
case _saturationSlider:
|
||||||
case _brightnessSlider: newColor = [CPColor colorWithHue:[_hueSlider floatValue]
|
case _brightnessSlider: newColor = [CPColor colorWithHue: [_hueSlider floatValue]
|
||||||
saturation:[_saturationSlider floatValue]
|
saturation: [_saturationSlider floatValue]
|
||||||
brightness:[_brightnessSlider floatValue]
|
brightness: [_brightnessSlider floatValue]
|
||||||
alpha:alpha];
|
alpha: alpha];
|
||||||
|
|
||||||
[self updateRGBSliders:newColor];
|
[self updateRGBSliders: newColor];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _redSlider:
|
case _redSlider:
|
||||||
case _greenSlider:
|
case _greenSlider:
|
||||||
case _blueSlider: newColor = [CPColor colorWithCalibratedRed:[_redSlider floatValue]
|
case _blueSlider: newColor = [CPColor colorWithCalibratedRed: [_redSlider floatValue]
|
||||||
green:[_greenSlider floatValue]
|
green: [_greenSlider floatValue]
|
||||||
blue:[_blueSlider floatValue]
|
blue: [_blueSlider floatValue]
|
||||||
alpha:alpha];
|
alpha: alpha];
|
||||||
|
|
||||||
[self updateHSBSliders:newColor];
|
[self updateHSBSliders: newColor];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self updateLabels];
|
[self updateLabels];
|
||||||
[self updateHex:newColor];
|
[self updateHex: newColor];
|
||||||
[colorPanel setColor:newColor];
|
[colorPanel setColor: newColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setColor:(CPColor)aColor
|
-(void)setColor:(CPColor)aColor
|
||||||
{
|
{
|
||||||
if (!aColor)
|
[self updateRGBSliders: aColor];
|
||||||
[CPException raise:CPInvalidArgumentException reason:"aColor can't be nil"];
|
[self updateHSBSliders: aColor];
|
||||||
|
[self updateHex: aColor];
|
||||||
[self updateRGBSliders:aColor];
|
|
||||||
[self updateHSBSliders:aColor];
|
|
||||||
[self updateHex:aColor];
|
|
||||||
[self updateLabels];
|
[self updateLabels];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateHSBSliders:(CPColor)aColor
|
-(void)updateHSBSliders:(CPColor)aColor
|
||||||
{
|
{
|
||||||
var hsb = [aColor hsbComponents];
|
var hsb = [aColor hsbComponents];
|
||||||
|
|
||||||
@@ -316,13 +307,13 @@
|
|||||||
|
|
||||||
- (void)updateLabels
|
- (void)updateLabels
|
||||||
{
|
{
|
||||||
[_hueValue setStringValue:ROUND([_hueSlider floatValue] * 360.0)];
|
[_hueValue setStringValue: ROUND([_hueSlider floatValue])];
|
||||||
[_saturationValue setStringValue:ROUND([_saturationSlider floatValue] * 100.0)];
|
[_saturationValue setStringValue: ROUND([_saturationSlider floatValue])];
|
||||||
[_brightnessValue setStringValue:ROUND([_brightnessSlider floatValue] * 100.0)];
|
[_brightnessValue setStringValue: ROUND([_brightnessSlider floatValue])];
|
||||||
|
|
||||||
[_redValue setStringValue:ROUND([_redSlider floatValue] * 255)];
|
[_redValue setStringValue: ROUND([_redSlider floatValue] * 255)];
|
||||||
[_greenValue setStringValue:ROUND([_greenSlider floatValue] * 255)];
|
[_greenValue setStringValue: ROUND([_greenSlider floatValue] * 255)];
|
||||||
[_blueValue setStringValue:ROUND([_blueSlider floatValue] * 255)];
|
[_blueValue setStringValue: ROUND([_blueSlider floatValue] * 255)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPImage)provideNewButtonImage
|
- (CPImage)provideNewButtonImage
|
||||||
@@ -339,43 +330,36 @@
|
|||||||
{
|
{
|
||||||
var field = [aNotification object],
|
var field = [aNotification object],
|
||||||
value = [[field stringValue] stringByTrimmingWhitespace];
|
value = [[field stringValue] stringByTrimmingWhitespace];
|
||||||
|
if (field === _hexValue) {
|
||||||
if (field === _hexValue)
|
var newColor = [CPColor colorWithHexString: value];
|
||||||
{
|
if (newColor) {
|
||||||
var newColor = [CPColor colorWithHexString:value];
|
[self setColor: newColor];
|
||||||
|
[[self colorPanel] setColor: newColor];
|
||||||
if (newColor)
|
|
||||||
{
|
|
||||||
[self setColor:newColor];
|
|
||||||
[[self colorPanel] setColor:newColor];
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
switch(field) {
|
||||||
{
|
|
||||||
switch (field)
|
|
||||||
{
|
|
||||||
case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
||||||
[self sliderChanged:_redSlider];
|
[self sliderChanged: _redSlider];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
||||||
[self sliderChanged:_greenSlider];
|
[self sliderChanged: _greenSlider];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
||||||
[self sliderChanged:_blueSlider];
|
[self sliderChanged: _blueSlider];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(value), 360) / 360.0, 0)];
|
case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(value), 360), 0)];
|
||||||
[self sliderChanged:_hueSlider];
|
[self sliderChanged: _hueSlider];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(value), 100) / 100.0, 0)];
|
case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
|
||||||
[self sliderChanged:_saturationSlider];
|
[self sliderChanged: _saturationSlider];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(value), 100) / 100.0, 0)];
|
case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
|
||||||
[self sliderChanged:_brightnessSlider];
|
[self sliderChanged: _brightnessSlider];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,318 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPSound.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Antoine Mercadal
|
|
||||||
* Copyright 2010, Antoine Mercadal
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
@import <Foundation/CPRunLoop.j>
|
|
||||||
|
|
||||||
CPSoundLoadStateEmpty = 0;
|
|
||||||
CPSoundLoadStateLoading = 1;
|
|
||||||
CPSoundLoadStateCanBePlayed = 2;
|
|
||||||
CPSoundLoadStateError = 3;
|
|
||||||
|
|
||||||
CPSoundPlayBackStatePlay = 0;
|
|
||||||
CPSoundPlayBackStateStop = 1;
|
|
||||||
CPSoundPlayBackStatePause = 2;
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
CPSound provides a way to load and play sounds. In the browser it relies on the
|
|
||||||
HTML5 audio tag being available.
|
|
||||||
|
|
||||||
CPSound delegate:
|
|
||||||
- sound:didFinishPlaying: called when sound has finished to played
|
|
||||||
|
|
||||||
*/
|
|
||||||
@implementation CPSound : CPObject
|
|
||||||
{
|
|
||||||
CPString _name @accessors(property=name);
|
|
||||||
id _delegate @accessors(property=delegate);
|
|
||||||
|
|
||||||
BOOL _playRequestBeforeLoad;
|
|
||||||
HTMLAudioElement _audioTag;
|
|
||||||
int _loadStatus;
|
|
||||||
int _playBackStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Initialization
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_loadStatus = CPSoundLoadStateEmpty;
|
|
||||||
// _loops = NO;
|
|
||||||
_audioTag = document.createElement("audio");
|
|
||||||
_audioTag.preload = YES;
|
|
||||||
_playRequestBeforeLoad = NO;
|
|
||||||
|
|
||||||
_audioTag.addEventListener("canplay", function()
|
|
||||||
{
|
|
||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
|
||||||
[self _soundDidload];
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
_audioTag.addEventListener("ended", function()
|
|
||||||
{
|
|
||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
|
||||||
[self _soundDidEnd];
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
_audioTag.addEventListener("error", function()
|
|
||||||
{
|
|
||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
|
||||||
[self _soundError];
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initialize with the sound contents of the URL represented by aFile.
|
|
||||||
|
|
||||||
@param aFile CPString the path of the sound
|
|
||||||
@param byRef ignored (Cocoa compatibility)
|
|
||||||
*/
|
|
||||||
- (id)initWithContentsOfFile:(CPString)aFile byReference:(BOOL)byRef
|
|
||||||
{
|
|
||||||
if (self = [self init])
|
|
||||||
{
|
|
||||||
_loadStatus = CPSoundLoadStateLoading;
|
|
||||||
_audioTag.src = aFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initialize with the sound contents of the file located at aURL.
|
|
||||||
|
|
||||||
@param aURL CPURL containing the URL of the sound
|
|
||||||
@param byRef ignored (Cocoa compatibility)
|
|
||||||
*/
|
|
||||||
- (id)initWithContentsOfURL:(CPURL)aURL byReference:(BOOL)byRef
|
|
||||||
{
|
|
||||||
return [self initWithContentsOfFile:[aURL absoluteString] byReference:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initialize with the sound contents of someData.
|
|
||||||
|
|
||||||
@param someData CPData containing the sound
|
|
||||||
@param byRef ignored (Cocoa compatibility)
|
|
||||||
*/
|
|
||||||
- (id)initWithData:(CPData)someData
|
|
||||||
{
|
|
||||||
if (self = [self init])
|
|
||||||
{
|
|
||||||
_loadStatus = CPSoundLoadStateLoading;
|
|
||||||
_audioTag.src = [someData rawString];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Events listener
|
|
||||||
|
|
||||||
/*! @ignore
|
|
||||||
*/
|
|
||||||
- (void)_soundDidload
|
|
||||||
{
|
|
||||||
_loadStatus = CPSoundLoadStateCanBePlayed;
|
|
||||||
|
|
||||||
if (_playRequestBeforeLoad)
|
|
||||||
{
|
|
||||||
_playRequestBeforeLoad = NO;
|
|
||||||
[self play];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @ignore
|
|
||||||
*/
|
|
||||||
- (void)_soundDidEnd
|
|
||||||
{
|
|
||||||
if (![self loops])
|
|
||||||
[self stop];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @ignore
|
|
||||||
*/
|
|
||||||
- (void)_soundError
|
|
||||||
{
|
|
||||||
_loadStatus = CPSoundLoadStateError;
|
|
||||||
CPLog.error("Cannot load sound. Maybe the format of your sound is not compatible with your browser.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Media controls
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Play the sound.
|
|
||||||
|
|
||||||
@return YES when the receiver is playing its audio data, NO otherwise.
|
|
||||||
*/
|
|
||||||
- (BOOL)play
|
|
||||||
{
|
|
||||||
if (_loadStatus === CPSoundLoadStateLoading)
|
|
||||||
{
|
|
||||||
_playRequestBeforeLoad = YES;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|
|
||||||
|| (_playBackStatus === CPSoundPlayBackStatePlay))
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
_audioTag.play();
|
|
||||||
_playBackStatus = CPSoundPlayBackStatePlay;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Stop the sound.
|
|
||||||
|
|
||||||
@return YES when the receiver is playing its audio data, NO otherwise.
|
|
||||||
*/
|
|
||||||
- (BOOL)stop
|
|
||||||
{
|
|
||||||
if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|
|
||||||
|| (_playBackStatus === CPSoundPlayBackStateStop))
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
_audioTag.pause();
|
|
||||||
_audioTag.currentTime = 0.0;
|
|
||||||
_playBackStatus = CPSoundPlayBackStateStop;
|
|
||||||
|
|
||||||
if (_delegate && [_delegate respondsToSelector:@selector(sound:didFinishPlaying:)])
|
|
||||||
[_delegate sound:self didFinishPlaying:YES];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Pause the sound.
|
|
||||||
|
|
||||||
@return YES when the receiver is playing its audio data, NO otherwise.
|
|
||||||
*/
|
|
||||||
- (BOOL)pause
|
|
||||||
{
|
|
||||||
if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|
|
||||||
|| (_playBackStatus === CPSoundPlayBackStatePause))
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
_audioTag.pause();
|
|
||||||
_playBackStatus = CPSoundPlayBackStatePause;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Resume playback of a paused sound.
|
|
||||||
|
|
||||||
@return YES when the receiver is playing its audio data, NO otherwise.
|
|
||||||
*/
|
|
||||||
- (BOOL)resume
|
|
||||||
{
|
|
||||||
if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|
|
||||||
|| (_playBackStatus !== CPSoundPlayBackStatePause))
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
_audioTag.play();
|
|
||||||
_playBackStatus = CPSoundPlayBackStatePlay;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Return YES if the sound is in loop mode.
|
|
||||||
|
|
||||||
@return BOOL YES if in loop mode, NO otherwise
|
|
||||||
*/
|
|
||||||
- (BOOL)loops
|
|
||||||
{
|
|
||||||
return _audioTag.loop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Specifies whether the sound should repeat.
|
|
||||||
|
|
||||||
@param BOOL YES for loop mode, NO otherwise
|
|
||||||
*/
|
|
||||||
- (void)setLoops:(BOOL)shouldLoop
|
|
||||||
{
|
|
||||||
_audioTag.loop = shouldLoop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the volume of the receiver.
|
|
||||||
|
|
||||||
@return double from 0.0 to 1.0
|
|
||||||
*/
|
|
||||||
- (double)volume
|
|
||||||
{
|
|
||||||
return _audioTag.volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the volume the sound should be played at.
|
|
||||||
|
|
||||||
@param double a volume value between 0.0 and 1.0
|
|
||||||
*/
|
|
||||||
- (void)setVolume:(double)aVolume
|
|
||||||
{
|
|
||||||
if (aVolume > 1.0)
|
|
||||||
aVolume = 1.0;
|
|
||||||
else if (aVolume < 0.0)
|
|
||||||
aVolume = 0.0;
|
|
||||||
|
|
||||||
_audioTag.volume = aVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Accessors
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the duration in seconds of the sound.
|
|
||||||
|
|
||||||
@return double the duration
|
|
||||||
*/
|
|
||||||
- (double)duration
|
|
||||||
{
|
|
||||||
return _audioTag.duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns if the sound is playing or not.
|
|
||||||
|
|
||||||
@return BOOL YES if the sound is playing, NO otherwise
|
|
||||||
*/
|
|
||||||
- (BOOL)isPlaying
|
|
||||||
{
|
|
||||||
return (_playBackStatus === CPSoundPlayBackStatePlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+109
-629
File diff suppressed because it is too large
Load Diff
@@ -1,340 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPStepper.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Antoine Mercadal
|
|
||||||
* Copyright 2009, Antoine Mercadal
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "CPControl.j"
|
|
||||||
@import "CPButton.j"
|
|
||||||
@import "CPTextField.j"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
CPStepper is an implementation of Cocoa NSStepper.
|
|
||||||
|
|
||||||
This control displays a two part button that can be used to increase or decrease a value with a given interval.
|
|
||||||
*/
|
|
||||||
@implementation CPStepper: CPControl
|
|
||||||
{
|
|
||||||
BOOL _valueWraps @accessors(property=valueWraps);
|
|
||||||
BOOL _autorepeat @accessors(getter=autorepeat);
|
|
||||||
int _increment @accessors(property=increment);
|
|
||||||
int _maxValue @accessors(property=maxValue);
|
|
||||||
int _minValue @accessors(property=minValue);
|
|
||||||
|
|
||||||
CPButton _buttonDown;
|
|
||||||
CPButton _buttonUp;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Initialization
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes a CPStepper with given values.
|
|
||||||
@param aValue the initial value of the CPStepper
|
|
||||||
@param minValue the minimal acceptable value of the stepper
|
|
||||||
@param maxValue the maximal acceptable value of the stepper
|
|
||||||
@return Initialized CPStepper
|
|
||||||
*/
|
|
||||||
+ (CPStepper)stepperWithInitialValue:(float)aValue minValue:(float)aMinValue maxValue:(float)aMaxValue
|
|
||||||
{
|
|
||||||
var stepper = [[CPStepper alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
[stepper setDoubleValue:aValue];
|
|
||||||
[stepper setMinValue:aMinValue];
|
|
||||||
[stepper setMaxValue:aMaxValue];
|
|
||||||
|
|
||||||
// _sizeToFit will put the good size for the stepper depending of the current theme
|
|
||||||
[stepper _sizeToFit];
|
|
||||||
|
|
||||||
return stepper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes a CPStepper with default values:
|
|
||||||
- minValue = 0.0
|
|
||||||
- maxValue = 59.0
|
|
||||||
- value = 0.0
|
|
||||||
|
|
||||||
@return Initialized CPStepper
|
|
||||||
*/
|
|
||||||
+ (CPStepper)stepper
|
|
||||||
{
|
|
||||||
return [CPStepper stepperWithInitialValue:0.0 minValue:0.0 maxValue:59.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding == CPValueBinding || aBinding == CPMinValueBinding || aBinding == CPMaxValueBinding)
|
|
||||||
return [_CPStepperValueBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPString)_replacementKeyPathForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding == CPValueBinding)
|
|
||||||
return @"doubleValue";
|
|
||||||
|
|
||||||
return [super _replacementKeyPathForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes a CPStepper.
|
|
||||||
@param aFrame the frame of the control
|
|
||||||
@return initialized CPStepper
|
|
||||||
*/
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
{
|
|
||||||
_maxValue = 59.0;
|
|
||||||
_minValue = 0.0;
|
|
||||||
_increment = 1.0;
|
|
||||||
_valueWraps = YES;
|
|
||||||
_autorepeat = YES;
|
|
||||||
|
|
||||||
[self setDoubleValue:0.0];
|
|
||||||
[self _init];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
_buttonUp = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[_buttonUp setContinuous:_autorepeat];
|
|
||||||
[_buttonUp setTarget:self];
|
|
||||||
[_buttonUp setAction:@selector(_buttonDidClick:)];
|
|
||||||
[_buttonUp setAutoresizingMask:CPViewNotSizable];
|
|
||||||
[self addSubview:_buttonUp];
|
|
||||||
|
|
||||||
_buttonDown = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[_buttonDown setContinuous:_autorepeat];
|
|
||||||
[_buttonDown setTarget:self];
|
|
||||||
[_buttonDown setAction:@selector(_buttonDidClick:)];
|
|
||||||
[_buttonDown setAutoresizingMask:CPViewNotSizable];
|
|
||||||
|
|
||||||
[self setContinuous:_autorepeat];
|
|
||||||
[self addSubview:_buttonDown];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Superclass overrides
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set if the CPStepper is enabled or not.
|
|
||||||
@param shouldEnabled BOOL that define if stepper is enabled or not.
|
|
||||||
*/
|
|
||||||
- (void)setEnabled:(BOOL)shouldEnabled
|
|
||||||
{
|
|
||||||
[super setEnabled:shouldEnabled];
|
|
||||||
|
|
||||||
[_buttonUp setEnabled:shouldEnabled];
|
|
||||||
[_buttonDown setEnabled:shouldEnabled];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)setFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
var upSize = [self valueForThemeAttribute:@"up-button-size"],
|
|
||||||
downSize = [self valueForThemeAttribute:@"down-button-size"],
|
|
||||||
minSize = CGSizeMake(upSize.width, upSize.height + downSize.height),
|
|
||||||
frame = CGRectMakeCopy(aFrame);
|
|
||||||
|
|
||||||
frame.size.width = MAX(minSize.width, frame.size.width);
|
|
||||||
frame.size.height = MAX(minSize.height, frame.size.height);
|
|
||||||
[super setFrame:frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
var aFrame = [self frame],
|
|
||||||
upSize = [self valueForThemeAttribute:@"up-button-size"],
|
|
||||||
downSize = [self valueForThemeAttribute:@"down-button-size"],
|
|
||||||
upFrame = CGRectMake(aFrame.size.width - upSize.width, 0, upSize.width, upSize.height),
|
|
||||||
downFrame = CGRectMake(aFrame.size.width - downSize.width, upSize.height, downSize.width, downSize.height);
|
|
||||||
|
|
||||||
[_buttonUp setFrame:upFrame];
|
|
||||||
[_buttonDown setFrame:downFrame];
|
|
||||||
|
|
||||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
|
||||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
|
|
||||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
|
|
||||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
|
||||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
|
|
||||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_sizeToFit
|
|
||||||
{
|
|
||||||
[self setFrame:CGRectMake([self frameOrigin].x, [self frameOrigin].y, 0, 0)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set if CPStepper should autorepeat.
|
|
||||||
@param shouldAutoRepeat if YES, the first mouse down does one increment (decrement) and, after each delay of 0.5 seconds
|
|
||||||
*/
|
|
||||||
- (void)setAutorepeat:(BOOL)shouldAutoRepeat
|
|
||||||
{
|
|
||||||
if (shouldAutoRepeat !== _autorepeat)
|
|
||||||
{
|
|
||||||
[_buttonUp setContinuous:shouldAutoRepeat];
|
|
||||||
[_buttonDown setContinuous:shouldAutoRepeat];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setContinuous:shouldAutoRepeat];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the current value of the stepper.
|
|
||||||
@param aValue a float containing the value
|
|
||||||
*/
|
|
||||||
- (void)setDoubleValue:(float)aValue
|
|
||||||
{
|
|
||||||
if (aValue > _maxValue)
|
|
||||||
[super setDoubleValue:_valueWraps ? _minValue : _maxValue];
|
|
||||||
else if (aValue < _minValue)
|
|
||||||
[super setDoubleValue:_valueWraps ? _maxValue : _minValue];
|
|
||||||
else
|
|
||||||
[super setDoubleValue:aValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Actions
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
- (IBAction)_buttonDidClick:(id)aSender
|
|
||||||
{
|
|
||||||
if (![self isEnabled])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (aSender == _buttonUp)
|
|
||||||
[self setDoubleValue:([self doubleValue] + _increment)];
|
|
||||||
else
|
|
||||||
[self setDoubleValue:([self doubleValue] - _increment)];
|
|
||||||
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Perform a programatic click on up button.
|
|
||||||
@param aSender sender of the action
|
|
||||||
*/
|
|
||||||
- (IBAction)performClickUp:(id)aSender
|
|
||||||
{
|
|
||||||
[_buttonUp performClick:aSender];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Perform a programatic click on down button.
|
|
||||||
@param aSender sender of the action
|
|
||||||
*/
|
|
||||||
- (IBAction)performClickDown:(id)aSender
|
|
||||||
{
|
|
||||||
[_buttonDown performClick:aSender];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Theming
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
|
||||||
{
|
|
||||||
return @"stepper";
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
|
||||||
{
|
|
||||||
return @{
|
|
||||||
@"bezel-color-up-button": [CPNull null],
|
|
||||||
@"bezel-color-down-button": [CPNull null],
|
|
||||||
@"up-button-size": CGSizeMakeZero(),
|
|
||||||
@"down-button-size": CGSizeMakeZero(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPStepperValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var placeholder = (aBinding == CPMaxValueBinding) ? [_source maxValue] : [_source minValue];
|
|
||||||
|
|
||||||
[super _updatePlaceholdersWithOptions:options];
|
|
||||||
|
|
||||||
[self _setPlaceholder:placeholder forMarker:CPMultipleValuesMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:placeholder forMarker:CPNoSelectionMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:placeholder forMarker:CPNotApplicableMarker isDefault:YES];
|
|
||||||
[self _setPlaceholder:placeholder forMarker:CPNullMarker isDefault:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPStepperMinValue = @"CPStepperMinValue",
|
|
||||||
CPStepperMaxValue = @"CPStepperMaxValue",
|
|
||||||
CPStepperValueWraps = @"CPStepperValueWraps",
|
|
||||||
CPStepperAutorepeat = @"CPStepperAutorepeat",
|
|
||||||
CPStepperIncrement = @"CPStepperIncrement";
|
|
||||||
|
|
||||||
@implementation CPStepper (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if (self = [super initWithCoder:aCoder])
|
|
||||||
{
|
|
||||||
_increment = [aCoder decodeIntForKey:CPStepperIncrement];
|
|
||||||
_minValue = [aCoder decodeIntForKey:CPStepperMinValue];
|
|
||||||
_maxValue = [aCoder decodeIntForKey:CPStepperMaxValue];
|
|
||||||
_valueWraps = [aCoder decodeBoolForKey:CPStepperValueWraps];
|
|
||||||
_autorepeat = [aCoder decodeBoolForKey:CPStepperAutorepeat];
|
|
||||||
|
|
||||||
[self _init];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeInt:_increment forKey:CPStepperIncrement];
|
|
||||||
|
|
||||||
if (_minValue)
|
|
||||||
[aCoder encodeInt:_minValue forKey:CPStepperMinValue];
|
|
||||||
if (_maxValue)
|
|
||||||
[aCoder encodeInt:_maxValue forKey:CPStepperMaxValue];
|
|
||||||
if (_valueWraps)
|
|
||||||
[aCoder encodeBool:_valueWraps forKey:CPStepperValueWraps];
|
|
||||||
if (_autorepeat)
|
|
||||||
[aCoder encodeBool:_autorepeat forKey:CPStepperAutorepeat];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
|
|
||||||
@import "CGGeometry.j"
|
|
||||||
@import "CPPlatformString.j"
|
@import "CPPlatformString.j"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+146
-252
@@ -1,97 +1,85 @@
|
|||||||
/*
|
|
||||||
* CPTabView.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Derek Hammer.
|
|
||||||
* Copyright 2010, Derek Hammer.
|
|
||||||
*
|
|
||||||
* 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 "CPBox.j"
|
@import "CPBox.j"
|
||||||
@import "CPSegmentedControl.j"
|
@import "CPSegmentedControl.j"
|
||||||
@import "CPTabViewItem.j"
|
@import "CPTabViewItem.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Places tabs on top with a bezeled border.
|
||||||
|
@global
|
||||||
|
@group CPTabViewType
|
||||||
|
*/
|
||||||
CPTopTabsBezelBorder = 0;
|
CPTopTabsBezelBorder = 0;
|
||||||
//CPLeftTabsBezelBorder = 1;
|
//CPLeftTabsBezelBorder = 1;
|
||||||
CPBottomTabsBezelBorder = 2;
|
CPBottomTabsBezelBorder = 2;
|
||||||
//CPRightTabsBezelBorder = 3;
|
//CPRightTabsBezelBorder = 3;
|
||||||
CPNoTabsBezelBorder = 4; //Displays no tabs and has a bezeled border.
|
/*
|
||||||
CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
|
Displays no tabs and has a bezeled border.
|
||||||
CPNoTabsNoBorder = 6; //Displays no tabs and no border.
|
@global
|
||||||
|
@group CPTabViewType
|
||||||
|
*/
|
||||||
|
CPNoTabsBezelBorder = 4;
|
||||||
|
/*
|
||||||
|
Has no tabs and displays a line border.
|
||||||
|
@global
|
||||||
|
@group CPTabViewType
|
||||||
|
*/
|
||||||
|
CPNoTabsLineBorder = 5;
|
||||||
|
/*
|
||||||
|
Displays no tabs and no border.
|
||||||
|
@global
|
||||||
|
@group CPTabViewType
|
||||||
|
*/
|
||||||
|
CPNoTabsNoBorder = 6;
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
A CPTabView object presents a tabbed interface where each page is one a
|
|
||||||
complete view hiearchy of its own. The user can navigate between various
|
|
||||||
pages by clicking on the tab headers.
|
|
||||||
*/
|
|
||||||
@implementation CPTabView : CPView
|
@implementation CPTabView : CPView
|
||||||
{
|
{
|
||||||
CPArray _items;
|
CPArray items;
|
||||||
|
|
||||||
CPSegmentedControl _tabs;
|
CPSegmentedControl tabs;
|
||||||
CPBox _box;
|
CPBox box;
|
||||||
|
|
||||||
CPNumber _selectedIndex;
|
CPNumber selectedIndex;
|
||||||
|
|
||||||
CPTabViewType _type;
|
CPTabViewType type;
|
||||||
CPFont _font;
|
|
||||||
|
|
||||||
id _delegate;
|
id delegate;
|
||||||
unsigned _delegateSelectors;
|
unsigned delegateSelectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
if (self = [super initWithFrame:aFrame])
|
self = [super initWithFrame:aFrame];
|
||||||
|
if (self)
|
||||||
{
|
{
|
||||||
_items = [CPArray array];
|
items = [CPArray array];
|
||||||
|
|
||||||
|
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 _init];
|
|
||||||
[self setTabViewType:CPTopTabsBezelBorder];
|
[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)_init
|
|
||||||
{
|
|
||||||
_selectedIndex = CPNotFound;
|
|
||||||
|
|
||||||
_tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
|
||||||
[_tabs setHitTests:NO];
|
|
||||||
|
|
||||||
var height = [_tabs valueForThemeAttribute:@"default-height"];
|
|
||||||
[_tabs setFrameSize:CGSizeMake(0, height)];
|
|
||||||
|
|
||||||
_box = [[CPBox alloc] initWithFrame:[self bounds]];
|
|
||||||
[self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
|
|
||||||
|
|
||||||
[self addSubview:_box];
|
|
||||||
[self addSubview:_tabs];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding and Removing Tabs
|
// Adding and Removing Tabs
|
||||||
/*!
|
/*!
|
||||||
Adds a CPTabViewItem to the tab view.
|
Adds a CPTabViewItem to the tab view.
|
||||||
@@ -99,25 +87,24 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
*/
|
*/
|
||||||
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
|
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
{
|
{
|
||||||
[self insertTabViewItem:aTabViewItem atIndex:[_items count]];
|
[self insertTabViewItem:aTabViewItem atIndex:[items count]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Inserts a CPTabViewItem into the tab view at the specified index.
|
Inserts a CPTabViewItem into the tab view
|
||||||
|
at the specified index.
|
||||||
@param aTabViewItem the item to insert
|
@param aTabViewItem the item to insert
|
||||||
@param anIndex the index for the item
|
@param anIndex the index for the item
|
||||||
*/
|
*/
|
||||||
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex
|
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
[_items insertObject:aTabViewItem atIndex:anIndex];
|
[items insertObject:aTabViewItem atIndex:anIndex];
|
||||||
|
|
||||||
[self _updateItems];
|
[self _updateItems];
|
||||||
[self _repositionTabs];
|
[self _repositionTabs];
|
||||||
|
|
||||||
[aTabViewItem _setTabView:self];
|
if (delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||||
|
[delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
|
||||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -126,45 +113,39 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
*/
|
*/
|
||||||
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
|
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
{
|
{
|
||||||
var count = [_items count];
|
for (var i = 0; i < [items count]; i++)
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
{
|
||||||
if ([_items objectAtIndex:i] === aTabViewItem)
|
if ([items objectAtIndex:i] === aTabViewItem)
|
||||||
{
|
{
|
||||||
[_items removeObjectAtIndex:i];
|
[items removeObjectAtIndex:i];
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _updateItems];
|
[self _updateItems];
|
||||||
[self _repositionTabs];
|
[self _repositionTabs];
|
||||||
|
|
||||||
[aTabViewItem _setTabView:nil];
|
if (delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||||
|
[delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
|
||||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessing Tabs
|
// Accessing Tabs
|
||||||
/*!
|
/*!
|
||||||
Returns the index of the specified item
|
Returns the index of the specified item
|
||||||
@param aTabViewItem the item to find the index for
|
@param aTabViewItem the item to find the index for
|
||||||
@return the index of aTabViewItem or CPNotFound
|
|
||||||
*/
|
*/
|
||||||
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
|
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
|
||||||
{
|
{
|
||||||
return [_items indexOfObjectIdenticalTo:aTabViewItem];
|
return [items indexOfObjectIdenticalTo:aTabViewItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the index of the CPTabViewItem with the specified identifier.
|
Returns the index of the CPTabViewItem with the specified identifier.
|
||||||
@param anIdentifier the identifier of the item
|
@param anIdentifier the identifier of the item
|
||||||
@return the index of the tab view item identified by anIdentifier, or CPNotFound
|
|
||||||
*/
|
*/
|
||||||
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
|
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
|
||||||
{
|
{
|
||||||
for (var index = [_items count]; index >= 0; index--)
|
for (var index = [items count]; index >= 0; index--)
|
||||||
if ([[_items[index] identifier] isEqual:anIdentifier])
|
if ([[items[index] identifier] isEqual:anIdentifier])
|
||||||
return index;
|
return index;
|
||||||
|
|
||||||
return CPNotFound;
|
return CPNotFound;
|
||||||
@@ -172,29 +153,26 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the number of items in the tab view.
|
Returns the number of items in the tab view.
|
||||||
@return the number of tab view items in the receiver
|
|
||||||
*/
|
*/
|
||||||
- (unsigned)numberOfTabViewItems
|
- (unsigned)numberOfTabViewItems
|
||||||
{
|
{
|
||||||
return [_items count];
|
return [items count];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the CPTabViewItem at the specified index.
|
Returns the CPTabViewItem at the specified index.
|
||||||
@return a tab view item, or nil
|
|
||||||
*/
|
*/
|
||||||
- (CPTabViewItem)tabViewItemAtIndex:(unsigned)anIndex
|
- (CPTabViewItem)tabViewItemAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
return [_items objectAtIndex:anIndex];
|
return [items objectAtIndex:anIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the array of items that backs this tab view.
|
Returns the array of items that backs this tab view.
|
||||||
@return a copy of the array of items in the receiver
|
|
||||||
*/
|
*/
|
||||||
- (CPArray)tabViewItems
|
- (CPArray)tabViewItems
|
||||||
{
|
{
|
||||||
return [_items copy]; // Copy?
|
return [items copy]; // Copy?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selecting a Tab
|
// Selecting a Tab
|
||||||
@@ -204,7 +182,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
*/
|
*/
|
||||||
- (void)selectFirstTabViewItem:(id)aSender
|
- (void)selectFirstTabViewItem:(id)aSender
|
||||||
{
|
{
|
||||||
if ([_items count] === 0)
|
if ([items count] === 0)
|
||||||
return; // throw?
|
return; // throw?
|
||||||
|
|
||||||
[self selectTabViewItemAtIndex:0];
|
[self selectTabViewItemAtIndex:0];
|
||||||
@@ -216,10 +194,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
*/
|
*/
|
||||||
- (void)selectLastTabViewItem:(id)aSender
|
- (void)selectLastTabViewItem:(id)aSender
|
||||||
{
|
{
|
||||||
if ([_items count] === 0)
|
if ([items count] === 0)
|
||||||
return; // throw?
|
return; // throw?
|
||||||
|
|
||||||
[self selectTabViewItemAtIndex:[_items count] - 1];
|
[self selectTabViewItemAtIndex:[items count] - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -228,12 +206,12 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
*/
|
*/
|
||||||
- (void)selectNextTabViewItem:(id)aSender
|
- (void)selectNextTabViewItem:(id)aSender
|
||||||
{
|
{
|
||||||
if (_selectedIndex === CPNotFound)
|
if (selectedIndex === CPNotFound)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var nextIndex = _selectedIndex + 1;
|
var nextIndex = selectedIndex + 1;
|
||||||
|
|
||||||
if (nextIndex === [_items 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:)
|
// 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:)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -246,10 +224,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
*/
|
*/
|
||||||
- (void)selectPreviousTabViewItem:(id)aSender
|
- (void)selectPreviousTabViewItem:(id)aSender
|
||||||
{
|
{
|
||||||
if (_selectedIndex === CPNotFound)
|
if (selectedIndex === CPNotFound)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var previousIndex = _selectedIndex - 1;
|
var previousIndex = selectedIndex - 1;
|
||||||
|
|
||||||
if (previousIndex < 0)
|
if (previousIndex < 0)
|
||||||
return; // does nothing. See above.
|
return; // does nothing. See above.
|
||||||
@@ -270,61 +248,34 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
Selects the item at the specified index.
|
Selects the item at the specified index.
|
||||||
@param anIndex the index of the item to display.
|
@param anIndex the index of the item to display.
|
||||||
*/
|
*/
|
||||||
- (BOOL)selectTabViewItemAtIndex:(unsigned)anIndex
|
- (void)selectTabViewItemAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
if (anIndex === _selectedIndex)
|
var aTabViewItem = [items objectAtIndex:anIndex];
|
||||||
|
|
||||||
|
if (anIndex === selectedIndex)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
|
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
|
||||||
|
|
||||||
if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
|
if ((delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
|
||||||
return NO;
|
return;
|
||||||
|
|
||||||
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
if (delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
||||||
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
[delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
||||||
|
|
||||||
[_tabs selectSegmentWithTag:anIndex];
|
[tabs selectSegmentWithTag:anIndex];
|
||||||
[self _setSelectedIndex:anIndex];
|
[self _setSelectedIndex:anIndex];
|
||||||
|
|
||||||
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
if (delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
||||||
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
[delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the current item being displayed.
|
Returns the current item being displayed.
|
||||||
@return the tab view item currenly being displayed by the receiver
|
|
||||||
*/
|
*/
|
||||||
- (CPTabViewItem)selectedTabViewItem
|
- (CPTabViewItem)selectedTabViewItem
|
||||||
{
|
{
|
||||||
if (_selectedIndex != CPNotFound)
|
return [items objectAtIndex:selectedIndex];
|
||||||
return [_items objectAtIndex:_selectedIndex];
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modifying the font
|
|
||||||
/*!
|
|
||||||
Returns the font for tab label text.
|
|
||||||
@return the font for tab label text
|
|
||||||
*/
|
|
||||||
- (CPFont)font
|
|
||||||
{
|
|
||||||
return _font;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the font for tab label text to font.
|
|
||||||
@param font the font the receiver should use for tab label text
|
|
||||||
*/
|
|
||||||
- (void)setFont:(CPFont)font
|
|
||||||
{
|
|
||||||
if ([_font isEqual:font])
|
|
||||||
return;
|
|
||||||
|
|
||||||
_font = font;
|
|
||||||
[_tabs setFont:_font];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -334,71 +285,49 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
*/
|
*/
|
||||||
- (void)setTabViewType:(CPTabViewType)aTabViewType
|
- (void)setTabViewType:(CPTabViewType)aTabViewType
|
||||||
{
|
{
|
||||||
if (_type === aTabViewType)
|
if (type === aTabViewType)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_type = aTabViewType;
|
if ((type === CPTopTabsBezelBorder || type === CPBottomTabsBezelBorder)
|
||||||
|
&& (aTabViewType !== CPTopTabsBezelBorder && aTabViewType !== CPBottomTabsBezelBorder))
|
||||||
|
[tabs removeFromSuperview];
|
||||||
|
|
||||||
if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder)
|
if ((type === CPNoTabsBezelBorder || type === CPNoTabsLineBorder || type === CPNoTabsNoBorder)
|
||||||
[_tabs removeFromSuperview];
|
&& (aTabViewType !== CPNoTabsBezelBorder && aTabViewType !== CPNoTabsBezelBorder && aTabViewType !== CPNoTabsNoBorder))
|
||||||
else
|
[self addSubview:tabs];
|
||||||
[self addSubview:_tabs];
|
|
||||||
|
|
||||||
switch (_type)
|
type = aTabViewType;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
{
|
{
|
||||||
case CPTopTabsBezelBorder:
|
case CPTopTabsBezelBorder:
|
||||||
case CPBottomTabsBezelBorder:
|
case CPBottomTabsBezelBorder:
|
||||||
case CPNoTabsBezelBorder:
|
case CPNoTabsBezelBorder:
|
||||||
[_box setBorderType:CPBezelBorder];
|
[box setBorderType:CPBezelBorder];
|
||||||
break;
|
break;
|
||||||
case CPNoTabsLineBorder:
|
case CPNoTabsLineBorder:
|
||||||
[_box setBorderType:CPLineBorder];
|
[box setBorderType:CPLineBorder];
|
||||||
break;
|
break;
|
||||||
case CPNoTabsNoBorder:
|
case CPNoTabsNoBorder:
|
||||||
[_box setBorderType:CPNoBorder];
|
[box setBorderType:CPNoBorder];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
// Even if CPTabView's autoresizesSubviews is NO, _tabs and _box has to be laid out.
|
|
||||||
// This means we can't rely on autoresize masks.
|
|
||||||
if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder)
|
|
||||||
{
|
|
||||||
[_box setFrame:[self bounds]];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var aFrame = [self frame],
|
|
||||||
segmentedHeight = CGRectGetHeight([_tabs frame]),
|
|
||||||
origin = _type === CPTopTabsBezelBorder ? segmentedHeight / 2 : 0;
|
|
||||||
|
|
||||||
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
|
|
||||||
CGRectGetHeight(aFrame) - segmentedHeight / 2)];
|
|
||||||
|
|
||||||
[self _repositionTabs];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the tab view type.
|
Returns the tab view type.
|
||||||
@return the tab view type of the receiver
|
|
||||||
*/
|
*/
|
||||||
- (CPTabViewType)tabViewType
|
- (CPTabViewType)tabViewType
|
||||||
{
|
{
|
||||||
return _type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's delegate.
|
Returns the receiver's delegate.
|
||||||
@return the receiver's delegate
|
|
||||||
*/
|
*/
|
||||||
- (id)delegate
|
- (id)delegate
|
||||||
{
|
{
|
||||||
return _delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -407,79 +336,74 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
*/
|
*/
|
||||||
- (void)setDelegate:(id)aDelegate
|
- (void)setDelegate:(id)aDelegate
|
||||||
{
|
{
|
||||||
if (_delegate == aDelegate)
|
if (delegate == aDelegate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_delegate = aDelegate;
|
delegate = aDelegate;
|
||||||
|
|
||||||
_delegateSelectors = 0;
|
delegateSelectors = 0;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)])
|
if ([delegate respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)])
|
||||||
_delegateSelectors |= CPTabViewShouldSelectTabViewItemSelector;
|
delegateSelectors |= CPTabViewShouldSelectTabViewItemSelector;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(tabView:willSelectTabViewItem:)])
|
if ([delegate respondsToSelector:@selector(tabView:willSelectTabViewItem:)])
|
||||||
_delegateSelectors |= CPTabViewWillSelectTabViewItemSelector;
|
delegateSelectors |= CPTabViewWillSelectTabViewItemSelector;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(tabView:didSelectTabViewItem:)])
|
if ([delegate respondsToSelector:@selector(tabView:didSelectTabViewItem:)])
|
||||||
_delegateSelectors |= CPTabViewDidSelectTabViewItemSelector;
|
delegateSelectors |= CPTabViewDidSelectTabViewItemSelector;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)])
|
if ([delegate respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)])
|
||||||
_delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector;
|
delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setBackgroundColor:(CPColor)aColor
|
- (void)setBackgroundColor:(CPColor)aColor
|
||||||
{
|
{
|
||||||
[_box setBackgroundColor:aColor];
|
[box setBackgroundColor:aColor];
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)backgroundColor
|
|
||||||
{
|
|
||||||
return [_box backgroundColor];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var segmentIndex = [_tabs testSegment:[_tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
|
var segmentIndex = [tabs testSegment:[tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
|
||||||
|
|
||||||
if (segmentIndex != CPNotFound && [self selectTabViewItemAtIndex:segmentIndex])
|
if (segmentIndex != CPNotFound)
|
||||||
[_tabs trackSegment:anEvent];
|
{
|
||||||
|
[self selectTabViewItemAtIndex:segmentIndex];
|
||||||
|
[tabs trackSegment:anEvent];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_repositionTabs
|
- (void)_repositionTabs
|
||||||
{
|
{
|
||||||
var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2,
|
var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2,
|
||||||
verticalCenterOfTabs = CGRectGetHeight([_tabs bounds]) / 2;
|
verticalCenterOfTabs = CGRectGetHeight([tabs bounds]) / 2;
|
||||||
|
|
||||||
if (_type === CPBottomTabsBezelBorder)
|
if (type === CPBottomTabsBezelBorder)
|
||||||
[_tabs setCenter:CGPointMake(horizontalCenterOfSelf, CGRectGetHeight([self bounds]) - verticalCenterOfTabs)];
|
[tabs setCenter:CGPointMake(horizontalCenterOfSelf, CGRectGetHeight([self bounds]) - verticalCenterOfTabs)];
|
||||||
else
|
else
|
||||||
[_tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
|
[tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_setSelectedIndex:(CPNumber)index
|
- (void)_setSelectedIndex:(CPNumber)index
|
||||||
{
|
{
|
||||||
_selectedIndex = index;
|
selectedIndex = index;
|
||||||
[self _setContentViewFromItem:[_items objectAtIndex:_selectedIndex]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setContentViewFromItem:(CPTabViewItem)anItem
|
[box setContentView:[[items objectAtIndex:selectedIndex] view]];
|
||||||
{
|
|
||||||
[_box setContentView:[anItem view]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_updateItems
|
- (void)_updateItems
|
||||||
{
|
{
|
||||||
var count = [_items count];
|
[tabs setSegmentCount:[items count]];
|
||||||
[_tabs setSegmentCount:count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < [items count]; i++)
|
||||||
{
|
{
|
||||||
[_tabs setLabel:[[_items objectAtIndex:i] label] forSegment:i];
|
[tabs setLabel:[[items objectAtIndex:i] label] forSegment:i];
|
||||||
[_tabs setTag:i forSegment:i];
|
[tabs setTag:i forSegment:i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_selectedIndex === CPNotFound)
|
if (selectedIndex === CPNotFound)
|
||||||
|
{
|
||||||
[self selectFirstTabViewItem:self];
|
[self selectFirstTabViewItem:self];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -487,7 +411,6 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
var CPTabViewItemsKey = "CPTabViewItemsKey",
|
var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||||
CPTabViewSelectedItemKey = "CPTabViewSelectedItemKey",
|
CPTabViewSelectedItemKey = "CPTabViewSelectedItemKey",
|
||||||
CPTabViewTypeKey = "CPTabViewTypeKey",
|
CPTabViewTypeKey = "CPTabViewTypeKey",
|
||||||
CPTabViewFontKey = "CPTabViewFontKey",
|
|
||||||
CPTabViewDelegateKey = "CPTabViewDelegateKey";
|
CPTabViewDelegateKey = "CPTabViewDelegateKey";
|
||||||
|
|
||||||
@implementation CPTabView (CPCoding)
|
@implementation CPTabView (CPCoding)
|
||||||
@@ -496,62 +419,33 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
|||||||
{
|
{
|
||||||
if (self = [super initWithCoder:aCoder])
|
if (self = [super initWithCoder:aCoder])
|
||||||
{
|
{
|
||||||
[self _init];
|
type = [aCoder decodeIntForKey:CPTabViewTypeKey];
|
||||||
|
items = [];
|
||||||
|
|
||||||
_font = [aCoder decodeObjectForKey:CPTabViewFontKey];
|
var encodedItems = [aCoder decodeObjectForKey:CPTabViewItemsKey];
|
||||||
[_tabs setFont:_font];
|
for (var i = 0; encodedItems && i < encodedItems.length; i++)
|
||||||
|
[self insertTabViewItem:encodedItems[i] atIndex:i];
|
||||||
|
|
||||||
_items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
|
var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
||||||
[_items makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
|
if (selected)
|
||||||
|
[self selectTabViewItem:selected];
|
||||||
|
|
||||||
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
||||||
|
|
||||||
self.selectOnAwake = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
|
||||||
_type = [aCoder decodeIntForKey:CPTabViewTypeKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)awakeFromCib
|
|
||||||
{
|
|
||||||
// This cannot be run in initWithCoder because it might call selectTabViewItem:, which is
|
|
||||||
// not safe to call before the views of the tab views items are fully decoded.
|
|
||||||
[self _updateItems];
|
|
||||||
|
|
||||||
if (self.selectOnAwake)
|
|
||||||
{
|
|
||||||
[self selectTabViewItem:self.selectOnAwake];
|
|
||||||
delete self.selectOnAwake;
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = _type;
|
|
||||||
_type = nil;
|
|
||||||
[self setTabViewType:type];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
// Don't bother to encode the CPBox. We will recreate it on decode and its content view is already
|
|
||||||
// stored by the tab view item. Not encoding _box makes the resulting archive smaller and reduces
|
|
||||||
// the surface for decoding bugs (of which we've had many in tab view).
|
|
||||||
var subviews = [self subviews];
|
|
||||||
[_box removeFromSuperview];
|
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
[self setSubviews:subviews];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_items forKey:CPTabViewItemsKey];
|
[aCoder encodeObject:items forKey:CPTabViewItemsKey];;
|
||||||
|
[aCoder encodeObject:[self selectedTabViewItem] forKey:CPTabViewSelectedItemKey];
|
||||||
|
|
||||||
var selected = [self selectedTabViewItem];
|
[aCoder encodeInt:type forKey:CPTabViewTypeKey];
|
||||||
if (selected)
|
|
||||||
[aCoder encodeObject:selected forKey:CPTabViewSelectedItemKey];
|
|
||||||
|
|
||||||
[aCoder encodeInt:_type forKey:CPTabViewTypeKey];
|
[aCoder encodeConditionalObject:delegate forKey:CPTabViewDelegateKey];
|
||||||
[aCoder encodeObject:_font forKey:CPTabViewFontKey];
|
|
||||||
|
|
||||||
[aCoder encodeConditionalObject:_delegate forKey:CPTabViewDelegateKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+3
-10
@@ -60,7 +60,6 @@ CPPressedTab = 2;
|
|||||||
CPView _auxiliaryView;
|
CPView _auxiliaryView;
|
||||||
|
|
||||||
CPTabView _tabView;
|
CPTabView _tabView;
|
||||||
unsigned _tabState; // Looks like it is not yet implemented
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
@@ -133,13 +132,7 @@ CPPressedTab = 2;
|
|||||||
*/
|
*/
|
||||||
- (void)setView:(CPView)aView
|
- (void)setView:(CPView)aView
|
||||||
{
|
{
|
||||||
if (_view == aView)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_view = aView;
|
_view = aView;
|
||||||
|
|
||||||
if ([_tabView selectedTabViewItem] == self)
|
|
||||||
[_tabView _setContentViewFromItem:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -152,8 +145,8 @@ CPPressedTab = 2;
|
|||||||
|
|
||||||
// Assigning an Auxiliary View
|
// Assigning an Auxiliary View
|
||||||
/*!
|
/*!
|
||||||
Sets the tab's auxiliary view.
|
Sets the tab's auxillary view.
|
||||||
@param anAuxiliaryView the new auxiliary view
|
@param anAuxillaryView the new auxillary view
|
||||||
*/
|
*/
|
||||||
- (void)setAuxiliaryView:(CPView)anAuxiliaryView
|
- (void)setAuxiliaryView:(CPView)anAuxiliaryView
|
||||||
{
|
{
|
||||||
@@ -161,7 +154,7 @@ CPPressedTab = 2;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the tab's auxiliary view
|
Returns the tab's auxillary view
|
||||||
*/
|
*/
|
||||||
- (CPView)auxiliaryView
|
- (CPView)auxiliaryView
|
||||||
{
|
{
|
||||||
|
|||||||
+62
-349
@@ -26,33 +26,19 @@
|
|||||||
@import <Foundation/CPSortDescriptor.j>
|
@import <Foundation/CPSortDescriptor.j>
|
||||||
@import <Foundation/CPString.j>
|
@import <Foundation/CPString.j>
|
||||||
|
|
||||||
@import "CPTextField.j"
|
@import "CPTableHeaderView.j"
|
||||||
|
|
||||||
@global CPTableViewColumnDidResizeNotification
|
|
||||||
|
|
||||||
@class _CPTableColumnHeaderView
|
|
||||||
|
|
||||||
CPTableColumnNoResizing = 0;
|
CPTableColumnNoResizing = 0;
|
||||||
CPTableColumnAutoresizingMask = 1 << 0;
|
CPTableColumnAutoresizingMask = 1 << 0;
|
||||||
CPTableColumnUserResizingMask = 1 << 1;
|
CPTableColumnUserResizingMask = 1 << 1;
|
||||||
|
|
||||||
/*!
|
|
||||||
@class CPTableColumn
|
|
||||||
|
|
||||||
A CPTableColumn contains a dataview to display for its column of the CPTableView.
|
|
||||||
A CPTableColumn determines its own size constrains and resizing behavior.
|
|
||||||
|
|
||||||
The default dataview is a CPTextField but you can set it to any view you'd like. See -setDataView: for documentation including theme states.
|
|
||||||
|
|
||||||
To customize the text of the column header you can simply call setStringValue: on the headerview of a table column.
|
|
||||||
For example: [[myTableColumn headerView] setStringValue:"My Title"];
|
|
||||||
*/
|
|
||||||
@implementation CPTableColumn : CPObject
|
@implementation CPTableColumn : CPObject
|
||||||
{
|
{
|
||||||
CPTableView _tableView;
|
CPTableView _tableView;
|
||||||
CPView _headerView;
|
CPView _headerView;
|
||||||
CPView _dataView;
|
CPView _dataView;
|
||||||
CPData _dataViewData;
|
Object _dataViewData;
|
||||||
|
|
||||||
float _width;
|
float _width;
|
||||||
float _minWidth;
|
float _minWidth;
|
||||||
@@ -68,25 +54,18 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
BOOL _disableResizingPosting @accessors(property=disableResizingPosting);
|
BOOL _disableResizingPosting @accessors(property=disableResizingPosting);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
return [self initWithIdentifier:@""];
|
return [self initWithIdentifier:@""];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes a newly created CPTableColumn with a given identifier.
|
|
||||||
|
|
||||||
*/
|
|
||||||
- (id)initWithIdentifier:(id)anIdentifier
|
- (id)initWithIdentifier:(id)anIdentifier
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_dataViewData = nil;
|
_dataViewData = { };
|
||||||
|
|
||||||
_width = 100.0;
|
_width = 100.0;
|
||||||
_minWidth = 10.0;
|
_minWidth = 10.0;
|
||||||
@@ -105,50 +84,17 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the columns's parent tableview
|
|
||||||
*/
|
|
||||||
- (void)setTableView:(CPTableView)aTableView
|
- (void)setTableView:(CPTableView)aTableView
|
||||||
{
|
{
|
||||||
_tableView = aTableView;
|
_tableView = aTableView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Return the column's parent tableview
|
|
||||||
*/
|
|
||||||
- (CPTableView)tableView
|
- (CPTableView)tableView
|
||||||
{
|
{
|
||||||
return _tableView;
|
return _tableView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
this method tries to resize a column called via the tableview via autoresizing
|
|
||||||
it returns the delta from the actual resize and the proposed resize
|
|
||||||
|
|
||||||
for example if the column should have been resized 50px but the maxWidth was hit only
|
|
||||||
after 25px then the return value would be 25px;
|
|
||||||
|
|
||||||
if no edge has been hit zero will be returned
|
|
||||||
*/
|
|
||||||
- (int)_tryToResizeToWidth:(int)width
|
|
||||||
{
|
|
||||||
var min = [self minWidth],
|
|
||||||
max = [self maxWidth],
|
|
||||||
newWidth = ROUND(MIN(MAX(width, min), max));
|
|
||||||
|
|
||||||
[self setWidth:newWidth];
|
|
||||||
|
|
||||||
return newWidth - width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the width of the column
|
|
||||||
Default value is: 100
|
|
||||||
|
|
||||||
If the value is greater than the maxWidth the maxWidth will be reset with the supplied width here
|
|
||||||
If the value is less than the minWidth the minWidth will be reset with the supplied width.
|
|
||||||
*/
|
|
||||||
- (void)setWidth:(float)aWidth
|
- (void)setWidth:(float)aWidth
|
||||||
{
|
{
|
||||||
aWidth = +aWidth;
|
aWidth = +aWidth;
|
||||||
@@ -189,18 +135,11 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the column's width
|
|
||||||
*/
|
|
||||||
- (float)width
|
- (float)width
|
||||||
{
|
{
|
||||||
return _width;
|
return _width;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the minimum width of the column.
|
|
||||||
Default value is 10.
|
|
||||||
*/
|
|
||||||
- (void)setMinWidth:(float)aMinWidth
|
- (void)setMinWidth:(float)aMinWidth
|
||||||
{
|
{
|
||||||
aMinWidth = +aMinWidth;
|
aMinWidth = +aMinWidth;
|
||||||
@@ -217,18 +156,11 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
[self setWidth:newWidth];
|
[self setWidth:newWidth];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the minimum width of the column.
|
|
||||||
*/
|
|
||||||
- (float)minWidth
|
- (float)minWidth
|
||||||
{
|
{
|
||||||
return _minWidth;
|
return _minWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the maximum width of the table column.
|
|
||||||
Default value is: 1000000
|
|
||||||
*/
|
|
||||||
- (void)setMaxWidth:(float)aMaxWidth
|
- (void)setMaxWidth:(float)aMaxWidth
|
||||||
{
|
{
|
||||||
aMaxWidth = +aMaxWidth;
|
aMaxWidth = +aMaxWidth;
|
||||||
@@ -245,45 +177,24 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
[self setWidth:newWidth];
|
[self setWidth:newWidth];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the maximum width of the column
|
|
||||||
*/
|
|
||||||
- (float)maxWidth
|
- (float)maxWidth
|
||||||
{
|
{
|
||||||
return _maxWidth;
|
return _maxWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
<pre>
|
|
||||||
Set the resizing mask of the column.
|
|
||||||
By default the column can be resized automatically with the tableview and manually by the user
|
|
||||||
|
|
||||||
Possible masking values are:
|
|
||||||
CPTableColumnNoResizing
|
|
||||||
CPTableColumnAutoresizingMask
|
|
||||||
CPTableColumnUserResizingMask
|
|
||||||
</pre>
|
|
||||||
*/
|
|
||||||
- (void)setResizingMask:(unsigned)aResizingMask
|
- (void)setResizingMask:(unsigned)aResizingMask
|
||||||
{
|
{
|
||||||
_resizingMask = aResizingMask;
|
_resizingMask = aResizingMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the resizing mask of the column
|
|
||||||
*/
|
|
||||||
- (unsigned)resizingMask
|
- (unsigned)resizingMask
|
||||||
{
|
{
|
||||||
return _resizingMask;
|
return _resizingMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sizes the column to fix the column header text.
|
|
||||||
*/
|
|
||||||
- (void)sizeToFit
|
- (void)sizeToFit
|
||||||
{
|
{
|
||||||
var width = CGRectGetWidth([_headerView frame]);
|
var width = _CGRectGetWidth([_headerView frame]);
|
||||||
|
|
||||||
if (width < [self minWidth])
|
if (width < [self minWidth])
|
||||||
[self setMinWidth:width];
|
[self setMinWidth:width];
|
||||||
@@ -294,16 +205,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
[self setWidth:width];
|
[self setWidth:width];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Setting Component Cells
|
||||||
/*!
|
|
||||||
Sets the header view for the column.
|
|
||||||
The headerview handles the display of sort indicators, text, etc.
|
|
||||||
|
|
||||||
If you do not want a headerview for you table you should call setHeaderView: on your CPTableView instance.
|
|
||||||
Passing nil here will throw an exception.
|
|
||||||
|
|
||||||
In order to customize the text of the column header see - (CPView)headerView;
|
|
||||||
*/
|
|
||||||
- (void)setHeaderView:(CPView)aView
|
- (void)setHeaderView:(CPView)aView
|
||||||
{
|
{
|
||||||
if (!aView)
|
if (!aView)
|
||||||
@@ -317,90 +219,36 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
[tableHeaderView setNeedsDisplay:YES];
|
[tableHeaderView setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the headerview for the column.
|
|
||||||
|
|
||||||
In order to change the text of the headerview for a column you should call setStringValue: on the headerview.
|
|
||||||
For example: [[myTableColumn headerView] setStringValue:"My Column"];
|
|
||||||
*/
|
|
||||||
- (CPView)headerView
|
- (CPView)headerView
|
||||||
{
|
{
|
||||||
return _headerView;
|
return _headerView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
This method sets the "prototype" view which will be used to create all table cells in this column.
|
This method set's the "prototype" view which will be used to create all table cells in this column.
|
||||||
|
|
||||||
It creates a snapshot of aView, using keyed archiving, which is then copied over and over for each
|
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.
|
individual cell that is shown. As a result, changes made after calling this method won't be reflected.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@code
|
|
||||||
[tableColumn setDataView:someView]; // snapshot taken
|
[tableColumn setDataView:someView]; // snapshot taken
|
||||||
[[tableColumn dataView] setSomething:x]; //won't work
|
[[tableColumn dataView] setSomething:x]; //won't work
|
||||||
@endcode
|
|
||||||
|
|
||||||
This doesn't work because the snapshot is taken before the new property is applied. Instead, do:
|
This doesn't work because the snapshot is taken before the new property is applied. Instead, do:
|
||||||
@code
|
|
||||||
[someView setSomething:x];
|
[someView setSomething:x];
|
||||||
[tableColumn setDataView:someView];
|
[tableColumn setDataView:someView];
|
||||||
@endcode
|
|
||||||
|
|
||||||
@note you should implement CPKeyedArchiving otherwise you might see unexpected results.
|
|
||||||
This is done by adding the following methods to your class:
|
|
||||||
@endnote
|
|
||||||
|
|
||||||
@code
|
|
||||||
- (id)initWithCoder(CPCoder)aCoder;
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Example:
|
|
||||||
Say you have two instance variables in your object that need to be set up each time an object is create.
|
|
||||||
We will call these instance variables "image" and "text".
|
|
||||||
Your CPCoding methods will look like the following:
|
|
||||||
|
|
||||||
@code
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
image = [aCoder decodeObjectForKey:"MyDataViewImage"];
|
|
||||||
text = [aCoder decodeObjectForKey:"MyDataViewText"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeObject:image forKey:"MyDataViewImage"];
|
|
||||||
[aCoder encodeObject:text forKey:"MyDataViewText"];
|
|
||||||
}
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
@section Themeing
|
|
||||||
|
|
||||||
When you set a dataview and it is added to the tableview the theme state will be set to \c CPThemeStateTableDataView
|
|
||||||
When the dataview becomes selected the theme state will be set to \c CPThemeStateSelectedDataView.
|
|
||||||
|
|
||||||
If the dataview shows up in a group row of the tableview the theme state will be set to \c CPThemeStateGroupRow.
|
|
||||||
|
|
||||||
You should overide \c setThemeState: and \c unsetThemeState: to handle these theme state changes in your dataview.
|
|
||||||
*/
|
*/
|
||||||
- (void)setDataView:(CPView)aView
|
- (void)setDataView:(CPView)aView
|
||||||
{
|
{
|
||||||
if (_dataView)
|
if (_dataView)
|
||||||
_dataViewData = nil;
|
_dataViewData[[_dataView UID]] = nil;
|
||||||
|
|
||||||
[aView setThemeState:CPThemeStateTableDataView];
|
[aView setThemeState:CPThemeStateTableDataView];
|
||||||
|
|
||||||
_dataView = aView;
|
_dataView = aView;
|
||||||
_dataViewData = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
_dataViewData[[aView UID]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPView)dataView
|
- (CPView)dataView
|
||||||
@@ -412,7 +260,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
Returns the CPView object used by the CPTableView to draw values for the receiver.
|
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
|
By default, this method just calls dataView. Subclassers can override if they need to
|
||||||
potentially use different "cells" or dataViews for different rows. Subclasses should expect this method
|
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
|
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.
|
view needs to get some generic cell info.
|
||||||
*/
|
*/
|
||||||
@@ -421,15 +269,24 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
return [self dataView];
|
return [self dataView];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (id)_newDataViewForRow:(int)aRowIndex
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (id)_newDataView
|
|
||||||
{
|
{
|
||||||
if (!_dataViewData)
|
var dataView = [self dataViewForRow:aRowIndex],
|
||||||
return nil;
|
dataViewUID = [dataView UID];
|
||||||
|
|
||||||
var newDataView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData];
|
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];
|
[newDataView setAutoresizingMask:CPViewNotSizable];
|
||||||
|
|
||||||
return newDataView;
|
return newDataView;
|
||||||
@@ -437,7 +294,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
|
|
||||||
//Setting the Identifier
|
//Setting the Identifier
|
||||||
|
|
||||||
/*!
|
/*
|
||||||
Sets the receiver identifier to anIdentifier.
|
Sets the receiver identifier to anIdentifier.
|
||||||
*/
|
*/
|
||||||
- (void)setIdentifier:(id)anIdentifier
|
- (void)setIdentifier:(id)anIdentifier
|
||||||
@@ -445,7 +302,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
_identifier = anIdentifier;
|
_identifier = anIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*
|
||||||
Returns the object used by the data source to identify the attribute corresponding to the receiver.
|
Returns the object used by the data source to identify the attribute corresponding to the receiver.
|
||||||
*/
|
*/
|
||||||
- (id)identifier
|
- (id)identifier
|
||||||
@@ -455,7 +312,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
|
|
||||||
//Controlling Editability
|
//Controlling Editability
|
||||||
|
|
||||||
/*!
|
/*
|
||||||
Controls whether the user can edit cells in the receiver by double-clicking them.
|
Controls whether the user can edit cells in the receiver by double-clicking them.
|
||||||
*/
|
*/
|
||||||
- (void)setEditable:(BOOL)shouldBeEditable
|
- (void)setEditable:(BOOL)shouldBeEditable
|
||||||
@@ -463,45 +320,31 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
_isEditable = shouldBeEditable;
|
_isEditable = shouldBeEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*
|
||||||
Returns YES if the user can edit cells associated with the receiver by double-clicking the
|
Returns YES if the user can edit cells associated with the receiver by double-clicking the
|
||||||
column in the CPTableView, NO otherwise.
|
column in the NSTableView, NO otherwise.
|
||||||
*/
|
*/
|
||||||
- (BOOL)isEditable
|
- (BOOL)isEditable
|
||||||
{
|
{
|
||||||
return _isEditable;
|
return _isEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
//Sorting
|
||||||
Sets the sort descriptor prototype for the column.
|
|
||||||
*/
|
|
||||||
- (void)setSortDescriptorPrototype:(CPSortDescriptor)aSortDescriptor
|
- (void)setSortDescriptorPrototype:(CPSortDescriptor)aSortDescriptor
|
||||||
{
|
{
|
||||||
_sortDescriptorPrototype = aSortDescriptor;
|
_sortDescriptorPrototype = aSortDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the sort descriptor prototype for the column.
|
|
||||||
*/
|
|
||||||
- (CPSortDescriptor)sortDescriptorPrototype
|
- (CPSortDescriptor)sortDescriptorPrototype
|
||||||
{
|
{
|
||||||
if (_sortDescriptorPrototype)
|
return _sortDescriptorPrototype;
|
||||||
return _sortDescriptorPrototype;
|
|
||||||
|
|
||||||
var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
|
|
||||||
binding = [binderClass getBinding:CPValueBinding forObject:self];
|
|
||||||
|
|
||||||
return [binding _defaultSortDescriptorPrototype];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
//Setting Column Visibility
|
||||||
If YES the tablecolumn will no longer be visible in the tableview.
|
|
||||||
If NO the tablecolumn will be visible in the tableview.
|
|
||||||
*/
|
|
||||||
- (void)setHidden:(BOOL)shouldBeHidden
|
- (void)setHidden:(BOOL)shouldBeHidden
|
||||||
{
|
{
|
||||||
shouldBeHidden = !!shouldBeHidden
|
shouldBeHidden = !!shouldBeHidden
|
||||||
|
|
||||||
if (_isHidden === shouldBeHidden)
|
if (_isHidden === shouldBeHidden)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -511,9 +354,6 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
[[self tableView] _tableColumnVisibilityDidChange:self];
|
[[self tableView] _tableColumnVisibilityDidChange:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the visibility status of the column.
|
|
||||||
*/
|
|
||||||
- (BOOL)isHidden
|
- (BOOL)isHidden
|
||||||
{
|
{
|
||||||
return _isHidden;
|
return _isHidden;
|
||||||
@@ -521,7 +361,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
|
|
||||||
//Setting Tool Tips
|
//Setting Tool Tips
|
||||||
|
|
||||||
/*!
|
/*
|
||||||
Sets the tooltip string that is displayed when the cursor pauses over the
|
Sets the tooltip string that is displayed when the cursor pauses over the
|
||||||
header cell of the receiver.
|
header cell of the receiver.
|
||||||
*/
|
*/
|
||||||
@@ -530,116 +370,34 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
_headerToolTip = aToolTip;
|
_headerToolTip = aToolTip;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the tooltip for the column header
|
|
||||||
*/
|
|
||||||
- (CPString)headerToolTip
|
- (CPString)headerToolTip
|
||||||
{
|
{
|
||||||
return _headerToolTip;
|
return _headerToolTip;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_postDidResizeNotificationWithOldWidth:(float)oldWidth
|
- (void)_postDidResizeNotificationWithOldWidth:(float)oldWidth
|
||||||
{
|
{
|
||||||
[[self tableView] _didResizeTableColumn:self];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
postNotificationName:CPTableViewColumnDidResizeNotification
|
postNotificationName:CPTableViewColumnDidResizeNotification
|
||||||
object:[self tableView]
|
object:[self tableView]
|
||||||
userInfo:@{ @"CPTableColumn": self, @"CPOldWidth": oldWidth }];
|
userInfo:[CPDictionary dictionaryWithObjects:[self, oldWidth] forKeys:[@"CPTableColumn", "CPOldWidth"]]];
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CPTableColumnValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)aBinding
|
|
||||||
{
|
|
||||||
var tableView = [_source tableView],
|
|
||||||
column = [[tableView tableColumns] indexOfObjectIdenticalTo:_source],
|
|
||||||
rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [tableView numberOfRows])],
|
|
||||||
columnIndexes = [CPIndexSet indexSetWithIndex:column];
|
|
||||||
|
|
||||||
[tableView reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPSortDescriptor)_defaultSortDescriptorPrototype
|
|
||||||
{
|
|
||||||
if (![self createsSortDescriptor])
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
var keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
|
||||||
dotIndex = keyPath.indexOf(".");
|
|
||||||
|
|
||||||
if (dotIndex === CPNotFound)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
var firstPart = keyPath.substring(0, dotIndex),
|
|
||||||
key = keyPath.substring(dotIndex + 1);
|
|
||||||
|
|
||||||
return [CPSortDescriptor sortDescriptorWithKey:key ascending:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)createsSortDescriptor
|
|
||||||
{
|
|
||||||
var options = [_info objectForKey:CPOptionsKey],
|
|
||||||
optionValue = [options objectForKey:CPCreatesSortDescriptorBindingOption];
|
|
||||||
return optionValue === nil ? YES : [optionValue boolValue];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPTableColumn (Bindings)
|
@implementation CPTableColumn (Bindings)
|
||||||
|
|
||||||
+ (id)_binderClassForBinding:(CPString)aBinding
|
|
||||||
{
|
|
||||||
if (aBinding == CPValueBinding)
|
|
||||||
return [CPTableColumnValueBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:aBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Binds the receiver to an object.
|
|
||||||
|
|
||||||
@param CPString aBinding - The binding you wish to make. Typically CPValueBinding.
|
|
||||||
@param id anObject - The object to bind the receiver to.
|
|
||||||
@param CPString aKeyPath - The key path you wish to bind the receiver to.
|
|
||||||
@param CPDictionary options - A dictionary of options for the binding. This parameter is optional, pass nil if you do not wish to use it.
|
|
||||||
*/
|
|
||||||
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
|
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
|
||||||
{
|
{
|
||||||
[super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
|
[super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
|
||||||
|
|
||||||
if (![aBinding isEqual:@"someListOfExceptedBindings(notAcceptedBindings)"])
|
if (![aBinding isEqual:@"someListOfExceptedBindings(notAcceptedBindings)"])
|
||||||
{
|
[[self tableView] _establishBindingsIfUnbound:anObject];
|
||||||
// Bind the table to the array controller this column is bound to.
|
|
||||||
// Note that anObject might not be the array controller. E.g. the keypath could be something like
|
|
||||||
// somePathTo.anArrayController.arrangedObjects.aKey. Cocoa doesn't support this but it is consistent
|
|
||||||
// and it makes sense.
|
|
||||||
var acIndex = aKeyPath.lastIndexOf("arrangedObjects."),
|
|
||||||
arrayController = anObject;
|
|
||||||
|
|
||||||
if (acIndex > 1)
|
|
||||||
{
|
|
||||||
var firstPart = aKeyPath.substring(0, acIndex - 1);
|
|
||||||
arrayController = [anObject valueForKeyPath:firstPart];
|
|
||||||
}
|
|
||||||
|
|
||||||
[[self tableView] _establishBindingsIfUnbound:arrayController];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (void)prepareDataView:(CPView)aDataView forRow:(unsigned)aRow
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_prepareDataView:(CPView)aDataView forRow:(unsigned)aRow
|
|
||||||
{
|
{
|
||||||
var bindingsDictionary = [CPBinder allBindingsForObject:self],
|
var bindingsDictionary = [CPKeyValueBinding allBindingsForObject:self],
|
||||||
keys = [bindingsDictionary allKeys];
|
keys = [bindingsDictionary allKeys];
|
||||||
|
|
||||||
for (var i = 0, count = [keys count]; i < count; i++)
|
for (var i = 0, count = [keys count]; i < count; i++)
|
||||||
@@ -679,46 +437,20 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
}
|
}
|
||||||
|
|
||||||
value = [binding transformValue:value withOptions:[bindingInfo objectForKey:CPOptionsKey]];
|
value = [binding transformValue:value withOptions:[bindingInfo objectForKey:CPOptionsKey]];
|
||||||
[aDataView setValue:value forKey:@"objectValue"];
|
|
||||||
|
// console.log(bindingName+" : "+keyPath+" : "+aRow+" : "+[[destination valueForKeyPath:keyPath] objectAtIndex:aRow]);
|
||||||
|
[aDataView setValue:value forKey:bindingPath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
//- (void)objectValue
|
||||||
@ignore
|
//{
|
||||||
*/
|
// return nil;
|
||||||
- (void)_reverseSetDataView:(CPView)aDataView forRow:(unsigned)aRow
|
//}
|
||||||
|
|
||||||
|
- (void)setValue:(CPArray)content
|
||||||
{
|
{
|
||||||
var bindingsDictionary = [CPBinder allBindingsForObject:self],
|
[[self tableView] reloadData];
|
||||||
keys = [bindingsDictionary allKeys],
|
|
||||||
newValue = [aDataView valueForKey:@"objectValue"];
|
|
||||||
|
|
||||||
for (var i = 0, count = [keys count]; i < count; i++)
|
|
||||||
{
|
|
||||||
var bindingName = keys[i],
|
|
||||||
bindingPath = [aDataView _replacementKeyPathForBinding:bindingName],
|
|
||||||
binding = [bindingsDictionary objectForKey:bindingName],
|
|
||||||
bindingInfo = binding._info,
|
|
||||||
destination = [bindingInfo objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [bindingInfo objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [bindingInfo objectForKey:CPOptionsKey],
|
|
||||||
dotIndex = keyPath.lastIndexOf(".");
|
|
||||||
|
|
||||||
newValue = [binding reverseTransformValue:newValue withOptions:options];
|
|
||||||
|
|
||||||
if (dotIndex === CPNotFound)
|
|
||||||
[[destination valueForKeyPath:keyPath] replaceObjectAtIndex:aRow withObject:newValue];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var firstPart = keyPath.substring(0, dotIndex),
|
|
||||||
secondPart = keyPath.substring(dotIndex + 1),
|
|
||||||
firstValue = [destination valueForKeyPath:firstPart];
|
|
||||||
|
|
||||||
if ([firstValue isKindOfClass:CPArray])
|
|
||||||
[[firstValue objectAtIndex:aRow] setValue:newValue forKeyPath:secondPart];
|
|
||||||
else
|
|
||||||
[[firstValue valueForKeyPath:secondPart] replaceObjectAtIndex:aRow withObject:newValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -736,16 +468,13 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
|||||||
|
|
||||||
@implementation CPTableColumn (CPCoding)
|
@implementation CPTableColumn (CPCoding)
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_dataViewData = nil;
|
_dataViewData = { };
|
||||||
|
|
||||||
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
||||||
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
||||||
@@ -754,6 +483,7 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
|||||||
[self setIdentifier:[aCoder decodeObjectForKey:CPTableColumnIdentifierKey]];
|
[self setIdentifier:[aCoder decodeObjectForKey:CPTableColumnIdentifierKey]];
|
||||||
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||||
[self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
[self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||||
|
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||||
|
|
||||||
_resizingMask = [aCoder decodeIntForKey:CPTableColumnResizingMaskKey];
|
_resizingMask = [aCoder decodeIntForKey:CPTableColumnResizingMaskKey];
|
||||||
_isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenKey];
|
_isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenKey];
|
||||||
@@ -765,16 +495,13 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
[aCoder encodeObject:_identifier forKey:CPTableColumnIdentifierKey];
|
[aCoder encodeObject:_identifier forKey:CPTableColumnIdentifierKey];
|
||||||
|
|
||||||
[aCoder encodeFloat:_width forKey:CPTableColumnWidthKey];
|
[aCoder encodeObject:_width forKey:CPTableColumnWidthKey];
|
||||||
[aCoder encodeFloat:_minWidth forKey:CPTableColumnMinWidthKey];
|
[aCoder encodeObject:_minWidth forKey:CPTableColumnMinWidthKey];
|
||||||
[aCoder encodeFloat:_maxWidth forKey:CPTableColumnMaxWidthKey];
|
[aCoder encodeObject:_maxWidth forKey:CPTableColumnMaxWidthKey];
|
||||||
|
|
||||||
[aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
[aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||||
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||||
@@ -789,45 +516,31 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPTableColumn (NSInCompatibility)
|
@implementation CPTableColumn (NSInCompatibility)
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)setHeaderCell:(CPView)aView
|
- (void)setHeaderCell:(CPView)aView
|
||||||
{
|
{
|
||||||
[CPException raise:CPUnsupportedMethodException
|
[CPException raise:CPUnsupportedMethodException
|
||||||
reason:@"setHeaderCell: is not supported. Use -setHeaderView:aView instead."];
|
reason:@"setHeaderCell: is not supported. -setHeaderCell:aView instead."];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (CPView)headerCell
|
- (CPView)headerCell
|
||||||
{
|
{
|
||||||
[CPException raise:CPUnsupportedMethodException
|
[CPException raise:CPUnsupportedMethodException
|
||||||
reason:@"headCell is not supported. Use -headerView instead."];
|
reason:@"headCell is not supported. -headerView instead."];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)setDataCell:(CPView)aView
|
- (void)setDataCell:(CPView)aView
|
||||||
{
|
{
|
||||||
[CPException raise:CPUnsupportedMethodException
|
[CPException raise:CPUnsupportedMethodException
|
||||||
reason:@"setDataCell: is not supported. Use -setDataView:aView instead."];
|
reason:@"setDataCell: is not supported. Use -setHeaderCell:aView instead."];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (CPView)dataCell
|
- (CPView)dataCell
|
||||||
{
|
{
|
||||||
[CPException raise:CPUnsupportedMethodException
|
[CPException raise:CPUnsupportedMethodException
|
||||||
reason:@"dataCell is not supported. Use -dataView instead."];
|
reason:@"dataCell is not supported. Use -dataCell instead."];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (id)dataCellForRow:(int)row
|
- (id)dataCellForRow:(int)row
|
||||||
{
|
{
|
||||||
[CPException raise:CPUnsupportedMethodException
|
[CPException raise:CPUnsupportedMethodException
|
||||||
|
|||||||
+60
-161
@@ -22,15 +22,9 @@
|
|||||||
|
|
||||||
@import <Foundation/CPIndexSet.j>
|
@import <Foundation/CPIndexSet.j>
|
||||||
|
|
||||||
@import "CPCursor.j"
|
|
||||||
@import "CPPasteboard.j"
|
|
||||||
@import "CPTableColumn.j"
|
@import "CPTableColumn.j"
|
||||||
|
@import "CPTableView.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
@import "_CPImageAndTextView.j"
|
|
||||||
|
|
||||||
@class CPTableView
|
|
||||||
|
|
||||||
@global CPApp
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPTableColumnHeaderView : CPView
|
@implementation _CPTableColumnHeaderView : CPView
|
||||||
@@ -45,22 +39,13 @@
|
|||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return @{
|
return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null], CGInsetMakeZero(), [CPNull null], [CPNull null], [CPNull null], CGSizeMakeZero()]
|
||||||
@"background-color": [CPNull null],
|
forKeys:[@"background-color", @"text-alignment", @"text-inset", @"text-color", @"text-font", @"text-shadow-color", @"text-shadow-offset"]];
|
||||||
@"text-alignment": CPLeftTextAlignment,
|
|
||||||
@"line-break-mode": CPLineBreakByTruncatingTail,
|
|
||||||
@"text-inset": CGInsetMakeZero(),
|
|
||||||
@"text-color": [CPNull null],
|
|
||||||
@"font": [CPNull null],
|
|
||||||
@"text-shadow-color": [CPNull null],
|
|
||||||
@"text-shadow-offset": CGSizeMakeZero(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)initWithFrame:(CGRect)frame
|
- (void)initWithFrame:(CGRect)frame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:frame];
|
self = [super initWithFrame:frame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
[self _init];
|
[self _init];
|
||||||
|
|
||||||
@@ -69,9 +54,9 @@
|
|||||||
|
|
||||||
- (void)_init
|
- (void)_init
|
||||||
{
|
{
|
||||||
_textField = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
|
_textField = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
||||||
|
|
||||||
[_textField setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[_textField setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||||
|
|
||||||
[_textField setLineBreakMode:CPLineBreakByTruncatingTail];
|
[_textField setLineBreakMode:CPLineBreakByTruncatingTail];
|
||||||
[_textField setAlignment:CPLeftTextAlignment];
|
[_textField setAlignment:CPLeftTextAlignment];
|
||||||
@@ -87,13 +72,12 @@
|
|||||||
var inset = [self currentValueForThemeAttribute:@"text-inset"],
|
var inset = [self currentValueForThemeAttribute:@"text-inset"],
|
||||||
bounds = [self bounds];
|
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 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 setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
||||||
[_textField setFont:[self currentValueForThemeAttribute:@"font"]];
|
[_textField setFont:[self currentValueForThemeAttribute:@"text-font"]];
|
||||||
[_textField setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
|
[_textField setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
|
||||||
[_textField setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
|
[_textField setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
|
||||||
[_textField setAlignment:[self currentValueForThemeAttribute:@"text-alignment"]];
|
[_textField setAlignment:[self currentValueForThemeAttribute:@"text-alignment"]];
|
||||||
[_textField setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setStringValue:(CPString)string
|
- (void)setStringValue:(CPString)string
|
||||||
@@ -118,75 +102,26 @@
|
|||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
- (void)setFont:(CPFont)aFont
|
||||||
{
|
{
|
||||||
[self setValue:aFont forThemeAttribute:@"font"];
|
[_textField setFont:aFont];
|
||||||
}
|
|
||||||
|
|
||||||
- (CPFont)font
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"font"]
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAlignment:(CPTextAlignment)alignment
|
|
||||||
{
|
|
||||||
[self setValue:alignment forThemeAttribute:@"text-alignment"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPTextAlignment)alignment
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"text-alignment"]
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setLineBreakMode:(CPLineBreakMode)mode
|
|
||||||
{
|
|
||||||
[self setValue:mode forThemeAttribute:@"line-break-mode"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPLineBreakMode)lineBreakMode
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"line-break-mode"]
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTextColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
[self setValue:aColor forThemeAttribute:@"text-color"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textColor
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"text-color"]
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setTextShadowColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
[self setValue:aColor forThemeAttribute:@"text-shadow-color"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPColor)textShadowColor
|
|
||||||
{
|
|
||||||
return [self currentValueForThemeAttribute:@"text-shadow-color"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_setIndicatorImage:(CPImage)anImage
|
- (void)_setIndicatorImage:(CPImage)anImage
|
||||||
{
|
{
|
||||||
if (anImage)
|
if (anImage)
|
||||||
{
|
{
|
||||||
[_textField setImage:anImage];
|
[_textField setImage:anImage];
|
||||||
[_textField setImagePosition:CPImageRight];
|
[_textField setImagePosition:CPImageRight];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[_textField setImagePosition:CPNoImage];
|
[_textField setImagePosition:CPNoImage];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringValueKey",
|
var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringValueKey",
|
||||||
_CPTableColumnHeaderViewFontKey = @"_CPTableColumnHeaderViewFontKey",
|
_CPTableColumnHeaderViewFontKey = @"_CPTableColumnHeaderViewFontKey",
|
||||||
_CPTableColumnHeaderViewTextColorKey = @"_CPTableColumnHeaderViewTextColorKey",
|
|
||||||
_CPTableColumnHeaderViewTextShadowColorKey = @"_CPTableColumnHeaderViewTextShadowColorKey",
|
|
||||||
_CPTableColumnHeaderViewAlignmentKey = @"_CPTableColumnHeaderViewAlignmentKey",
|
|
||||||
_CPTableColumnHeaderViewLineBreakModeKey = @"_CPTableColumnHeaderViewLineBreakModeKey",
|
|
||||||
_CPTableColumnHeaderViewImageKey = @"_CPTableColumnHeaderViewImageKey";
|
_CPTableColumnHeaderViewImageKey = @"_CPTableColumnHeaderViewImageKey";
|
||||||
|
|
||||||
@implementation _CPTableColumnHeaderView (CPCoding)
|
@implementation _CPTableColumnHeaderView (CPCoding)
|
||||||
@@ -199,10 +134,6 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
[self _setIndicatorImage:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewImageKey]];
|
[self _setIndicatorImage:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewImageKey]];
|
||||||
[self setStringValue:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewStringValueKey]];
|
[self setStringValue:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewStringValueKey]];
|
||||||
[self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
|
[self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
|
||||||
[self setTextColor:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewTextColorKey]];
|
|
||||||
[self setTextShadowColor:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewTextShadowColorKey]];
|
|
||||||
[self setAlignment:[aCoder decodeIntForKey:_CPTableColumnHeaderViewAlignmentKey]];
|
|
||||||
[self setLineBreakMode:[aCoder decodeIntForKey:_CPTableColumnHeaderViewLineBreakModeKey]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -214,11 +145,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
|
|
||||||
[aCoder encodeObject:[_textField text] forKey:_CPTableColumnHeaderViewStringValueKey];
|
[aCoder encodeObject:[_textField text] forKey:_CPTableColumnHeaderViewStringValueKey];
|
||||||
[aCoder encodeObject:[_textField image] forKey:_CPTableColumnHeaderViewImageKey];
|
[aCoder encodeObject:[_textField image] forKey:_CPTableColumnHeaderViewImageKey];
|
||||||
[aCoder encodeObject:[self font] forKey:_CPTableColumnHeaderViewFontKey];
|
[aCoder encodeObject:[_textField font] forKey:_CPTableColumnHeaderViewFontKey];
|
||||||
[aCoder encodeObject:[self textColor] forKey:_CPTableColumnHeaderViewTextColorKey];
|
|
||||||
[aCoder encodeObject:[self textShadowColor] forKey:_CPTableColumnHeaderViewTextShadowColorKey];
|
|
||||||
[aCoder encodeInt:[self alignment] forKey:_CPTableColumnHeaderViewAlignmentKey];
|
|
||||||
[aCoder encodeInt:[self lineBreakMode] forKey:_CPTableColumnHeaderViewLineBreakModeKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -229,7 +156,6 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
CGPoint _previousTrackingLocation;
|
CGPoint _previousTrackingLocation;
|
||||||
int _activeColumn;
|
int _activeColumn;
|
||||||
int _pressedColumn;
|
int _pressedColumn;
|
||||||
int _lastDragDestinationColumnIndex;
|
|
||||||
|
|
||||||
BOOL _isResizing;
|
BOOL _isResizing;
|
||||||
BOOL _isDragging;
|
BOOL _isDragging;
|
||||||
@@ -248,16 +174,14 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return @{
|
return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPColor grayColor]]
|
||||||
@"background-color": [CPNull null],
|
forKeys:[@"background-color", @"divider-color"]];
|
||||||
@"divider-color": [CPColor grayColor],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_init
|
- (void)_init
|
||||||
{
|
{
|
||||||
_mouseDownLocation = CGPointMakeZero();
|
_mouseDownLocation = _CGPointMakeZero();
|
||||||
_previousTrackingLocation = CGPointMakeZero();
|
_previousTrackingLocation = _CGPointMakeZero();
|
||||||
_activeColumn = -1;
|
_activeColumn = -1;
|
||||||
_pressedColumn = -1;
|
_pressedColumn = -1;
|
||||||
|
|
||||||
@@ -288,11 +212,11 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
|
|
||||||
- (CGRect)headerRectOfColumn:(int)aColumnIndex
|
- (CGRect)headerRectOfColumn:(int)aColumnIndex
|
||||||
{
|
{
|
||||||
var headerRect = CGRectMakeCopy([self bounds]),
|
var headerRect = [self bounds],
|
||||||
columnRect = [_tableView rectOfColumn:aColumnIndex];
|
columnRect = [_tableView rectOfColumn:aColumnIndex];
|
||||||
|
|
||||||
headerRect.origin.x = CGRectGetMinX(columnRect);
|
headerRect.origin.x = _CGRectGetMinX(columnRect);
|
||||||
headerRect.size.width = CGRectGetWidth(columnRect);
|
headerRect.size.width = _CGRectGetWidth(columnRect);
|
||||||
|
|
||||||
return headerRect;
|
return headerRect;
|
||||||
}
|
}
|
||||||
@@ -310,11 +234,11 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
- (CGRect)_cursorRectForColumn:(int)column
|
- (CGRect)_cursorRectForColumn:(int)column
|
||||||
{
|
{
|
||||||
if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
|
if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
|
||||||
return CGRectMakeZero();
|
return _CGRectMakeZero();
|
||||||
|
|
||||||
var rect = [self headerRectOfColumn:column];
|
var rect = [self headerRectOfColumn:column];
|
||||||
|
|
||||||
rect.origin.x = CGRectGetMaxX(rect) - 5;
|
rect.origin.x = _CGRectGetMaxX(rect) - 5;
|
||||||
rect.size.width = 20;
|
rect.size.width = 20;
|
||||||
|
|
||||||
return rect;
|
return rect;
|
||||||
@@ -332,9 +256,6 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
{
|
{
|
||||||
var headerView = [_tableView._tableColumns[column] headerView];
|
var headerView = [_tableView._tableColumns[column] headerView];
|
||||||
[headerView setThemeState:CPThemeStateHighlighted];
|
[headerView setThemeState:CPThemeStateHighlighted];
|
||||||
|
|
||||||
if (_tableView._editingColumn == column)
|
|
||||||
[[self window] makeFirstResponder:_tableView];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_pressedColumn = column;
|
_pressedColumn = column;
|
||||||
@@ -354,7 +275,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
currentLocation.x -= 5.0;
|
currentLocation.x -= 5.0;
|
||||||
|
|
||||||
var columnIndex = [self columnAtPoint:currentLocation],
|
var columnIndex = [self columnAtPoint:currentLocation],
|
||||||
shouldResize = [self shouldResizeTableColumn:columnIndex at:CGPointMake(currentLocation.x + 5.0, currentLocation.y)];
|
shouldResize = [self shouldResizeTableColumn:columnIndex at:_CGPointMake(currentLocation.x + 5.0, currentLocation.y)];
|
||||||
|
|
||||||
if (type === CPLeftMouseUp)
|
if (type === CPLeftMouseUp)
|
||||||
{
|
{
|
||||||
@@ -398,7 +319,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
[self continueResizingTableColumn:_activeColumn at:currentLocation];
|
[self continueResizingTableColumn:_activeColumn at:currentLocation];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_activeColumn === columnIndex && CGRectContainsPoint([self headerRectOfColumn:columnIndex], currentLocation))
|
if (_activeColumn === columnIndex && _CGRectContainsPoint([self headerRectOfColumn:columnIndex], currentLocation))
|
||||||
{
|
{
|
||||||
if (_isTrackingColumn && _pressedColumn !== -1)
|
if (_isTrackingColumn && _pressedColumn !== -1)
|
||||||
{
|
{
|
||||||
@@ -418,7 +339,6 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
|
|
||||||
- (void)startTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
- (void)startTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
_lastDragDestinationColumnIndex = -1;
|
|
||||||
[self _setPressedColumn:aColumnIndex];
|
[self _setPressedColumn:aColumnIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,14 +347,14 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
if ([self _shouldDragTableColumn:aColumnIndex at:aPoint])
|
if ([self _shouldDragTableColumn:aColumnIndex at:aPoint])
|
||||||
{
|
{
|
||||||
var columnRect = [self headerRectOfColumn:aColumnIndex],
|
var columnRect = [self headerRectOfColumn:aColumnIndex],
|
||||||
offset = CGPointMakeZero(),
|
offset = _CGPointMakeZero(),
|
||||||
view = [_tableView _dragViewForColumn:aColumnIndex event:[CPApp currentEvent] offset:offset],
|
view = [_tableView _dragViewForColumn:aColumnIndex event:[CPApp currentEvent] offset:offset],
|
||||||
viewLocation = CGPointMakeZero();
|
viewLocation = _CGPointMakeZero();
|
||||||
|
|
||||||
viewLocation.x = ( CGRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
|
viewLocation.x = ( _CGRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
|
||||||
viewLocation.y = CGRectGetMinY(columnRect) + offset.y;
|
viewLocation.y = _CGRectGetMinY(columnRect) + offset.y;
|
||||||
|
|
||||||
[self dragView:view at:viewLocation offset:CGSizeMakeZero() event:[CPApp currentEvent]
|
[self dragView:view at:viewLocation offset:_CGSizeMakeZero() event:[CPApp currentEvent]
|
||||||
pasteboard:[CPPasteboard pasteboardWithName:CPDragPboard] source:self slideBack:YES];
|
pasteboard:[CPPasteboard pasteboardWithName:CPDragPboard] source:self slideBack:YES];
|
||||||
|
|
||||||
return NO;
|
return NO;
|
||||||
@@ -446,7 +366,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
- (BOOL)_shouldStopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
- (BOOL)_shouldStopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
return _isTrackingColumn && _activeColumn === aColumnIndex &&
|
return _isTrackingColumn && _activeColumn === aColumnIndex &&
|
||||||
CGRectContainsPoint([self headerRectOfColumn:aColumnIndex], aPoint);
|
_CGRectContainsPoint([self headerRectOfColumn:aColumnIndex], aPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
- (void)stopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||||
@@ -457,7 +377,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
|
|
||||||
- (BOOL)_shouldDragTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
- (BOOL)_shouldDragTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
return ABS(aPoint.x - _mouseDownLocation.x) >= 10.0 && [_tableView _shouldReorderColumn:aColumnIndex toColumn:-1];
|
return [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= 10.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)_headerRectOfLastVisibleColumn
|
- (CGRect)_headerRectOfLastVisibleColumn
|
||||||
@@ -490,10 +410,10 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
frame.origin = [self convertPoint:frame.origin fromView:nil];
|
frame.origin = [self convertPoint:frame.origin fromView:nil];
|
||||||
|
|
||||||
// This effectively clamps the value between the minimum and maximum
|
// This effectively clamps the value between the minimum and maximum
|
||||||
frame.origin.x = MAX(0.0, MIN(CGRectGetMinX(frame), CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect)));
|
frame.origin.x = MAX(0.0, MIN(_CGRectGetMinX(frame), _CGRectGetMaxX(lastColumnRect) - _CGRectGetWidth(activeColumnRect)));
|
||||||
|
|
||||||
// Make sure the column cannot move vertically
|
// Make sure the column cannot move vertically
|
||||||
frame.origin.y = CGRectGetMinY(lastColumnRect);
|
frame.origin.y = _CGRectGetMinY(lastColumnRect);
|
||||||
|
|
||||||
// Convert the calculated origin back to the window coordinate system
|
// Convert the calculated origin back to the window coordinate system
|
||||||
frame.origin = [self convertPoint:frame.origin toView:nil];
|
frame.origin = [self convertPoint:frame.origin toView:nil];
|
||||||
@@ -505,12 +425,9 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
|
|
||||||
- (void)_moveColumn:(int)aFromIndex toColumn:(int)aToIndex
|
- (void)_moveColumn:(int)aFromIndex toColumn:(int)aToIndex
|
||||||
{
|
{
|
||||||
if ([_tableView _shouldReorderColumn:aFromIndex toColumn:aToIndex])
|
[_tableView moveColumn:aFromIndex toColumn:aToIndex];
|
||||||
{
|
_activeColumn = aToIndex;
|
||||||
[_tableView moveColumn:aFromIndex toColumn:aToIndex];
|
_pressedColumn = _activeColumn;
|
||||||
_activeColumn = aToIndex;
|
|
||||||
_pressedColumn = _activeColumn;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)draggedView:(CPView)aView beganAt:(CGPoint)aPoint
|
- (void)draggedView:(CPView)aView beganAt:(CGPoint)aPoint
|
||||||
@@ -535,9 +452,9 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
var hoverPoint = CGPointCreateCopy(aPoint);
|
var hoverPoint = CGPointCreateCopy(aPoint);
|
||||||
|
|
||||||
if (aPoint.x < _previousTrackingLocation.x)
|
if (aPoint.x < _previousTrackingLocation.x)
|
||||||
hoverPoint = CGPointMake(CGRectGetMinX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
|
hoverPoint = _CGPointMake(_CGRectGetMinX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
|
||||||
else if (aPoint.x > _previousTrackingLocation.x)
|
else if (aPoint.x > _previousTrackingLocation.x)
|
||||||
hoverPoint = CGPointMake(CGRectGetMaxX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
|
hoverPoint = _CGPointMake(_CGRectGetMaxX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
|
||||||
|
|
||||||
// Convert the hover point from the global coordinate system to windows' coordinate system
|
// Convert the hover point from the global coordinate system to windows' coordinate system
|
||||||
hoverPoint = [[self window] convertGlobalToBase:hoverPoint];
|
hoverPoint = [[self window] convertGlobalToBase:hoverPoint];
|
||||||
@@ -546,21 +463,14 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
|
|
||||||
var hoveredColumn = [self columnAtPoint:hoverPoint];
|
var hoveredColumn = [self columnAtPoint:hoverPoint];
|
||||||
|
|
||||||
if (hoveredColumn !== _lastDragDestinationColumnIndex && hoveredColumn !== -1)
|
if (hoveredColumn !== -1)
|
||||||
{
|
{
|
||||||
var columnRect = [self headerRectOfColumn:hoveredColumn],
|
var columnRect = [self headerRectOfColumn:hoveredColumn],
|
||||||
columnCenterPoint = [self convertPoint:CGPointMake(CGRectGetMidX(columnRect), CGRectGetMidY(columnRect)) fromView:self];
|
columnCenterPoint = [self convertPoint:CGPointMake(_CGRectGetMidX(columnRect), _CGRectGetMidY(columnRect)) fromView:self];
|
||||||
|
|
||||||
if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
|
if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
|
||||||
{
|
|
||||||
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
||||||
_lastDragDestinationColumnIndex = hoveredColumn;
|
|
||||||
}
|
|
||||||
else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
|
else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
|
||||||
{
|
|
||||||
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
||||||
_lastDragDestinationColumnIndex = hoveredColumn;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_previousTrackingLocation = aPoint;
|
_previousTrackingLocation = aPoint;
|
||||||
@@ -576,8 +486,6 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
[self stopTrackingTableColumn:_activeColumn at:aLocation];
|
[self stopTrackingTableColumn:_activeColumn at:aLocation];
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
|
|
||||||
[_tableView _enqueueDraggingViews];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)shouldResizeTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
- (BOOL)shouldResizeTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||||
@@ -588,7 +496,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
if (_isTrackingColumn)
|
if (_isTrackingColumn)
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
return [_tableView allowsColumnResizing] && CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
|
return [_tableView allowsColumnResizing] && _CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)startResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
- (void)startResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||||
@@ -644,7 +552,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
mouseOverLocation = CGPointMake(mouseLocation.x - 5, mouseLocation.y),
|
mouseOverLocation = CGPointMake(mouseLocation.x - 5, mouseLocation.y),
|
||||||
overColumn = [self columnAtPoint:mouseOverLocation];
|
overColumn = [self columnAtPoint:mouseOverLocation];
|
||||||
|
|
||||||
if (overColumn >= 0 && CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
|
if (overColumn >= 0 && _CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
|
||||||
{
|
{
|
||||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
|
var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
|
||||||
width = [tableColumn width];
|
width = [tableColumn width];
|
||||||
@@ -684,19 +592,19 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var column = [tableColumns objectAtIndex:i],
|
var column = [tableColumns objectAtIndex:i],
|
||||||
headerView = [column headerView],
|
headerView = [column headerView];
|
||||||
frame = [self headerRectOfColumn:i];
|
|
||||||
|
|
||||||
// Make space for the gridline on the right.
|
var frame = [self headerRectOfColumn:i];
|
||||||
frame.origin.x -= 0.5;
|
|
||||||
frame.size.width -= 1.0;
|
|
||||||
frame.size.height -= 0.5;
|
frame.size.height -= 0.5;
|
||||||
// Note: we're not adding in intercell spacing here. This setting only affects the regular
|
if (i > 0)
|
||||||
// table cell data views, not the header. Verified in Cocoa on March 29th, 2011.
|
{
|
||||||
|
frame.origin.x += 0.5;
|
||||||
|
frame.size.width -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
[headerView setFrame:frame];
|
[headerView setFrame:frame];
|
||||||
|
|
||||||
if ([headerView superview] != self)
|
if([headerView superview] != self)
|
||||||
[self addSubview:headerView];
|
[self addSubview:headerView];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,17 +634,16 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
columnMaxX;
|
columnMaxX;
|
||||||
|
|
||||||
CGContextBeginPath(context);
|
CGContextBeginPath(context);
|
||||||
|
for(; columnArrayIndex < columnArrayCount; columnArrayIndex++)
|
||||||
for (; columnArrayIndex < columnArrayCount; columnArrayIndex++)
|
|
||||||
{
|
{
|
||||||
// grab each column rect and add vertical lines
|
// grab each column rect and add vertical lines
|
||||||
var columnIndex = columnsArray[columnArrayIndex],
|
var columnIndex = columnsArray[columnArrayIndex],
|
||||||
columnToStroke = [self headerRectOfColumn:columnIndex];
|
columnToStroke = [self headerRectOfColumn:columnIndex];
|
||||||
|
|
||||||
columnMaxX = CGRectGetMaxX(columnToStroke);
|
columnMaxX = _CGRectGetMaxX(columnToStroke);
|
||||||
|
|
||||||
CGContextMoveToPoint(context, FLOOR(columnMaxX) - 0.5, ROUND(CGRectGetMinY(columnToStroke)));
|
CGContextMoveToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMinY(columnToStroke)));
|
||||||
CGContextAddLineToPoint(context, FLOOR(columnMaxX) - 0.5, ROUND(CGRectGetMaxY(columnToStroke)) - 1.0);
|
CGContextAddLineToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMaxY(columnToStroke)));
|
||||||
}
|
}
|
||||||
|
|
||||||
CGContextClosePath(context);
|
CGContextClosePath(context);
|
||||||
@@ -762,15 +669,7 @@ var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey",
|
|||||||
{
|
{
|
||||||
[self _init];
|
[self _init];
|
||||||
_tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
|
_tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
|
||||||
|
_drawsColumnLines = [aCoder decodeBoolForKey:CPTableHeaderViewDrawsColumnLines];
|
||||||
// FIX ME: Take this out before 1.0
|
|
||||||
if ([aCoder containsValueForKey:CPTableHeaderViewDrawsColumnLines])
|
|
||||||
_drawsColumnLines = [aCoder decodeBoolForKey:CPTableHeaderViewDrawsColumnLines];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_drawsColumnLines = YES;
|
|
||||||
CPLog.warn("The tableview header being decoded is using an old cib. Please run Nib2Cib.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
+737
-2554
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user