mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-10 20:57:11 +00:00
Compare commits
6
Commits
v0.9.5-RC5
..
@each
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80097ea2a2 | ||
|
|
ea7257c00d | ||
|
|
2f0de57c92 | ||
|
|
bb659e3c06 | ||
|
|
3c98ed78ee | ||
|
|
458481af4e |
@@ -5,8 +5,3 @@ Demos
|
|||||||
./Aristo
|
./Aristo
|
||||||
WebSite
|
WebSite
|
||||||
.push-package
|
.push-package
|
||||||
*.xcodeproj/*.pbxuser
|
|
||||||
*.xcodeproj/*.perspectivev3
|
|
||||||
xcuserdata/
|
|
||||||
!*.xcodeproj/project.pbxproj
|
|
||||||
*.xCodeSupport/
|
|
||||||
|
|||||||
+2
-12
@@ -24,7 +24,6 @@
|
|||||||
@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"
|
||||||
@@ -32,7 +31,6 @@
|
|||||||
@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 "CPCibLoading.j"
|
@import "CPCibLoading.j"
|
||||||
@@ -59,21 +57,16 @@
|
|||||||
@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"
|
||||||
@@ -81,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 "_CPToolTip.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 "CPArrayController.j"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
+365
-601
@@ -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
|
||||||
@@ -77,587 +75,22 @@ CPCriticalAlertStyle = 2;
|
|||||||
*/
|
*/
|
||||||
@implementation CPAlert : CPView
|
@implementation CPAlert : CPView
|
||||||
{
|
{
|
||||||
BOOL _showHelp @accessors(property=showsHelp);
|
CPPanel _alertPanel;
|
||||||
BOOL _showSuppressionButton @accessors(property=showsSuppressionButton);
|
|
||||||
|
|
||||||
CPAlertStyle _alertStyle @accessors(property=alertStyle);
|
|
||||||
CPString _title @accessors(property=title);
|
|
||||||
CPView _accessoryView @accessors(property=accessoryView);
|
|
||||||
CPImage _icon @accessors(property=icon);
|
|
||||||
|
|
||||||
CPArray _buttons @accessors(property=buttons,readonly);
|
|
||||||
CPCheckBox _suppressionButton @accessors(property=suppressionButton,readonly);
|
|
||||||
|
|
||||||
id _delegate @accessors(property=delegate);
|
|
||||||
id _modalDelegate;
|
|
||||||
SEL _didEndSelector;
|
|
||||||
|
|
||||||
CPWindow _window @accessors(property=window,readonly);
|
|
||||||
int _defaultWindowStyle;
|
|
||||||
|
|
||||||
CPImageView _alertImageView;
|
|
||||||
CPTextField _informativeLabel;
|
|
||||||
CPTextField _messageLabel;
|
CPTextField _messageLabel;
|
||||||
CPButton _alertHelpButton;
|
CPTextField _informativeLabel;
|
||||||
|
CPImageView _alertImageView;
|
||||||
|
|
||||||
BOOL _needsLayout;
|
CPAlertStyle _alertStyle;
|
||||||
|
CPString _windowTitle;
|
||||||
|
int _windowStyle;
|
||||||
|
CPArray _buttons;
|
||||||
|
|
||||||
|
id _delegate;
|
||||||
|
SEL _didEndSelector;
|
||||||
|
id _modalDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Creating Alerts
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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 alert = [[self alloc] init];
|
|
||||||
|
|
||||||
[alert setMessageText:aMessage];
|
|
||||||
[alert addButtonWithTitle:defaultButtonTitle];
|
|
||||||
|
|
||||||
if (alternateButtonTitle)
|
|
||||||
[alert addButtonWithTitle:alternateButtonTitle];
|
|
||||||
|
|
||||||
if (otherButtonTitle)
|
|
||||||
[alert addButtonWithTitle:otherButtonTitle];
|
|
||||||
|
|
||||||
if (informativeText)
|
|
||||||
[alert setInformativeText:informativeText];
|
|
||||||
|
|
||||||
return alert;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Return an CPAlert with type error
|
|
||||||
|
|
||||||
@param anErrorMessage the message of the alert
|
|
||||||
@return fully initialized CPAlert
|
|
||||||
*/
|
|
||||||
+ (CPAlert)alertWithError:(CPString)anErrorMessage
|
|
||||||
{
|
|
||||||
var alert = [[self alloc] init];
|
|
||||||
|
|
||||||
[alert setMessageText:anErrorMessage];
|
|
||||||
[alert setAlertStyle:CPCriticalAlertStyle];
|
|
||||||
|
|
||||||
return alert;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes a \c CPAlert panel with the default alert style \c CPWarningAlertStyle.
|
|
||||||
*/
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_buttons = [];
|
|
||||||
_alertStyle = CPWarningAlertStyle;
|
|
||||||
_showHelp = NO;
|
|
||||||
_needsLayout = YES;
|
|
||||||
_defaultWindowStyle = CPTitledWindowMask;
|
|
||||||
|
|
||||||
_messageLabel = [CPTextField labelWithTitle:@"Alert"];
|
|
||||||
_alertImageView = [[CPImageView alloc] init];
|
|
||||||
_informativeLabel = [[CPTextField alloc] init];
|
|
||||||
_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Accessors
|
|
||||||
|
|
||||||
/*!
|
|
||||||
set the theme to use
|
|
||||||
|
|
||||||
@param the theme to use
|
|
||||||
*/
|
|
||||||
- (void)setTheme:(CPTheme)aTheme
|
|
||||||
{
|
|
||||||
if (aTheme === [self theme])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (aTheme === [CPTheme defaultHudTheme])
|
|
||||||
_defaultWindowStyle = CPTitledWindowMask | CPHUDBackgroundWindowMask;
|
|
||||||
else
|
|
||||||
_defaultWindowStyle = CPTitledWindowMask;
|
|
||||||
|
|
||||||
_window = nil; // will be regenerated at next layout
|
|
||||||
_needsLayout = YES;
|
|
||||||
[super setTheme:aTheme];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @deprecated
|
|
||||||
*/
|
|
||||||
- (void)setWindowStyle:(int)aStyle
|
|
||||||
{
|
|
||||||
CPLog.warn("DEPRECATED: setWindowStyle: is deprecated. use setTheme: instead");
|
|
||||||
[self setTheme:(aStyle === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @deprecated
|
|
||||||
*/
|
|
||||||
- (int)windowStyle
|
|
||||||
{
|
|
||||||
CPLog.warn("DEPRECATED: windowStyle: is deprecated. use theme instead");
|
|
||||||
return _defaultWindowStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
set the text of the alert's message
|
|
||||||
|
|
||||||
@param aText CPString containing the text
|
|
||||||
*/
|
|
||||||
- (void)setMessageText:(CPString)aText
|
|
||||||
{
|
|
||||||
[_messageLabel setStringValue:aText];
|
|
||||||
_needsLayout = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
return the content of the message text
|
|
||||||
@return CPString containing the message text
|
|
||||||
*/
|
|
||||||
- (CPString)messageText
|
|
||||||
{
|
|
||||||
return [_messageLabel stringValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
set the text of the alert's informative text
|
|
||||||
|
|
||||||
@param aText CPString containing the informative text
|
|
||||||
*/
|
|
||||||
- (void)setInformativeText:(CPString)aText
|
|
||||||
{
|
|
||||||
[_informativeLabel setStringValue:aText];
|
|
||||||
_needsLayout = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
return the content of the message text
|
|
||||||
|
|
||||||
@return CPString containing the message text
|
|
||||||
*/
|
|
||||||
- (CPString)informativeText
|
|
||||||
{
|
|
||||||
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.
|
|
||||||
Buttons will be added starting from the right hand side of the \c CPAlert panel.
|
|
||||||
The first button will have the index 0, the second button 1 and so on.
|
|
||||||
|
|
||||||
The first button will automatically be given a key equivalent of Return,
|
|
||||||
and any button titled "Cancel" will be given a key equivalent of Escape.
|
|
||||||
|
|
||||||
You really shouldn't need more than 3 buttons.
|
|
||||||
|
|
||||||
@param title the title of the button
|
|
||||||
*/
|
|
||||||
- (void)addButtonWithTitle:(CPString)aTitle
|
|
||||||
{
|
|
||||||
var bounds = [[_window contentView] bounds],
|
|
||||||
count = [_buttons count],
|
|
||||||
|
|
||||||
button = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
|
|
||||||
[button setTitle:aTitle];
|
|
||||||
[button setTag:count];
|
|
||||||
[button setTarget:self];
|
|
||||||
[button setAction:@selector(_takeReturnCodeFrom:)];
|
|
||||||
|
|
||||||
[[_window contentView] addSubview:button];
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
[button setKeyEquivalent:CPCarriageReturnCharacter];
|
|
||||||
else if ([aTitle lowercaseString] === @"cancel")
|
|
||||||
[button setKeyEquivalent:CPEscapeFunctionKey];
|
|
||||||
|
|
||||||
[_buttons insertObject:button atIndex:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Layout
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_layoutMessageView
|
|
||||||
{
|
|
||||||
var inset = [self currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
sizeWithFontCorrection = 6.0,
|
|
||||||
messageLabelWidth,
|
|
||||||
messageLabelTextSize;
|
|
||||||
|
|
||||||
[_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]];
|
|
||||||
[_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]];
|
|
||||||
[_messageLabel setTextShadowColor:[self currentValueForThemeAttribute:@"message-text-shadow-color"]];
|
|
||||||
[_messageLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"message-text-shadow-offset"]];
|
|
||||||
[_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]];
|
|
||||||
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
|
||||||
|
|
||||||
messageLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right;
|
|
||||||
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 = [self currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
|
|
||||||
sizeWithFontCorrection = 6.0,
|
|
||||||
informativeLabelWidth,
|
|
||||||
informativeLabelOriginY,
|
|
||||||
informativeLabelTextSize;
|
|
||||||
|
|
||||||
[_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]];
|
|
||||||
[_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]];
|
|
||||||
[_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]];
|
|
||||||
[_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
|
|
||||||
[_informativeLabel setAlignment:[self 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 = [self currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
defaultElementsMargin = [self 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 = [self currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
suppressionViewXOffset = [self currentValueForThemeAttribute:@"suppression-button-x-offset"],
|
|
||||||
suppressionViewYOffset = [self currentValueForThemeAttribute:@"suppression-button-y-offset"],
|
|
||||||
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
|
|
||||||
suppressionButtonViewOriginY = CGRectGetMaxY([(_accessoryView || _informativeLabel) frame]) + defaultElementsMargin + suppressionViewYOffset;
|
|
||||||
|
|
||||||
[_suppressionButton setTextColor:[self currentValueForThemeAttribute:@"suppression-button-text-color"]];
|
|
||||||
[_suppressionButton setFont:[self currentValueForThemeAttribute:@"suppression-button-text-font"]];
|
|
||||||
[_suppressionButton setTextShadowColor:[self currentValueForThemeAttribute:@"suppression-button-text-shadow-color"]];
|
|
||||||
[_suppressionButton setTextShadowOffset:[self 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 = [self currentValueForThemeAttribute:@"content-inset"],
|
|
||||||
minimumSize = [self currentValueForThemeAttribute:@"size"],
|
|
||||||
buttonOffset = [self currentValueForThemeAttribute:@"button-offset"],
|
|
||||||
helpLeftOffset = [self currentValueForThemeAttribute:@"help-image-left-offset"],
|
|
||||||
aRepresentativeButton = [_buttons objectAtIndex:0],
|
|
||||||
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
|
|
||||||
panelSize = [[_window contentView] frame].size,
|
|
||||||
buttonsOriginY,
|
|
||||||
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;
|
|
||||||
|
|
||||||
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, buttonsOriginY, width, height)];
|
|
||||||
offsetX -= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_showHelp)
|
|
||||||
{
|
|
||||||
var helpImage = [self currentValueForThemeAttribute:@"help-image"],
|
|
||||||
helpImagePressed = [self 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 = [self currentValueForThemeAttribute:@"image-offset"],
|
|
||||||
theImage = _icon,
|
|
||||||
finalSize;
|
|
||||||
|
|
||||||
if (!theImage)
|
|
||||||
switch (_alertStyle)
|
|
||||||
{
|
|
||||||
case CPWarningAlertStyle:
|
|
||||||
theImage = [self currentValueForThemeAttribute:@"warning-image"];
|
|
||||||
break;
|
|
||||||
case CPInformationalAlertStyle:
|
|
||||||
theImage = [self currentValueForThemeAttribute:@"information-image"];
|
|
||||||
break;
|
|
||||||
case CPCriticalAlertStyle:
|
|
||||||
theImage = [self currentValueForThemeAttribute:@"error-image"];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[_alertImageView setImage:theImage];
|
|
||||||
|
|
||||||
var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
|
|
||||||
[_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
|
|
||||||
|
|
||||||
[self _layoutMessageView];
|
|
||||||
[self _layoutInformativeView];
|
|
||||||
[self _layoutAccessoryView];
|
|
||||||
[self _layoutSuppressionButton];
|
|
||||||
|
|
||||||
var lastView = _informativeLabel;
|
|
||||||
if (_showSuppressionButton)
|
|
||||||
lastView = _suppressionButton;
|
|
||||||
else if (_accessoryView)
|
|
||||||
lastView = _accessoryView
|
|
||||||
|
|
||||||
finalSize = [self _layoutButtonsFromView:lastView];
|
|
||||||
if ([_window styleMask] & CPDocModalWindowMask)
|
|
||||||
finalSize.height -= 26; // adjust the absence of title bar
|
|
||||||
|
|
||||||
[_window setFrameSize:finalSize];
|
|
||||||
[_window center];
|
|
||||||
|
|
||||||
_needsLayout = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Displaying Alerts
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Displays the \c CPAlert panel as a modal dialog. The user will not be
|
|
||||||
able to interact with any other controls until s/he has dismissed the alert
|
|
||||||
by clicking on one of the buttons.
|
|
||||||
*/
|
|
||||||
- (void)runModal
|
|
||||||
{
|
|
||||||
if (!([_window styleMask] & _defaultWindowStyle))
|
|
||||||
{
|
|
||||||
_needsLayout = YES;
|
|
||||||
[self _createWindowWithStyle:_defaultWindowStyle];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self layout];
|
|
||||||
[CPApp runModalForWindow:_window];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Runs the receiver modally as an alert sheet attached to a specified window.
|
|
||||||
|
|
||||||
@param window The parent window for the sheet.
|
|
||||||
@param modalDelegate The delegate for the modal-dialog session.
|
|
||||||
@param alertDidEndSelector Message the alert sends to modalDelegate after the sheet is dismissed.
|
|
||||||
@param contextInfo Contextual data passed to modalDelegate in didEndSelector message.
|
|
||||||
*/
|
|
||||||
- (void)beginSheetModalForWindow:(CPWindow)aWindow modalDelegate:(id)modalDelegate didEndSelector:(SEL)alertDidEndSelector contextInfo:(id)contextInfo
|
|
||||||
{
|
|
||||||
if (!([_window styleMask] & CPDocModalWindowMask))
|
|
||||||
{
|
|
||||||
_needsLayout = YES;
|
|
||||||
[self _createWindowWithStyle:CPDocModalWindowMask];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self layout];
|
|
||||||
|
|
||||||
_modalDelegate = modalDelegate;
|
|
||||||
_didEndSelector = alertDidEndSelector;
|
|
||||||
|
|
||||||
[CPApp beginSheet:_window modalForWindow:aWindow modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Runs the receiver modally as an alert sheet attached to a specified window.
|
|
||||||
|
|
||||||
@param window The parent window for the sheet.
|
|
||||||
*/
|
|
||||||
- (void)beginSheetModalForWindow:(CPWindow)aWindow
|
|
||||||
{
|
|
||||||
[self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Private
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_createWindowWithStyle:(int)forceStyle
|
|
||||||
{
|
|
||||||
var frame = CGRectMakeZero();
|
|
||||||
frame.size = [self currentValueForThemeAttribute:@"size"];
|
|
||||||
|
|
||||||
_window = [[CPWindow alloc] initWithContentRect:frame styleMask:forceStyle || _defaultWindowStyle];
|
|
||||||
|
|
||||||
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])
|
|
||||||
[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 ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
|
|
||||||
[_delegate alertDidEnd:self returnCode:returnCode];
|
|
||||||
|
|
||||||
if (_didEndSelector)
|
|
||||||
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
|
||||||
|
|
||||||
_modalDelegate = nil;
|
|
||||||
_didEndSelector = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Theme Attributes
|
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)defaultThemeClass
|
||||||
{
|
{
|
||||||
return @"alert";
|
return @"alert";
|
||||||
@@ -671,17 +104,7 @@ CPCriticalAlertStyle = 2;
|
|||||||
CGPointMake(15, 12),
|
CGPointMake(15, 12),
|
||||||
[CPNull null],
|
[CPNull null],
|
||||||
[CPNull null],
|
[CPNull null],
|
||||||
[CPNull null],
|
[CPNull null]
|
||||||
[CPNull null],
|
|
||||||
[CPNull null],
|
|
||||||
[CPNull null],
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
3.0,
|
|
||||||
[CPColor blackColor],
|
|
||||||
[CPFont systemFontOfSize:12.0],
|
|
||||||
[CPNull null],
|
|
||||||
0.0
|
|
||||||
]
|
]
|
||||||
forKeys:[@"size", @"content-inset", @"informative-offset", @"button-offset",
|
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",
|
@"message-text-alignment", @"message-text-color", @"message-text-font", @"message-text-shadow-color", @"message-text-shadow-offset",
|
||||||
@@ -689,18 +112,359 @@ CPCriticalAlertStyle = 2;
|
|||||||
@"image-offset",
|
@"image-offset",
|
||||||
@"information-image",
|
@"information-image",
|
||||||
@"warning-image",
|
@"warning-image",
|
||||||
@"error-image",
|
@"error-image"
|
||||||
@"help-image",
|
|
||||||
@"help-image-left-offset",
|
|
||||||
@"help-image-pressed",
|
|
||||||
@"suppression-button-y-offset",
|
|
||||||
@"suppression-button-x-offset",
|
|
||||||
@"default-elements-margin",
|
|
||||||
@"suppression-button-text-color",
|
|
||||||
@"suppression-button-text-font",
|
|
||||||
@"suppression-button-text-shadow-color",
|
|
||||||
@"suppression-button-text-shadow-offset"
|
|
||||||
]];
|
]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Initializes a \c CPAlert panel with the default alert style \c CPWarningAlertStyle.
|
||||||
|
*/
|
||||||
|
- (id)init
|
||||||
|
{
|
||||||
|
if (self = [super init])
|
||||||
|
{
|
||||||
|
_buttons = [CPArray array];
|
||||||
|
_alertStyle = CPWarningAlertStyle;
|
||||||
|
_alertPanel = nil;
|
||||||
|
_windowStyle = nil;
|
||||||
|
_didEndSelector = nil;
|
||||||
|
|
||||||
|
_messageLabel = [CPTextField labelWithTitle:@"Alert"];
|
||||||
|
_alertImageView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
_informativeLabel = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the window appearance. If CPHUDBackgroundWindowMask is set, the default HUD theme
|
||||||
|
will be activated.
|
||||||
|
|
||||||
|
@param styleMask - Either CPHUDBackgroundWindowMask or nil for standard.
|
||||||
|
*/
|
||||||
|
- (void)setWindowStyle:(int)styleMask
|
||||||
|
{
|
||||||
|
_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];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
- (void)setTitle:(CPString)aTitle
|
||||||
|
{
|
||||||
|
_windowTitle = aTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets the window's title.
|
||||||
|
*/
|
||||||
|
- (CPString)title
|
||||||
|
{
|
||||||
|
return _windowTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets the window's style.
|
||||||
|
*/
|
||||||
|
- (int)windowStyle
|
||||||
|
{
|
||||||
|
return _windowStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the receiver’s delegate.
|
||||||
|
@param delegate - Delegate for the alert. nil removes the delegate.
|
||||||
|
*/
|
||||||
|
- (void)setDelegate:(id)delegate
|
||||||
|
{
|
||||||
|
_delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets the receiver's delegate.
|
||||||
|
*/
|
||||||
|
- (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
|
||||||
|
{
|
||||||
|
return [_messageLabel stringValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the receiver's informative text, shown below the message text.
|
||||||
|
@param informativeText - The informative text.
|
||||||
|
*/
|
||||||
|
- (void)setInformativeText:(CPString)informativeText
|
||||||
|
{
|
||||||
|
[_informativeLabel setStringValue:informativeText];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the receiver's informative text.
|
||||||
|
*/
|
||||||
|
- (CPString)informativeText
|
||||||
|
{
|
||||||
|
return [_informativeLabel stringValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Adds a button with a given title to the receiver.
|
||||||
|
Buttons will be added starting from the right hand side of the \c CPAlert panel.
|
||||||
|
The first button will have the index 0, the second button 1 and so on.
|
||||||
|
|
||||||
|
The first button will automatically be given a key equivalent of Return,
|
||||||
|
and any button titled "Cancel" will be given a key equivalent of Escape.
|
||||||
|
|
||||||
|
You really shouldn't need more than 3 buttons.
|
||||||
|
*/
|
||||||
|
- (void)addButtonWithTitle:(CPString)title
|
||||||
|
{
|
||||||
|
var bounds = [[_alertPanel contentView] bounds],
|
||||||
|
button = [[CPButton alloc] initWithFrame:CGRectMakeZero()],
|
||||||
|
_buttonCount = [_buttons count];
|
||||||
|
|
||||||
|
[button setTitle:title];
|
||||||
|
[button setTarget:self];
|
||||||
|
[button setTag:_buttonCount];
|
||||||
|
[button setAction:@selector(_dismissAlert:)];
|
||||||
|
|
||||||
|
[[_alertPanel contentView] addSubview:button];
|
||||||
|
|
||||||
|
if (_buttonCount == 0)
|
||||||
|
[button setKeyEquivalent:CPCarriageReturnCharacter];
|
||||||
|
else if ([title lowercaseString] === "cancel")
|
||||||
|
[button setKeyEquivalent:CPEscapeFunctionKey];
|
||||||
|
else
|
||||||
|
[button setKeyEquivalent:nil];
|
||||||
|
|
||||||
|
[_buttons insertObject:button atIndex:0];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layoutPanel
|
||||||
|
{
|
||||||
|
if (!_alertPanel)
|
||||||
|
[self _createPanel];
|
||||||
|
|
||||||
|
var inset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||||
|
iconOffset = [self currentValueForThemeAttribute:@"image-offset"],
|
||||||
|
theTitle,
|
||||||
|
theImage;
|
||||||
|
|
||||||
|
switch (_alertStyle)
|
||||||
|
{
|
||||||
|
case CPWarningAlertStyle: theImage = [self currentValueForThemeAttribute:@"warning-image"];
|
||||||
|
theTitle = @"Warning";
|
||||||
|
break;
|
||||||
|
case CPInformationalAlertStyle: theImage = [self currentValueForThemeAttribute:@"information-image"];
|
||||||
|
theTitle = @"Information";
|
||||||
|
break;
|
||||||
|
case CPCriticalAlertStyle: theImage = [self currentValueForThemeAttribute:@"error-image"];
|
||||||
|
theTitle = @"Error";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_alertImageView setImage:theImage];
|
||||||
|
|
||||||
|
var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
|
||||||
|
[_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
|
||||||
|
|
||||||
|
[_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle];
|
||||||
|
[_alertPanel setFloatingPanel:YES];
|
||||||
|
[_alertPanel center];
|
||||||
|
|
||||||
|
[_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]];
|
||||||
|
[_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]];
|
||||||
|
[_messageLabel setTextShadowColor:[self currentValueForThemeAttribute:@"message-text-shadow-color"]];
|
||||||
|
[_messageLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"message-text-shadow-offset"]];
|
||||||
|
[_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]];
|
||||||
|
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
||||||
|
|
||||||
|
[_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]];
|
||||||
|
[_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]];
|
||||||
|
[_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]];
|
||||||
|
[_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
|
||||||
|
[_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
||||||
|
|
||||||
|
// FIXME sizeWithFontCorrection shouldn't be needed.
|
||||||
|
var bounds = [[_alertPanel contentView] bounds],
|
||||||
|
offsetX = CGRectGetWidth(bounds) - inset.right,
|
||||||
|
informativeOffset = [self currentValueForThemeAttribute:@"informative-offset"],
|
||||||
|
buttonOffset = [self currentValueForThemeAttribute:@"button-offset"],
|
||||||
|
|
||||||
|
textWidth = offsetX - inset.left,
|
||||||
|
messageSize = [([_messageLabel stringValue] || " ") sizeWithFont:[_messageLabel font] inWidth:textWidth],
|
||||||
|
informationString = [_informativeLabel stringValue],
|
||||||
|
informativeSize = [(informationString || " ") sizeWithFont:[_informativeLabel font] inWidth:textWidth],
|
||||||
|
sizeWithFontCorrection = 6.0;
|
||||||
|
|
||||||
|
[_messageLabel setFrame:CGRectMake(inset.left, inset.top, textWidth, messageSize.height + sizeWithFontCorrection)];
|
||||||
|
[_informativeLabel setFrame:CGRectMake(inset.left, CGRectGetMaxY([_messageLabel frame]) + informativeOffset, textWidth, informativeSize.height + sizeWithFontCorrection)];
|
||||||
|
// Don't let an empty informative label partially cover the buttons.
|
||||||
|
[_informativeLabel setHidden:!informationString];
|
||||||
|
|
||||||
|
var aRepresentativeButton = _buttons[0],
|
||||||
|
buttonY = MAX(CGRectGetMaxY([_alertImageView frame]), CGRectGetMaxY(informationString ? [_informativeLabel frame] : [_messageLabel frame])) + buttonOffset; // the lower of the bottom of the text and the bottom of the icon.
|
||||||
|
|
||||||
|
[aRepresentativeButton setTheme:[self theme]];
|
||||||
|
[aRepresentativeButton sizeToFit];
|
||||||
|
|
||||||
|
// Make the window just tall enough to fit everything. Bit of a hack really.
|
||||||
|
var minimumSize = [self currentValueForThemeAttribute:@"size"],
|
||||||
|
desiredHeight = MAX(minimumSize.height, buttonY + CGRectGetHeight([aRepresentativeButton bounds]) + inset.bottom),
|
||||||
|
deltaY = desiredHeight - CGRectGetHeight(bounds),
|
||||||
|
frameSize = CGSizeMakeCopy([_alertPanel frame].size);
|
||||||
|
|
||||||
|
frameSize.height += deltaY;
|
||||||
|
[_alertPanel setFrameSize:frameSize];
|
||||||
|
|
||||||
|
var count = [_buttons count];
|
||||||
|
|
||||||
|
while (count--)
|
||||||
|
{
|
||||||
|
var button = _buttons[count];
|
||||||
|
[button setTheme:[self theme]];
|
||||||
|
[button sizeToFit];
|
||||||
|
|
||||||
|
var buttonBounds = [button bounds],
|
||||||
|
width = MAX(80.0, CGRectGetWidth(buttonBounds)),
|
||||||
|
height = CGRectGetHeight(buttonBounds);
|
||||||
|
|
||||||
|
offsetX -= width;
|
||||||
|
[button setFrame:CGRectMake(offsetX, buttonY, width, height)];
|
||||||
|
offsetX -= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Displays the \c CPAlert panel as a modal dialog. The user will not be
|
||||||
|
able to interact with any other controls until s/he has dismissed the alert
|
||||||
|
by clicking on one of the buttons.
|
||||||
|
*/
|
||||||
|
- (void)runModal
|
||||||
|
{
|
||||||
|
[self layoutPanel];
|
||||||
|
[CPApp runModalForWindow:_alertPanel];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Runs the receiver modally as an alert sheet attached to a specified window.
|
||||||
|
|
||||||
|
@param window The parent window for the sheet.
|
||||||
|
@param modalDelegate The delegate for the modal-dialog session.
|
||||||
|
@param alertDidEndSelector Message the alert sends to modalDelegate after the sheet is dismissed.
|
||||||
|
@param contextInfo Contextual data passed to modalDelegate in didEndSelector message.
|
||||||
|
*/
|
||||||
|
- (void)beginSheetModalForWindow:(CPWindow)window modalDelegate:(id)modalDelegate didEndSelector:(SEL)alertDidEndSelector contextInfo:(void)contextInfo
|
||||||
|
{
|
||||||
|
if (!(_windowStyle & CPDocModalWindowMask))
|
||||||
|
[self setWindowStyle:CPDocModalWindowMask];
|
||||||
|
[self layoutPanel];
|
||||||
|
|
||||||
|
_didEndSelector = alertDidEndSelector;
|
||||||
|
_modalDelegate = modalDelegate;
|
||||||
|
|
||||||
|
[CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Runs the receiver modally as an alert sheet attached to a specified window.
|
||||||
|
|
||||||
|
@param window The parent window for the sheet.
|
||||||
|
*/
|
||||||
|
- (void)beginSheetModalForWindow:(CPWindow)window
|
||||||
|
{
|
||||||
|
if (!(_windowStyle & CPDocModalWindowMask))
|
||||||
|
[self setWindowStyle:CPDocModalWindowMask];
|
||||||
|
[self layoutPanel];
|
||||||
|
|
||||||
|
[CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_alertDidEnd:(CPWindow)aSheet returnCode:(CPInteger)returnCode contextInfo:(id)contextInfo
|
||||||
|
{
|
||||||
|
if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
|
||||||
|
[_delegate alertDidEnd:self returnCode:returnCode];
|
||||||
|
|
||||||
|
if (_didEndSelector)
|
||||||
|
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
||||||
|
|
||||||
|
_didEndSelector = nil;
|
||||||
|
_modalDelegate = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @ignore */
|
||||||
|
- (void)_dismissAlert:(CPButton)button
|
||||||
|
{
|
||||||
|
if ([_alertPanel isSheet])
|
||||||
|
[CPApp endSheet:_alertPanel returnCode:[button tag]];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[CPApp abortModal];
|
||||||
|
[_alertPanel close];
|
||||||
|
|
||||||
|
[self _alertDidEnd:nil returnCode:[button tag] contextInfo:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+14
-94
@@ -21,7 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPTimer.j>
|
|
||||||
|
|
||||||
@import "CAMediaTimingFunction.j"
|
@import "CAMediaTimingFunction.j"
|
||||||
|
|
||||||
@@ -307,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
|
||||||
@@ -324,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 CubicBezierAtTime(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));
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-148
@@ -57,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:
|
||||||
@@ -90,6 +90,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
CPWindow _previousKeyWindow;
|
CPWindow _previousKeyWindow;
|
||||||
CPWindow _previousMainWindow;
|
CPWindow _previousMainWindow;
|
||||||
|
|
||||||
|
CPMenu _mainMenu;
|
||||||
CPDocumentController _documentController;
|
CPDocumentController _documentController;
|
||||||
|
|
||||||
CPModalSession _currentSession;
|
CPModalSession _currentSession;
|
||||||
@@ -292,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)
|
||||||
@@ -319,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)
|
||||||
@@ -566,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];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -610,10 +574,6 @@ CPRunContinuesResponse = -1002;
|
|||||||
[[anEvent window] sendEvent:anEvent];
|
[[anEvent window] 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])
|
||||||
@@ -677,7 +637,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
*/
|
*/
|
||||||
- (CPMenu)mainMenu
|
- (CPMenu)mainMenu
|
||||||
{
|
{
|
||||||
return [self menu];
|
return _mainMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -685,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];
|
||||||
@@ -877,38 +828,16 @@ CPRunContinuesResponse = -1002;
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Fires a callback function when an event matching a given mask occurs.
|
|
||||||
@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 (not implemented).
|
|
||||||
*/
|
|
||||||
- (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.push(_CPEventListenerMake(aMask, aCallback));
|
_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.
|
|
||||||
|
|
||||||
@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 (not implemented).
|
|
||||||
*/
|
|
||||||
- (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
|
|
||||||
{
|
{
|
||||||
_eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }));
|
_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;
|
||||||
@@ -938,19 +867,6 @@ CPRunContinuesResponse = -1002;
|
|||||||
[aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
|
[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];
|
||||||
@@ -969,31 +885,11 @@ CPRunContinuesResponse = -1002;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
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
|
||||||
{
|
{
|
||||||
if (_fullArgsString !== window.location.hash)
|
if (_fullArgsString !== window.location.hash)
|
||||||
@@ -1002,22 +898,6 @@ CPRunContinuesResponse = -1002;
|
|||||||
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)
|
||||||
@@ -1028,7 +908,7 @@ CPRunContinuesResponse = -1002;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![args isKindOfClass:CPArray])
|
if ([args class] != CPArray)
|
||||||
args = [CPArray arrayWithObject:args];
|
args = [CPArray arrayWithObject:args];
|
||||||
|
|
||||||
_args = args;
|
_args = args;
|
||||||
@@ -1059,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;
|
||||||
@@ -1340,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];
|
||||||
|
|||||||
+67
-563
File diff suppressed because it is too large
Load Diff
@@ -38,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
|
||||||
@@ -184,6 +184,7 @@ var DefaultLineWidth = 1.0;
|
|||||||
CGContextBeginPath(ctx);
|
CGContextBeginPath(ctx);
|
||||||
CGContextAddPath(ctx, _path);
|
CGContextAddPath(ctx, _path);
|
||||||
CGContextSetLineWidth(ctx, [self lineWidth]);
|
CGContextSetLineWidth(ctx, [self lineWidth]);
|
||||||
|
CGContextClosePath(ctx);
|
||||||
CGContextStrokePath(ctx);
|
CGContextStrokePath(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,7 +281,6 @@ var DefaultLineWidth = 1.0;
|
|||||||
{
|
{
|
||||||
CGPathAddPath(_path, nil, other._path);
|
CGPathAddPath(_path, nil, other._path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Remove all path elements; clears path.
|
Remove all path elements; clears path.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+8
-75
@@ -25,7 +25,7 @@
|
|||||||
// CPBoxType
|
// CPBoxType
|
||||||
CPBoxPrimary = 0;
|
CPBoxPrimary = 0;
|
||||||
CPBoxSecondary = 1;
|
CPBoxSecondary = 1;
|
||||||
CPBoxSeparator = 2;
|
CPBoxSeperator = 2;
|
||||||
CPBoxOldStyle = 3;
|
CPBoxOldStyle = 3;
|
||||||
CPBoxCustom = 4;
|
CPBoxCustom = 4;
|
||||||
|
|
||||||
@@ -35,12 +35,6 @@ CPLineBorder = 1;
|
|||||||
CPBezelBorder = 2;
|
CPBezelBorder = 2;
|
||||||
CPGrooveBorder = 3;
|
CPGrooveBorder = 3;
|
||||||
|
|
||||||
/*!
|
|
||||||
@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;
|
||||||
@@ -61,7 +55,6 @@ CPGrooveBorder = 3;
|
|||||||
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];
|
||||||
@@ -96,46 +89,16 @@ CPGrooveBorder = 3;
|
|||||||
|
|
||||||
// Configuring Boxes
|
// Configuring Boxes
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the receiver's border rectangle.
|
|
||||||
|
|
||||||
@return the border rectangle of the box
|
|
||||||
*/
|
|
||||||
- (CPRect)borderRect
|
- (CPRect)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)
|
||||||
@@ -145,41 +108,11 @@ CPGrooveBorder = 3;
|
|||||||
[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)
|
||||||
@@ -301,9 +234,9 @@ CPGrooveBorder = 3;
|
|||||||
|
|
||||||
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 _drawVerticalSeperatorInRect:bounds];
|
return [self _drawVerticalSeperatorInRect:bounds];
|
||||||
else if (CGRectGetHeight(bounds) === 5.0)
|
else if (CGRectGetHeight(bounds) === 5.0)
|
||||||
@@ -332,8 +265,8 @@ CPGrooveBorder = 3;
|
|||||||
CGContextSetStrokeColor(context, [self borderColor]);
|
CGContextSetStrokeColor(context, [self borderColor]);
|
||||||
CGContextSetLineWidth(context, 1.0);
|
CGContextSetLineWidth(context, 1.0);
|
||||||
|
|
||||||
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMinY(aRect) + 0.5);
|
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
|
||||||
CGContextAddLineToPoint(context, CGRectGetWidth(aRect), CGRectGetMinY(aRect) + 0.5);
|
CGContextAddLineToPoint(context, CGRectGetWidth(aRect), CGRectGetMinY(aRect));
|
||||||
CGContextStrokePath(context);
|
CGContextStrokePath(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,8 +277,8 @@ CPGrooveBorder = 3;
|
|||||||
CGContextSetStrokeColor(context, [self borderColor]);
|
CGContextSetStrokeColor(context, [self borderColor]);
|
||||||
CGContextSetLineWidth(context, 1.0);
|
CGContextSetLineWidth(context, 1.0);
|
||||||
|
|
||||||
CGContextMoveToPoint(context, CGRectGetMinX(aRect) + 0.5, CGRectGetMinY(aRect));
|
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
|
||||||
CGContextAddLineToPoint(context, CGRectGetMinX(aRect) + 0.5, CGRectGetHeight(aRect));
|
CGContextAddLineToPoint(context, CGRectGetMinX(aRect), CGRectGetHeight(aRect));
|
||||||
CGContextStrokePath(context);
|
CGContextStrokePath(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+34
-88
@@ -83,47 +83,42 @@
|
|||||||
{
|
{
|
||||||
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:
|
||||||
@@ -304,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];
|
||||||
@@ -436,14 +431,12 @@
|
|||||||
|
|
||||||
- (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
|
||||||
@@ -657,53 +650,6 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPBrowser (CPCoding)
|
|
||||||
|
|
||||||
- (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
|
|
||||||
|
|
||||||
|
|
||||||
var _CPBrowserResizeControlBackgroundImage = nil;
|
var _CPBrowserResizeControlBackgroundImage = nil;
|
||||||
|
|
||||||
@implementation _CPBrowserResizeControl : CPView
|
@implementation _CPBrowserResizeControl : CPView
|
||||||
|
|||||||
+19
-152
@@ -70,13 +70,7 @@ 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.
|
|
||||||
var CPButtonBezelStyleStateMap = [CPDictionary dictionaryWithObjects:[CPButtonStateBezelStyleRounded, nil]
|
|
||||||
forKeys:[CPRoundedBezelStyle, CPRoundRectBezelStyle]];
|
|
||||||
|
|
||||||
|
|
||||||
CPButtonDefaultHeight = 24.0;
|
CPButtonDefaultHeight = 24.0;
|
||||||
CPButtonImageOffset = 3.0;
|
CPButtonImageOffset = 3.0;
|
||||||
@@ -106,11 +100,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
CPString _keyEquivalent;
|
CPString _keyEquivalent;
|
||||||
unsigned _keyEquivalentModifierMask;
|
unsigned _keyEquivalentModifierMask;
|
||||||
|
|
||||||
CPTimer _continuousDelayTimer;
|
|
||||||
CPTimer _continuousTimer;
|
|
||||||
float _periodicDelay;
|
|
||||||
float _periodicInterval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)buttonWithTitle:(CPString)aTitle
|
+ (id)buttonWithTitle:(CPString)aTitle
|
||||||
@@ -140,11 +129,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
forKeys:[@"image", @"image-offset", @"bezel-inset", @"content-inset", @"bezel-color"]];
|
forKeys:[@"image", @"image-offset", @"bezel-inset", @"content-inset", @"bezel-color"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
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];
|
||||||
@@ -157,31 +141,21 @@ CPButtonImageOffset = 3.0;
|
|||||||
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
||||||
[self setValue:CPScaleNone forThemeAttribute:@"image-scaling"];
|
[self setValue:CPScaleNone forThemeAttribute:@"image-scaling"];
|
||||||
|
|
||||||
[self setBezelStyle:CPRoundRectBezelStyle];
|
_controlSize = CPRegularControlSize;
|
||||||
[self setBordered:YES];
|
|
||||||
|
|
||||||
[self _init];
|
_keyEquivalent = @"";
|
||||||
|
_keyEquivalentModifierMask = 0;
|
||||||
|
|
||||||
|
// [self setBezelStyle:CPRoundRectBezelStyle];
|
||||||
|
[self setBordered:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
_controlSize = CPRegularControlSize;
|
|
||||||
|
|
||||||
_keyEquivalent = @"";
|
|
||||||
_keyEquivalentModifierMask = 0;
|
|
||||||
|
|
||||||
// Continuous button defaults.
|
|
||||||
_periodicInterval = 0.05;
|
|
||||||
_periodicDelay = 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
||||||
{
|
{
|
||||||
@@ -205,10 +179,6 @@ 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))
|
||||||
@@ -217,7 +187,7 @@ CPButtonImageOffset = 3.0;
|
|||||||
else if (![anObjectValue isKindOfClass:[CPNumber class]])
|
else if (![anObjectValue isKindOfClass:[CPNumber class]])
|
||||||
anObjectValue = CPOnState;
|
anObjectValue = CPOnState;
|
||||||
|
|
||||||
else if (anObjectValue >= CPOnState)
|
else if (anObjectValue > CPOnState)
|
||||||
anObjectValue = CPOnState
|
anObjectValue = CPOnState
|
||||||
|
|
||||||
else if (anObjectValue < CPOffState)
|
else if (anObjectValue < CPOffState)
|
||||||
@@ -243,13 +213,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
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])
|
||||||
@@ -262,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]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,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)
|
||||||
@@ -309,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;
|
||||||
@@ -464,34 +409,6 @@ 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
|
||||||
{
|
{
|
||||||
[self highlight:YES];
|
[self highlight:YES];
|
||||||
@@ -502,7 +419,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
||||||
{
|
{
|
||||||
[self highlight:NO];
|
[self highlight:NO];
|
||||||
[self invalidateTimers];
|
|
||||||
|
|
||||||
[super stopTracking:lastPoint at:aPoint mouseIsUp:mouseIsUp];
|
[super stopTracking:lastPoint at:aPoint mouseIsUp:mouseIsUp];
|
||||||
|
|
||||||
@@ -510,21 +426,6 @@ CPButtonImageOffset = 3.0;
|
|||||||
[self setNextState];
|
[self setNextState];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (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"];
|
||||||
@@ -560,9 +461,14 @@ CPButtonImageOffset = 3.0;
|
|||||||
return bounds;
|
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)
|
||||||
@@ -586,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
|
||||||
@@ -750,24 +643,10 @@ CPButtonImageOffset = 3.0;
|
|||||||
|
|
||||||
- (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
|
||||||
@@ -782,9 +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";
|
|
||||||
|
|
||||||
@implementation CPButton (CPCoding)
|
@implementation CPButton (CPCoding)
|
||||||
|
|
||||||
@@ -798,7 +675,7 @@ 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];
|
||||||
@@ -814,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];
|
||||||
@@ -836,7 +707,6 @@ 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];
|
||||||
@@ -850,9 +720,6 @@ 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
|
||||||
|
|||||||
@@ -44,14 +44,6 @@ CPCheckBoxImageOffset = 4.0;
|
|||||||
return @"check-box";
|
return @"check-box";
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
|
||||||
{
|
|
||||||
if (theBinding === CPValueBinding)
|
|
||||||
return [_CPCheckBoxValueBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:theBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
- (id)initWithFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:aFrame];
|
self = [super initWithFrame:aFrame];
|
||||||
@@ -95,53 +87,3 @@ CPCheckBoxImageOffset = 4.0;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation _CPCheckBoxValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)theBinding
|
|
||||||
{
|
|
||||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [_info objectForKey:CPOptionsKey],
|
|
||||||
newValue = [destination valueForKeyPath:keyPath],
|
|
||||||
isPlaceholder = CPIsControllerMarker(newValue);
|
|
||||||
|
|
||||||
if (isPlaceholder)
|
|
||||||
{
|
|
||||||
switch (newValue)
|
|
||||||
{
|
|
||||||
case CPMultipleValuesMarker:
|
|
||||||
newValue = CPMixedState;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPNoSelectionMarker:
|
|
||||||
newValue = CPOffState;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPNotApplicableMarker:
|
|
||||||
if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
|
||||||
[CPException raise:CPGenericException reason:@"can't transform non applicable key on: "+_source+" value: "+newValue];
|
|
||||||
|
|
||||||
newValue = CPOffState;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newValue === CPMixedState)
|
|
||||||
{
|
|
||||||
[_source setAllowsMixedState:YES];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Cocoa will always set allowsMixedState to NO
|
|
||||||
// This behavior will be fine for Cappuccino as well if we (like Cocoa)
|
|
||||||
// default the CPConditionallySetsEnabledBindingOption to YES
|
|
||||||
[_source setAllowsMixedState:NO];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[_source setState:newValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|||||||
@@ -131,49 +131,6 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the item prototype to \c anItem
|
Sets the item prototype to \c anItem
|
||||||
|
|
||||||
The item prototype should implement the CPCoding protocol
|
|
||||||
because the item is copied by archiving and unarchiving the
|
|
||||||
prototypal view.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
@implement MyCustomView : CPCollectionViewItem
|
|
||||||
{
|
|
||||||
CPArray items @accessors;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
items = [];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
items = [aCoder decodeObjectForKey:@"KEY"];
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[aCoder encodeObject:items forKey:@"KEY"];
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
This will allow the collection view to create multiple 'clean' copies
|
|
||||||
of the item prototype which will maintain the original values for item
|
|
||||||
and all of the properties archived by the super class.
|
|
||||||
|
|
||||||
@param anItem the new item prototype
|
@param anItem the new item prototype
|
||||||
*/
|
*/
|
||||||
- (void)setItemPrototype:(CPCollectionViewItem)anItem
|
- (void)setItemPrototype:(CPCollectionViewItem)anItem
|
||||||
@@ -246,8 +203,8 @@
|
|||||||
*/
|
*/
|
||||||
- (void)setContent:(CPArray)anArray
|
- (void)setContent:(CPArray)anArray
|
||||||
{
|
{
|
||||||
// reset the _selectionIndexes
|
if (_content == anArray)
|
||||||
[self setSelectionIndexes:[CPIndexSet indexSet]];
|
return;
|
||||||
|
|
||||||
_content = anArray;
|
_content = anArray;
|
||||||
|
|
||||||
@@ -355,8 +312,7 @@
|
|||||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
||||||
[_items[index] setSelected:YES];
|
[_items[index] setSelected:YES];
|
||||||
|
|
||||||
var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
|
[[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
|
||||||
[[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)])
|
if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)])
|
||||||
[_delegate collectionViewDidChangeSelection:self];
|
[_delegate collectionViewDidChangeSelection:self];
|
||||||
@@ -386,7 +342,7 @@
|
|||||||
|
|
||||||
_items = [];
|
_items = [];
|
||||||
|
|
||||||
if (!_itemPrototype)
|
if (!_itemPrototype || !_content)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var index = 0;
|
var index = 0;
|
||||||
@@ -412,7 +368,7 @@
|
|||||||
{
|
{
|
||||||
var width = CGRectGetWidth([self bounds]);
|
var width = CGRectGetWidth([self bounds]);
|
||||||
|
|
||||||
if (width == _tileWidth)
|
if (![_content count] || width == _tileWidth)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We try to fit as many views per row as possible. Any remaining space is then
|
// We try to fit as many views per row as possible. Any remaining space is then
|
||||||
@@ -632,7 +588,9 @@
|
|||||||
_mouseDownEvent = anEvent;
|
_mouseDownEvent = anEvent;
|
||||||
|
|
||||||
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||||
index = [self _indexAtPoint:location];
|
row = FLOOR(location.y / (_itemSize.height + _verticalMargin)),
|
||||||
|
column = FLOOR(location.x / (_itemSize.width + _horizontalMargin)),
|
||||||
|
index = row * _numberOfColumns + column;
|
||||||
|
|
||||||
if (index >= 0 && index < _items.length)
|
if (index >= 0 && index < _items.length)
|
||||||
{
|
{
|
||||||
@@ -768,28 +726,6 @@
|
|||||||
return _delegate;
|
return _delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (CPMenu)menuForEvent:(CPEvent)theEvent
|
|
||||||
{
|
|
||||||
if (![[self delegate] respondsToSelector:@selector(collectionView:menuForItemAtIndex:)])
|
|
||||||
return [super menuForEvent:theEvent];
|
|
||||||
|
|
||||||
var location = [self convertPoint:[theEvent locationInWindow] fromView:nil],
|
|
||||||
index = [self _indexAtPoint:location];
|
|
||||||
|
|
||||||
return [_delegate collectionView:self menuForItemAtIndex:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)_indexAtPoint:(CGPoint)thePoint
|
|
||||||
{
|
|
||||||
var row = FLOOR(thePoint.y / (_itemSize.height + _verticalMargin)),
|
|
||||||
column = FLOOR(thePoint.x / (_itemSize.width + _horizontalMargin));
|
|
||||||
|
|
||||||
return row * _numberOfColumns + column;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPCollectionViewItem)itemAtIndex:(unsigned)anIndex
|
- (CPCollectionViewItem)itemAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
return [_items objectAtIndex:anIndex];
|
return [_items objectAtIndex:anIndex];
|
||||||
@@ -961,21 +897,6 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeK
|
|||||||
|
|
||||||
@implementation CPCollectionView (CPCoding)
|
@implementation CPCollectionView (CPCoding)
|
||||||
|
|
||||||
- (void)awakeFromCib
|
|
||||||
{
|
|
||||||
[super awakeFromCib];
|
|
||||||
|
|
||||||
if (CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()) || CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
|
|
||||||
{
|
|
||||||
var item = _itemPrototype;
|
|
||||||
|
|
||||||
if (CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
|
|
||||||
_minItemSize = [[item view] frameSize];
|
|
||||||
else if (CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
|
|
||||||
_maxItemSize = [[item view] frameSize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super initWithCoder:aCoder];
|
self = [super initWithCoder:aCoder];
|
||||||
|
|||||||
+10
-115
@@ -27,7 +27,6 @@
|
|||||||
@import "CPCompatibility.j"
|
@import "CPCompatibility.j"
|
||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
|
|
||||||
/// @cond IGNORE
|
|
||||||
|
|
||||||
var _redComponent = 0,
|
var _redComponent = 0,
|
||||||
_greenComponent = 1,
|
_greenComponent = 1,
|
||||||
@@ -55,66 +54,6 @@ var cachedBlackColor,
|
|||||||
cachedShadowColor,
|
cachedShadowColor,
|
||||||
cachedClearColor;
|
cachedClearColor;
|
||||||
|
|
||||||
/// @endcond
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Orientation to use with \c CPColorPattern for vertical patterns.
|
|
||||||
*/
|
|
||||||
CPColorPatternIsVertical = YES;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Orientation to use with \c CPColorPattern for horizontal patterns.
|
|
||||||
*/
|
|
||||||
CPColorPatternIsHorizontal = NO;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
To create a simple color with a pattern image:
|
|
||||||
|
|
||||||
<code>CPColorWithImages(name, width, height{, bundle})</code>
|
|
||||||
|
|
||||||
To create a color with a three part pattern image:
|
|
||||||
|
|
||||||
<code>CPColorWithImages(slices{, orientation})</code>
|
|
||||||
|
|
||||||
where slices is an array of three [name, width, height{, bundle}] arrays,
|
|
||||||
and orientation is \c CPColorPatternIsVertical or \ref CPColorPatternIsHorizontal.
|
|
||||||
If orientatation is not passed, it defaults to \ref CPColorPatternIsHorizontal.
|
|
||||||
|
|
||||||
To create a color with a nine part pattern image:
|
|
||||||
|
|
||||||
<code>CPColorWithImages(slices);</code>
|
|
||||||
|
|
||||||
where slices is an array of nine [name, width, height{, bundle}] arrays.
|
|
||||||
*/
|
|
||||||
function CPColorWithImages()
|
|
||||||
{
|
|
||||||
if (arguments.length < 3)
|
|
||||||
{
|
|
||||||
var slices = arguments[0],
|
|
||||||
imageSlices = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < slices.length; ++i)
|
|
||||||
{
|
|
||||||
var slice = slices[i];
|
|
||||||
|
|
||||||
imageSlices.push(slice ? CPImageInBundle(slice[0], CGSizeMake(slice[1], slice[2]), slice[3]) : nil);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageSlices.length === 3)
|
|
||||||
return [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:imageSlices isVertical:arguments[1] || CPColorPatternIsHorizontal]];
|
|
||||||
else
|
|
||||||
return [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:imageSlices]];
|
|
||||||
}
|
|
||||||
else if (arguments.length === 3 || arguments.length === 4)
|
|
||||||
{
|
|
||||||
return [CPColor colorWithPatternImage:CPImageInBundle(arguments[0], CGSizeMake(arguments[1], arguments[2]), arguments[3])];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
|
|
||||||
@@ -186,7 +125,7 @@ function CPColorWithImages()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@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.
|
||||||
@@ -477,7 +416,7 @@ function CPColorWithImages()
|
|||||||
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
|
||||||
];
|
];
|
||||||
|
|
||||||
_cssString = aString;
|
_cssString = aString;
|
||||||
@@ -666,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
|
||||||
@@ -677,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];
|
|
||||||
|
|
||||||
description += imgDescription.replace(/^/mg, " ") + ",\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
description = description.substr(0, description.length - 2) + "\n ]\n}";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
description += [patternImage description].replace(/^/mg, " ") + "\n}";
|
|
||||||
|
|
||||||
return description;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -768,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)
|
||||||
|
|
||||||
@@ -802,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)
|
||||||
{
|
{
|
||||||
@@ -845,5 +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);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @endcond
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ CPWheelColorPickerMode = 1;
|
|||||||
*/
|
*/
|
||||||
CPSliderColorPickerMode = 2;
|
CPSliderColorPickerMode = 2;
|
||||||
|
|
||||||
CPColorPickerViewWidth = 265;
|
CPColorPickerViewWidth = 265,
|
||||||
CPColorPickerViewHeight = 370;
|
CPColorPickerViewHeight = 370;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -131,7 +131,7 @@ CPColorPickerViewHeight = 370;
|
|||||||
[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)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +372,6 @@ CPColorPickerViewHeight = 370;
|
|||||||
|
|
||||||
[_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:)];
|
||||||
@@ -535,7 +534,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
|||||||
{
|
{
|
||||||
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];
|
||||||
|
|||||||
+13
-17
@@ -44,20 +44,20 @@ CPHTMLCanvasFeature = 1 << 6;
|
|||||||
CPHTMLContentEditableFeature = 1 << 7;
|
CPHTMLContentEditableFeature = 1 << 7;
|
||||||
CPHTMLDragAndDropFeature = 1 << 8;
|
CPHTMLDragAndDropFeature = 1 << 8;
|
||||||
|
|
||||||
CPJavaScriptInnerTextFeature = 1 << 9;
|
CPJavascriptInnerTextFeature = 1 << 9;
|
||||||
CPJavaScriptTextContentFeature = 1 << 10;
|
CPJavascriptTextContentFeature = 1 << 10;
|
||||||
CPJavaScriptClipboardEventsFeature = 1 << 11;
|
CPJavascriptClipboardEventsFeature = 1 << 11;
|
||||||
CPJavaScriptClipboardAccessFeature = 1 << 12;
|
CPJavascriptClipboardAccessFeature = 1 << 12;
|
||||||
CPJavaScriptCanvasDrawFeature = 1 << 13;
|
CPJavaScriptCanvasDrawFeature = 1 << 13;
|
||||||
CPJavaScriptCanvasTransformFeature = 1 << 14;
|
CPJavaScriptCanvasTransformFeature = 1 << 14;
|
||||||
|
|
||||||
CPVMLFeature = 1 << 15;
|
CPVMLFeature = 1 << 15;
|
||||||
|
|
||||||
CPJavaScriptRemedialKeySupport = 1 << 16;
|
CPJavascriptRemedialKeySupport = 1 << 16;
|
||||||
CPJavaScriptShadowFeature = 1 << 20;
|
CPJavaScriptShadowFeature = 1 << 20;
|
||||||
|
|
||||||
CPJavaScriptNegativeMouseWheelValues = 1 << 22;
|
CPJavaScriptNegativeMouseWheelValues = 1 << 22;
|
||||||
CPJavaScriptMouseWheelValues_8_15 = 1 << 23;
|
CPJavaScriptMouseWheelValues_8_15 = 1 << 23
|
||||||
|
|
||||||
CPOpacityRequiresFilterFeature = 1 << 24;
|
CPOpacityRequiresFilterFeature = 1 << 24;
|
||||||
|
|
||||||
@@ -65,7 +65,6 @@ CPOpacityRequiresFilterFeature = 1 << 24;
|
|||||||
CPInputTypeCanBeChangedFeature = 1 << 25;
|
CPInputTypeCanBeChangedFeature = 1 << 25;
|
||||||
CPHTML5DragAndDropSourceYOffBy1 = 1 << 26;
|
CPHTML5DragAndDropSourceYOffBy1 = 1 << 26;
|
||||||
|
|
||||||
CPSOPDisabledFromFileURLs = 1 << 27;
|
|
||||||
|
|
||||||
var USER_AGENT = "",
|
var USER_AGENT = "",
|
||||||
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
||||||
@@ -93,7 +92,7 @@ else if (typeof window !== "undefined" && window.attachEvent) // Must follow Ope
|
|||||||
|
|
||||||
// Features we can only be sure of with IE (no known independent tests)
|
// Features we can only be sure of with IE (no known independent tests)
|
||||||
PLATFORM_FEATURES |= CPVMLFeature;
|
PLATFORM_FEATURES |= CPVMLFeature;
|
||||||
PLATFORM_FEATURES |= CPJavaScriptRemedialKeySupport;
|
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||||
|
|
||||||
PLATFORM_FEATURES |= CPOpacityRequiresFilterFeature;
|
PLATFORM_FEATURES |= CPOpacityRequiresFilterFeature;
|
||||||
@@ -113,8 +112,8 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
|||||||
if (USER_AGENT.indexOf("Chrome") === -1)
|
if (USER_AGENT.indexOf("Chrome") === -1)
|
||||||
PLATFORM_FEATURES |= CPHTMLDragAndDropFeature;
|
PLATFORM_FEATURES |= CPHTMLDragAndDropFeature;
|
||||||
|
|
||||||
PLATFORM_FEATURES |= CPJavaScriptClipboardEventsFeature;
|
PLATFORM_FEATURES |= CPJavascriptClipboardEventsFeature;
|
||||||
PLATFORM_FEATURES |= CPJavaScriptClipboardAccessFeature;
|
PLATFORM_FEATURES |= CPJavascriptClipboardAccessFeature;
|
||||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||||
|
|
||||||
var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
|
var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
|
||||||
@@ -125,18 +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;
|
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;
|
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||||
|
|
||||||
if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
|
if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
|
||||||
PLATFORM_FEATURES |= CPHTML5DragAndDropSourceYOffBy1;
|
PLATFORM_FEATURES |= CPHTML5DragAndDropSourceYOffBy1;
|
||||||
|
|
||||||
if (USER_AGENT.indexOf("Chrome") === CPNotFound)
|
|
||||||
PLATFORM_FEATURES |= CPSOPDisabledFromFileURLs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// KHTML
|
// KHTML
|
||||||
@@ -182,9 +178,9 @@ if (typeof document != "undefined")
|
|||||||
|
|
||||||
// Detect whether we have innerText or textContent (or neither)
|
// Detect whether we have innerText or textContent (or neither)
|
||||||
if (DOMElement.innerText != undefined)
|
if (DOMElement.innerText != undefined)
|
||||||
PLATFORM_FEATURES |= CPJavaScriptInnerTextFeature;
|
PLATFORM_FEATURES |= CPJavascriptInnerTextFeature;
|
||||||
else if (DOMElement.textContent != undefined)
|
else if (DOMElement.textContent != undefined)
|
||||||
PLATFORM_FEATURES |= CPJavaScriptTextContentFeature;
|
PLATFORM_FEATURES |= CPJavascriptTextContentFeature;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CPFeatureIsCompatible(aFeature)
|
function CPFeatureIsCompatible(aFeature)
|
||||||
|
|||||||
+79
-261
@@ -20,14 +20,12 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "../Foundation/Ref.h"
|
|
||||||
|
|
||||||
@import "../Foundation/CPFormatter.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"
|
||||||
|
|
||||||
|
|
||||||
CPLeftTextAlignment = 0;
|
CPLeftTextAlignment = 0;
|
||||||
CPRightTextAlignment = 1;
|
CPRightTextAlignment = 1;
|
||||||
CPCenterTextAlignment = 2;
|
CPCenterTextAlignment = 2;
|
||||||
@@ -45,21 +43,21 @@ CPLineBreakByTruncatingHead = 3;
|
|||||||
CPLineBreakByTruncatingTail = 4;
|
CPLineBreakByTruncatingTail = 4;
|
||||||
CPLineBreakByTruncatingMiddle = 5;
|
CPLineBreakByTruncatingMiddle = 5;
|
||||||
|
|
||||||
CPTopVerticalTextAlignment = 1;
|
CPTopVerticalTextAlignment = 1,
|
||||||
CPCenterVerticalTextAlignment = 2;
|
CPCenterVerticalTextAlignment = 2,
|
||||||
CPBottomVerticalTextAlignment = 3;
|
CPBottomVerticalTextAlignment = 3;
|
||||||
|
|
||||||
CPScaleProportionally = 0;
|
CPScaleProportionally = 0;
|
||||||
CPScaleToFit = 1;
|
CPScaleToFit = 1;
|
||||||
CPScaleNone = 2;
|
CPScaleNone = 2;
|
||||||
|
|
||||||
CPNoImage = 0;
|
CPNoImage = 0;
|
||||||
CPImageOnly = 1;
|
CPImageOnly = 1;
|
||||||
CPImageLeft = 2;
|
CPImageLeft = 2;
|
||||||
CPImageRight = 3;
|
CPImageRight = 3;
|
||||||
CPImageBelow = 4;
|
CPImageBelow = 4;
|
||||||
CPImageAbove = 5;
|
CPImageAbove = 5;
|
||||||
CPImageOverlaps = 6;
|
CPImageOverlaps = 6;
|
||||||
|
|
||||||
CPOnState = 1;
|
CPOnState = 1;
|
||||||
CPOffState = 0;
|
CPOffState = 0;
|
||||||
@@ -74,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
|
||||||
@@ -85,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;
|
||||||
@@ -98,6 +95,8 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
BOOL _trackingWasWithinFrame;
|
BOOL _trackingWasWithinFrame;
|
||||||
unsigned _trackingMouseDownFlags;
|
unsigned _trackingMouseDownFlags;
|
||||||
CGPoint _previousTrackingLocation;
|
CGPoint _previousTrackingLocation;
|
||||||
|
|
||||||
|
CPString _toolTip;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPDictionary)themeAttributes
|
+ (CPDictionary)themeAttributes
|
||||||
@@ -142,34 +141,20 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
|
||||||
{
|
|
||||||
if (theBinding === CPValueBinding)
|
|
||||||
return [_CPValueBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:theBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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];
|
||||||
@@ -184,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
|
||||||
@@ -194,7 +178,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's target action.
|
Returns the receiver's target action
|
||||||
*/
|
*/
|
||||||
- (SEL)action
|
- (SEL)action
|
||||||
{
|
{
|
||||||
@@ -203,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
|
||||||
@@ -221,15 +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];
|
||||||
return [CPApp sendAction:anAction to:anObject from:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)sendActionOn:(int)mask
|
- (int)sendActionOn:(int)mask
|
||||||
@@ -241,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.
|
||||||
*/
|
*/
|
||||||
@@ -252,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
|
||||||
@@ -262,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;
|
||||||
@@ -281,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];
|
||||||
@@ -327,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])
|
||||||
@@ -354,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;
|
||||||
@@ -396,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
|
||||||
{
|
{
|
||||||
@@ -424,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
|
||||||
{
|
{
|
||||||
@@ -435,7 +404,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's float value.
|
Returns the receiver's float value
|
||||||
*/
|
*/
|
||||||
- (float)floatValue
|
- (float)floatValue
|
||||||
{
|
{
|
||||||
@@ -444,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
|
||||||
{
|
{
|
||||||
@@ -452,7 +421,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's double value.
|
Returns the receiver's double value
|
||||||
*/
|
*/
|
||||||
- (double)doubleValue
|
- (double)doubleValue
|
||||||
{
|
{
|
||||||
@@ -461,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
|
||||||
{
|
{
|
||||||
@@ -469,7 +438,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's int value.
|
Returns the receiver's int value
|
||||||
*/
|
*/
|
||||||
- (int)intValue
|
- (int)intValue
|
||||||
{
|
{
|
||||||
@@ -478,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
|
||||||
{
|
{
|
||||||
@@ -486,7 +455,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the receiver's int value.
|
Returns the receiver's int value
|
||||||
*/
|
*/
|
||||||
- (int)integerValue
|
- (int)integerValue
|
||||||
{
|
{
|
||||||
@@ -495,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
|
||||||
{
|
{
|
||||||
@@ -503,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 && _value !== nil)
|
|
||||||
{
|
|
||||||
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 sent to CPControl -setStringValue");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var value;
|
|
||||||
|
|
||||||
if (_formatter)
|
|
||||||
{
|
|
||||||
value = nil;
|
|
||||||
|
|
||||||
if ([_formatter getObjectValue:AT_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:AT_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
|
||||||
@@ -562,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)])
|
||||||
@@ -615,199 +556,96 @@ var CPControlBlackColor = [CPColor blackColor];
|
|||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
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>
|
|
||||||
CPScaleProportionally
|
|
||||||
CPScaleToFit
|
|
||||||
CPScaleNone
|
|
||||||
</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)
|
||||||
@@ -816,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)
|
||||||
@@ -847,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,7 +695,6 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
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
|
||||||
*/
|
*/
|
||||||
@@ -897,7 +718,6 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
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
|
||||||
@@ -907,15 +727,13 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
|||||||
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];
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ var CPControllerDeclaredKeysKey = @"CPControllerDeclaredKeysKey";
|
|||||||
_declaredKeys = [aDecoder decodeObjectForKey:CPControllerDeclaredKeysKey] || [];
|
_declaredKeys = [aDecoder decodeObjectForKey:CPControllerDeclaredKeysKey] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEditing
|
- (BOOL)isEditing
|
||||||
|
|||||||
+203
-174
@@ -1,41 +1,36 @@
|
|||||||
/*
|
/*
|
||||||
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>
|
||||||
|
|
||||||
var currentCursor = nil,
|
var currentCursor = nil,
|
||||||
cursorStack = [],
|
cursorStack = [],
|
||||||
cursors = {};
|
cursors = {},
|
||||||
|
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:);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (CPCursor)currentCursor
|
||||||
|
{
|
||||||
|
return currentCursor;
|
||||||
|
}
|
||||||
|
|
||||||
- (id)initWithCSSString:(CPString)aString
|
- (id)initWithCSSString:(CPString)aString
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
@@ -44,18 +39,155 @@ var currentCursor = nil,
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
// hotspot is supported in CSS3 (but not IE).
|
+ (CPCursor)cursorWithCSSString:(CPString)cssString
|
||||||
- (id)initWithImage:(CPImage)image hotSpot:(CPPoint)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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// foregroundColor and backgroundColor are ignored in Cocoa as well. See http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSCursor_Class/Reference/Reference.html
|
+ (CPCursor)cursorWithImageNamed:(CPString)imageName
|
||||||
- (id)initWithImage:(CPImage)image foregroundColorHint:(CPColor)foregroundColor backgroundColorHint:(CPColor)backgroundColor hotSpot:(CPPoint)aHotSpot
|
|
||||||
{
|
{
|
||||||
return [self initWithImage:image hotSpot:hotSpot];
|
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
|
||||||
@@ -76,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];
|
||||||
@@ -90,148 +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)
|
|
||||||
cssString = @"url(" + [[CPBundle bundleForClass:self] resourcePath] + @"/CPCursor/" + cursorName + ".cur), " + 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)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)resizeUpDownCursor
|
|
||||||
{
|
|
||||||
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"row-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])
|
||||||
@@ -246,3 +274,4 @@ var currentCursor = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -88,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.
|
||||||
*/
|
*/
|
||||||
@@ -208,7 +208,7 @@ 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
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
@import <Foundation/CPBundle.j>
|
@import <Foundation/CPBundle.j>
|
||||||
|
|
||||||
@import "CPDocument.j"
|
@import "CPDocument.j"
|
||||||
@import "CPOpenPanel.j"
|
@import "CPOpenPanel.j";
|
||||||
|
|
||||||
|
|
||||||
var CPSharedDocumentController = nil;
|
var CPSharedDocumentController = nil;
|
||||||
@@ -177,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
|
||||||
{
|
{
|
||||||
@@ -390,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];
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-28
@@ -28,13 +28,13 @@
|
|||||||
@import "CPWindow.j"
|
@import "CPWindow.j"
|
||||||
|
|
||||||
|
|
||||||
CPDragOperationNone = 0;
|
CPDragOperationNone = 0,
|
||||||
CPDragOperationCopy = 1 << 1;
|
CPDragOperationCopy = 1 << 1,
|
||||||
CPDragOperationLink = 1 << 1;
|
CPDragOperationLink = 1 << 1,
|
||||||
CPDragOperationGeneric = 1 << 2;
|
CPDragOperationGeneric = 1 << 2,
|
||||||
CPDragOperationPrivate = 1 << 3;
|
CPDragOperationPrivate = 1 << 3,
|
||||||
CPDragOperationMove = 1 << 4;
|
CPDragOperationMove = 1 << 4,
|
||||||
CPDragOperationDelete = 1 << 5;
|
CPDragOperationDelete = 1 << 5,
|
||||||
CPDragOperationEvery = -1;
|
CPDragOperationEvery = -1;
|
||||||
|
|
||||||
#define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window])
|
#define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window])
|
||||||
@@ -44,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.
|
||||||
@@ -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];
|
||||||
@@ -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;
|
||||||
@@ -270,10 +270,10 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
{
|
{
|
||||||
if ([scrollView hasVerticalScroller])
|
if ([scrollView hasVerticalScroller])
|
||||||
{
|
{
|
||||||
if (eventLocation.y < _CGRectGetMinY(insetBounds))
|
if (eventLocation.y < CGRectGetMinY(insetBounds))
|
||||||
deltaY = _CGRectGetMinY(insetBounds) - eventLocation.y;
|
deltaY = CGRectGetMinY(insetBounds) - eventLocation.y;
|
||||||
else if (eventLocation.y > _CGRectGetMaxY(insetBounds))
|
else if (eventLocation.y > CGRectGetMaxY(insetBounds))
|
||||||
deltaY = _CGRectGetMaxY(insetBounds) - eventLocation.y;
|
deltaY = CGRectGetMaxY(insetBounds) - eventLocation.y;
|
||||||
if (deltaY < -insetBounds.size.height)
|
if (deltaY < -insetBounds.size.height)
|
||||||
deltaY = -insetBounds.size.height;
|
deltaY = -insetBounds.size.height;
|
||||||
if (deltaY > insetBounds.size.height)
|
if (deltaY > insetBounds.size.height)
|
||||||
@@ -282,21 +282,17 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
|||||||
|
|
||||||
if ([scrollView hasHorizontalScroller])
|
if ([scrollView hasHorizontalScroller])
|
||||||
{
|
{
|
||||||
if (eventLocation.x < _CGRectGetMinX(insetBounds))
|
if (eventLocation.x < CGRectGetMinX(insetBounds))
|
||||||
deltaX = _CGRectGetMinX(insetBounds) - eventLocation.x;
|
deltaX = CGRectGetMinX(insetBounds) - eventLocation.x;
|
||||||
else if (eventLocation.x > _CGRectGetMaxX(insetBounds))
|
else if (eventLocation.x > CGRectGetMaxX(insetBounds))
|
||||||
deltaX = _CGRectGetMaxX(insetBounds) - eventLocation.x;
|
deltaX = CGRectGetMaxX(insetBounds) - eventLocation.x;
|
||||||
if (deltaX < -insetBounds.size.width)
|
if (deltaX < -insetBounds.size.width)
|
||||||
deltaX = -insetBounds.size.width;
|
deltaX = -insetBounds.size.width;
|
||||||
if (deltaX > insetBounds.size.width)
|
if (deltaX > insetBounds.size.width)
|
||||||
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];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -460,8 +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 (characters === CPEscapeFunctionKey)
|
if (keyCode === CPEscapeKeyCode)
|
||||||
{
|
{
|
||||||
_dragOperation = CPDragOperationNone;
|
_dragOperation = CPDragOperationNone;
|
||||||
[self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation];
|
[self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation];
|
||||||
|
|||||||
+60
-74
@@ -158,24 +158,23 @@ CPFindFunctionKey = "\uF745";
|
|||||||
CPHelpFunctionKey = "\uF746";
|
CPHelpFunctionKey = "\uF746";
|
||||||
CPModeSwitchFunctionKey = "\uF747";
|
CPModeSwitchFunctionKey = "\uF747";
|
||||||
CPEscapeFunctionKey = "\u001B";
|
CPEscapeFunctionKey = "\u001B";
|
||||||
CPSpaceFunctionKey = "\u0020";
|
|
||||||
|
|
||||||
|
|
||||||
CPDOMEventDoubleClick = "dblclick";
|
CPDOMEventDoubleClick = "dblclick",
|
||||||
CPDOMEventMouseDown = "mousedown";
|
CPDOMEventMouseDown = "mousedown",
|
||||||
CPDOMEventMouseUp = "mouseup";
|
CPDOMEventMouseUp = "mouseup",
|
||||||
CPDOMEventMouseMoved = "mousemove";
|
CPDOMEventMouseMoved = "mousemove",
|
||||||
CPDOMEventMouseDragged = "mousedrag";
|
CPDOMEventMouseDragged = "mousedrag",
|
||||||
CPDOMEventKeyUp = "keyup";
|
CPDOMEventKeyUp = "keyup",
|
||||||
CPDOMEventKeyDown = "keydown";
|
CPDOMEventKeyDown = "keydown",
|
||||||
CPDOMEventKeyPress = "keypress";
|
CPDOMEventKeyPress = "keypress";
|
||||||
CPDOMEventCopy = "copy";
|
CPDOMEventCopy = "copy";
|
||||||
CPDOMEventPaste = "paste";
|
CPDOMEventPaste = "paste";
|
||||||
CPDOMEventScrollWheel = "mousewheel";
|
CPDOMEventScrollWheel = "mousewheel";
|
||||||
CPDOMEventTouchStart = "touchstart";
|
CPDOMEventTouchStart = "touchstart";
|
||||||
CPDOMEventTouchMove = "touchmove";
|
CPDOMEventTouchMove = "touchmove";
|
||||||
CPDOMEventTouchEnd = "touchend";
|
CPDOMEventTouchEnd = "touchend";
|
||||||
CPDOMEventTouchCancel = "touchcancel";
|
CPDOMEventTouchCancel = "touchcancel";
|
||||||
|
|
||||||
var _CPEventPeriodicEventPeriod = 0,
|
var _CPEventPeriodicEventPeriod = 0,
|
||||||
_CPEventPeriodicEventTimer = nil,
|
_CPEventPeriodicEventTimer = nil,
|
||||||
@@ -211,7 +210,6 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a new keyboard event.
|
Creates a new keyboard event.
|
||||||
|
|
||||||
@param anEventType the event type. Must be one of CPKeyDown, CPKeyUp or CPFlagsChanged
|
@param anEventType the event type. Must be one of CPKeyDown, CPKeyUp or CPFlagsChanged
|
||||||
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
|
@param aPoint the location of the cursor in the window specified by \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
|
||||||
@@ -236,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
|
||||||
@@ -259,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
|
||||||
@@ -281,28 +277,16 @@ 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:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||||
eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
|
eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
|
||||||
{
|
{
|
||||||
if (self = [self _initWithType:anEventType])
|
self = [super init];
|
||||||
|
|
||||||
|
if (self)
|
||||||
{
|
{
|
||||||
|
_type = anEventType;
|
||||||
_location = CPPointCreateCopy(aPoint);
|
_location = CPPointCreateCopy(aPoint);
|
||||||
_modifierFlags = modifierFlags;
|
_modifierFlags = modifierFlags;
|
||||||
_timestamp = aTimestamp;
|
_timestamp = aTimestamp;
|
||||||
@@ -321,8 +305,11 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
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)
|
||||||
{
|
{
|
||||||
|
_type = anEventType;
|
||||||
_location = CPPointCreateCopy(aPoint);
|
_location = CPPointCreateCopy(aPoint);
|
||||||
_modifierFlags = modifierFlags;
|
_modifierFlags = modifierFlags;
|
||||||
_timestamp = aTimestamp;
|
_timestamp = aTimestamp;
|
||||||
@@ -342,8 +329,11 @@ 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)
|
||||||
{
|
{
|
||||||
|
_type = anEventType;
|
||||||
_location = CPPointCreateCopy(aPoint);
|
_location = CPPointCreateCopy(aPoint);
|
||||||
_modifierFlags = modifierFlags;
|
_modifierFlags = modifierFlags;
|
||||||
_timestamp = aTimestamp;
|
_timestamp = aTimestamp;
|
||||||
@@ -358,10 +348,10 @@ 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
|
||||||
@@ -381,7 +371,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns event information as a bit mask.
|
Returns event information as a bit mask
|
||||||
*/
|
*/
|
||||||
- (unsigned)modifierFlags
|
- (unsigned)modifierFlags
|
||||||
{
|
{
|
||||||
@@ -389,7 +379,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the time the event occurred.
|
Returns the time the event occurred
|
||||||
*/
|
*/
|
||||||
- (CPTimeInterval)timestamp
|
- (CPTimeInterval)timestamp
|
||||||
{
|
{
|
||||||
@@ -405,7 +395,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the event's associated window.
|
Returns the event's associated window
|
||||||
*/
|
*/
|
||||||
- (CPWindow)window
|
- (CPWindow)window
|
||||||
{
|
{
|
||||||
@@ -436,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
|
||||||
{
|
{
|
||||||
@@ -444,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
|
||||||
@@ -455,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
|
||||||
@@ -465,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
|
||||||
@@ -475,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
|
||||||
@@ -508,7 +494,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
return _DOMEvent;
|
return _DOMEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getting Scroll Wheel Event Information
|
// Getting Scroll Wheel Event Infomration
|
||||||
/*!
|
/*!
|
||||||
Returns the change in the x-axis for a mouse event.
|
Returns the change in the x-axis for a mouse event.
|
||||||
*/
|
*/
|
||||||
@@ -541,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;
|
||||||
@@ -571,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
|
||||||
*/
|
*/
|
||||||
@@ -609,7 +594,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Stops the periodic events from being generated.
|
Stops the periodic events from being generated
|
||||||
*/
|
*/
|
||||||
+ (void)stopPeriodicEvents
|
+ (void)stopPeriodicEvents
|
||||||
{
|
{
|
||||||
@@ -647,3 +632,4 @@ function _CPEventFromNativeMouseEvent(aNativeEvent, anEventType, aPoint, modifie
|
|||||||
|
|
||||||
return aNativeEvent;
|
return aNativeEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,10 +134,7 @@ 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))
|
||||||
{
|
{
|
||||||
@@ -146,7 +143,7 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
|||||||
var enumerator = [_params keyEnumerator],
|
var enumerator = [_params keyEnumerator],
|
||||||
key;
|
key;
|
||||||
|
|
||||||
while (_DOMObjectElement && (key = [enumerator nextObject]))
|
while (key = [enumerator nextObject] && _DOMObjectElement)
|
||||||
{
|
{
|
||||||
var param = document.createElement(@"param");
|
var param = document.createElement(@"param");
|
||||||
param.name = key;
|
param.name = key;
|
||||||
|
|||||||
+29
-186
@@ -25,49 +25,20 @@
|
|||||||
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
CPFontDefaultSystemFontFace = @"Arial, sans-serif";
|
|
||||||
CPFontDefaultSystemFontSize = 12;
|
|
||||||
|
|
||||||
var _CPFonts = {},
|
var _CPFonts = {},
|
||||||
_CPFontSystemFontFace = CPFontDefaultSystemFontFace,
|
_CPFontSystemFontFace = @"Arial, sans-serif",
|
||||||
_CPFontSystemFontSize = 12,
|
_CPWrapRegExp = new RegExp("\\s*,\\s*", "g");
|
||||||
_CPFontFallbackFaces = CPFontDefaultSystemFontFace.split(", "),
|
|
||||||
_CPFontStripRegExp = new RegExp("(^\\s*[\"']?|[\"']?\\s*$)", "g");
|
|
||||||
|
|
||||||
|
|
||||||
#define _CPFontNormalizedNames(aName) _CPFontNormalizedNameArray(aName).join(", ")
|
#define _CPCreateCSSString(aName, aSize, isBold) (isBold ? @"bold " : @"") + ROUND(aSize) + @"px " + ((aName === _CPFontSystemFontFace) ? aName : (@"\"" + aName.replace(_CPWrapRegExp, '", "') + @"\", " + _CPFontSystemFontFace))
|
||||||
#define _CPCachedFont(aName, aSize, isBold, isItalic) _CPFonts[_CPFontCreateCSSString(_CPFontNormalizedNames(aName), aSize, isBold, isItalic)]
|
#define _CPCachedFont(aName, aSize, isBold) _CPFonts[_CPCreateCSSString(aName, aSize, isBold)]
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPFont
|
@class CPFont
|
||||||
|
|
||||||
The CPFont class allows control of the fonts used for displaying text anywhere on the screen.
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
By default the system font face/size is Arial 12px, with a fallback to sans-serif. You may
|
|
||||||
configure this at runtime in two ways:
|
|
||||||
|
|
||||||
- By sending [CPFont @link CPFont::setSystemFontFace: setSystemFontFace:@endlink] and/or [CPFont @link CPFont::setSystemFontSize: setSystemFontSize:@endlink].
|
|
||||||
- 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.
|
|
||||||
The browser will use the first available font in the list. CPFont always ensures that Arial
|
|
||||||
and sans-serif are in the generated CSS string, so there is no need to add them to the end
|
|
||||||
of your font list.
|
|
||||||
|
|
||||||
If you are using nib2cib, you should use the second method (using Info.plist),
|
|
||||||
and be sure to run nib2cib again any time you modify the CPSystemFontFace or CPSystemFontSize items.
|
|
||||||
For example, you might add this to your application's Info.plist to set the system font to Lucida Grande,
|
|
||||||
with an automatic fallback to Arial or sans-serif:
|
|
||||||
|
|
||||||
@code
|
|
||||||
<key>CPSystemFontFace</key>
|
|
||||||
<string>Lucida Grande</string>
|
|
||||||
@endcode
|
|
||||||
*/
|
*/
|
||||||
@implementation CPFont : CPObject
|
@implementation CPFont : CPObject
|
||||||
{
|
{
|
||||||
@@ -76,153 +47,78 @@ var _CPFonts = {},
|
|||||||
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);
|
|
||||||
|
|
||||||
CPString _cssString;
|
CPString _cssString;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
var systemFontFace = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontFace"];
|
var systemFont = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:"CPSystemFontFace"];
|
||||||
|
|
||||||
if (!systemFontFace)
|
if (systemFont)
|
||||||
systemFontFace = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontFace"];
|
_CPFontSystemFontFace = systemFont;
|
||||||
|
|
||||||
if (systemFontFace)
|
|
||||||
[self setSystemFontFace: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
|
|
||||||
{
|
|
||||||
_CPFontSystemFontFace = _CPFontNormalizedNames(aFace);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the default system font size.
|
|
||||||
*/
|
|
||||||
+ (float)systemFontSize
|
|
||||||
{
|
|
||||||
return _CPFontSystemFontSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the default system font size.
|
|
||||||
*/
|
|
||||||
+ (float)setSystemFontSize:(float)size
|
|
||||||
{
|
|
||||||
if (size > 0)
|
|
||||||
_CPFontSystemFontSize = size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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)
|
@param aSize the size of the font (in points)
|
||||||
@return the requested font
|
@return the requested font
|
||||||
*/
|
*/
|
||||||
+ (CPFont)fontWithName:(CPString)aName size:(float)aSize
|
+ (CPFont)fontWithName:(CPString)aName size:(float)aSize
|
||||||
{
|
{
|
||||||
return _CPCachedFont(aName, aSize, NO, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:NO italic: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)
|
|
||||||
@param italic whether the font should be italicized
|
|
||||||
@return the requested font
|
|
||||||
*/
|
|
||||||
+ (CPFont)fontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic
|
|
||||||
{
|
|
||||||
return _CPCachedFont(aName, aSize, NO, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:NO italic: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)
|
@param aSize the size of the font (in points)
|
||||||
@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 _CPCachedFont(aName, aSize, YES, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:YES italic: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)
|
|
||||||
@param italic whether the font should be italicized
|
|
||||||
@return the requested font
|
|
||||||
*/
|
|
||||||
+ (CPFont)boldFontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic
|
|
||||||
{
|
|
||||||
return _CPCachedFont(aName, aSize, NO, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:YES italic: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)
|
@param aSize the size of the font (in points)
|
||||||
@return the requested system font
|
@return the requested system font
|
||||||
*/
|
*/
|
||||||
+ (CPFont)systemFontOfSize:(CPSize)aSize
|
+ (CPFont)systemFontOfSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
return _CPCachedFont(_CPFontSystemFontFace, aSize, NO, NO) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:NO italic: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)
|
@param aSize the size of the font (in points)
|
||||||
@return the requested bold system font
|
@return the requested bold system font
|
||||||
*/
|
*/
|
||||||
+ (CPFont)boldSystemFontOfSize:(CPSize)aSize
|
+ (CPFont)boldSystemFontOfSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
return _CPCachedFont(_CPFontSystemFontFace, aSize, YES, NO) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:YES italic:NO];
|
return _CPCachedFont(_CPFontSystemFontFace, aSize, YES) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME Font Descriptor
|
/* FIXME Font Descriptor
|
||||||
@ignore
|
@ignore
|
||||||
*/
|
*/
|
||||||
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold
|
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold
|
||||||
{
|
|
||||||
return [self _initWithName:aName size:aSize bold:isBold italic:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold italic:(BOOL)isItalic
|
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_name = _CPFontNormalizedNames(aName);
|
_name = aName;
|
||||||
_size = aSize;
|
_size = aSize;
|
||||||
_ascender = 0;
|
_ascender = 0;
|
||||||
_descender = 0;
|
_descender = 0;
|
||||||
_lineHeight = 0;
|
_lineHeight = 0;
|
||||||
_isBold = isBold;
|
_isBold = isBold;
|
||||||
_isItalic = isItalic;
|
|
||||||
|
|
||||||
_cssString = _CPFontCreateCSSString(_name, _size, _isBold, _isItalic);
|
_cssString = _CPCreateCSSString(_name, _size, _isBold);
|
||||||
|
|
||||||
_CPFonts[_cssString] = self;
|
_CPFonts[_cssString] = self;
|
||||||
}
|
}
|
||||||
@@ -243,7 +139,7 @@ var _CPFonts = {},
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
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
|
||||||
{
|
{
|
||||||
@@ -292,17 +188,12 @@ var _CPFonts = {},
|
|||||||
|
|
||||||
- (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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_getMetrics
|
- (void)_getMetrics
|
||||||
@@ -316,10 +207,9 @@ var _CPFonts = {},
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPFontNameKey = @"CPFontNameKey",
|
var CPFontNameKey = @"CPFontNameKey",
|
||||||
CPFontSizeKey = @"CPFontSizeKey",
|
CPFontSizeKey = @"CPFontSizeKey",
|
||||||
CPFontIsBoldKey = @"CPFontIsBoldKey",
|
CPFontIsBoldKey = @"CPFontIsBoldKey";
|
||||||
CPFontIsItalicKey = @"CPFontIsItalicKey";
|
|
||||||
|
|
||||||
@implementation CPFont (CPCoding)
|
@implementation CPFont (CPCoding)
|
||||||
|
|
||||||
@@ -330,12 +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];
|
|
||||||
|
|
||||||
return [self _initWithName:fontName size:size bold:isBold italic:isItalic];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -347,50 +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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
// aName must be normalized
|
|
||||||
var _CPFontCreateCSSString = function(aName, aSize, isBold, isItalic)
|
|
||||||
{
|
|
||||||
var properties = (isItalic ? "italic " : "") + (isBold ? "bold " : "") + 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;
|
|
||||||
};
|
|
||||||
|
|||||||
+22
-28
@@ -75,13 +75,12 @@ 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"]);
|
||||||
|
|
||||||
_availableFonts = [];
|
_availableFonts = [];
|
||||||
for (var i = 0; i < _CPFontDetectAllFonts.length; i++)
|
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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,44 +136,39 @@ var _CPFontDetectSpan,
|
|||||||
"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
-1
@@ -71,7 +71,7 @@ function CPPointMake(x, y)
|
|||||||
function CPRectInset(aRect, dX, dY)
|
function CPRectInset(aRect, dX, dY)
|
||||||
{
|
{
|
||||||
return CPRectMake( aRect.origin.x + dX, aRect.origin.y + dY,
|
return CPRectMake( aRect.origin.x + dX, aRect.origin.y + dY,
|
||||||
aRect.size.width - 2 * dX, aRect.size.height - 2 * dY);
|
aRect.size.width - 2 * dX, aRect.size.height - 2*dY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
@import "CGContext.j"
|
@import "CGContext.j"
|
||||||
|
|
||||||
|
|
||||||
var CPGraphicsContextCurrent = nil;
|
var CPGraphicsContextCurrent = nil;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -33,7 +33,7 @@ var CPGraphicsContextCurrent = nil;
|
|||||||
*/
|
*/
|
||||||
@implementation CPGraphicsContext : CPObject
|
@implementation CPGraphicsContext : CPObject
|
||||||
{
|
{
|
||||||
CGContext _graphicsPort;
|
CPContext _graphicsPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
+1
-20
@@ -44,8 +44,7 @@ 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);
|
||||||
@@ -325,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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -357,8 +357,7 @@ var LEFT_SHADOW_INSET = 3.0,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var x,
|
var x, y;
|
||||||
y;
|
|
||||||
|
|
||||||
switch (_imageAlignment)
|
switch (_imageAlignment)
|
||||||
{
|
{
|
||||||
|
|||||||
+92
-141
@@ -35,22 +35,20 @@ var exposedBindingsMap = [CPDictionary new],
|
|||||||
var CPBindingOperationAnd = 0,
|
var CPBindingOperationAnd = 0,
|
||||||
CPBindingOperationOr = 1;
|
CPBindingOperationOr = 1;
|
||||||
|
|
||||||
@implementation CPBinder : CPObject
|
@implementation CPKeyValueBinding : CPObject
|
||||||
{
|
{
|
||||||
CPDictionary _info;
|
CPDictionary _info;
|
||||||
id _source;
|
id _source;
|
||||||
|
|
||||||
JSObject _suppressedNotifications;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (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);
|
||||||
@@ -58,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
|
||||||
@@ -78,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;
|
||||||
@@ -103,7 +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;
|
||||||
|
|
||||||
@@ -113,7 +111,7 @@ 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
|
||||||
@@ -124,18 +122,17 @@ var CPBindingOperationAnd = 0,
|
|||||||
{
|
{
|
||||||
_source = aSource;
|
_source = aSource;
|
||||||
_info = [CPDictionary dictionaryWithObjects:[aDestination, aKeyPath] forKeys:[CPObservedObjectKey, CPObservedKeyPathKey]];
|
_info = [CPDictionary dictionaryWithObjects:[aDestination, aKeyPath] forKeys:[CPObservedObjectKey, CPObservedKeyPathKey]];
|
||||||
_suppressedNotifications = {};
|
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
[_info setObject:options forKey:CPOptionsKey];
|
[_info setObject:options forKey:CPOptionsKey];
|
||||||
|
|
||||||
[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 = [CPDictionary new];
|
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];
|
||||||
@@ -150,10 +147,39 @@ 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 = [destination valueForKeyPath:keyPath];
|
newValue = [destination valueForKeyPath:keyPath],
|
||||||
|
isPlaceholder = CPIsControllerMarker(newValue);
|
||||||
|
|
||||||
|
if (isPlaceholder)
|
||||||
|
{
|
||||||
|
switch (newValue)
|
||||||
|
{
|
||||||
|
case CPMultipleValuesMarker:
|
||||||
|
newValue = [options objectForKey:CPMultipleValuesPlaceholderBindingOption] || @"Multiple Values";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CPNoSelectionMarker:
|
||||||
|
newValue = [options objectForKey:CPNoSelectionPlaceholderBindingOption] || @"No Selection";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CPNotApplicableMarker:
|
||||||
|
if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
||||||
|
[CPException raise:CPGenericException reason:@"can't transform non applicable key on: "+_source+" value: "+newValue];
|
||||||
|
|
||||||
|
newValue = [options objectForKey:CPNotApplicablePlaceholderBindingOption] || @"Not Applicable";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Only transform the value if the current value is not a placeholder
|
||||||
|
newValue = [self transformValue:newValue withOptions:options];
|
||||||
|
}
|
||||||
|
|
||||||
newValue = [self transformValue:newValue withOptions:options];
|
|
||||||
[_source setValue:newValue forKey:aBinding];
|
[_source setValue:newValue forKey:aBinding];
|
||||||
|
|
||||||
|
if ([_source respondsToSelector:@selector(_setCurrentValueIsPlaceholder:)])
|
||||||
|
[_source _setCurrentValueIsPlaceholder:isPlaceholder];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reverseSetValueFor:(CPString)aBinding
|
- (void)reverseSetValueFor:(CPString)aBinding
|
||||||
@@ -172,15 +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;
|
||||||
|
|
||||||
@@ -205,7 +231,7 @@ var CPBindingOperationAnd = 0,
|
|||||||
aValue = [valueTransformer transformedValue:aValue];
|
aValue = [valueTransformer transformedValue:aValue];
|
||||||
|
|
||||||
|
|
||||||
if (aValue === undefined || aValue === nil || aValue === [CPNull null])
|
if (aValue === undefined || aValue === nil)
|
||||||
aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
|
aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
|
||||||
|
|
||||||
return aValue;
|
return aValue;
|
||||||
@@ -227,62 +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];
|
|
||||||
}
|
|
||||||
|
|
||||||
@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)theBinding
|
|
||||||
{
|
|
||||||
return [CPBinder class];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPArray)exposedBindings
|
- (CPArray)exposedBindings
|
||||||
@@ -292,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];
|
||||||
@@ -311,26 +288,23 @@ 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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)_replacementKeyPathForBinding:(CPString)binding
|
- (id)_replacementKeyPathForBinding:(CPString)binding
|
||||||
@@ -340,35 +314,13 @@ var CPBindingOperationAnd = 0,
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/*!
|
@implementation _CPKeyValueOrBinding : CPKeyValueBinding
|
||||||
@ignore
|
|
||||||
Provides stub implementations that simply call super for the "objectValue" binding
|
|
||||||
This class should not be necessary but assures backwards compliance with our old way of doing bindings
|
|
||||||
Every class with a value binding should implement a subclass to handle it's specific value binding logic
|
|
||||||
*/
|
|
||||||
@implementation _CPValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)theBinding
|
|
||||||
{
|
|
||||||
[super setValueFor:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reverseSetValueFor:(CPString)theBinding
|
|
||||||
{
|
|
||||||
[super reverseSetValueFor:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPKeyValueOrBinding : CPBinder
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
- (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;
|
||||||
@@ -383,13 +335,13 @@ var CPBindingOperationAnd = 0,
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation _CPKeyValueAndBinding : CPBinder
|
@implementation _CPKeyValueAndBinding : CPKeyValueBinding
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)aBinding
|
- (void)setValueFor:(CPString)aBinding
|
||||||
{
|
{
|
||||||
var bindings = [bindingsMap objectForKey:[_source UID]];
|
var bindings = [bindingsMap objectForKey:[_source hash]];
|
||||||
|
|
||||||
if (!bindings)
|
if (!bindings)
|
||||||
return;
|
return;
|
||||||
@@ -476,46 +428,45 @@ CPOptionsKey = @"CPOptionsKey";
|
|||||||
CPMultipleValuesMarker = @"CPMultipleValuesMarker";
|
CPMultipleValuesMarker = @"CPMultipleValuesMarker";
|
||||||
CPNoSelectionMarker = @"CPNoSelectionMarker";
|
CPNoSelectionMarker = @"CPNoSelectionMarker";
|
||||||
CPNotApplicableMarker = @"CPNotApplicableMarker";
|
CPNotApplicableMarker = @"CPNotApplicableMarker";
|
||||||
CPNullMarker = @"CPNullMarker";
|
|
||||||
|
|
||||||
// Binding name constants
|
// Binding name constants
|
||||||
CPAlignmentBinding = @"alignment";
|
CPAlignmentBinding = @"CPAlignmentBinding";
|
||||||
CPEditableBinding = @"editable";
|
CPEditableBinding = @"CPEditableBinding";
|
||||||
CPEnabledBinding = @"enabled";
|
CPEnabledBinding = @"CPEnabledBinding";
|
||||||
CPFontBinding = @"font";
|
CPFontBinding = @"CPFontBinding";
|
||||||
CPHiddenBinding = @"hidden";
|
CPHiddenBinding = @"CPHiddenBinding";
|
||||||
CPSelectedIndexBinding = @"selectedIndex";
|
CPSelectedIndexBinding = @"CPSelectedIndexBinding";
|
||||||
CPTextColorBinding = @"textColor";
|
CPTextColorBinding = @"CPTextColorBinding";
|
||||||
CPToolTipBinding = @"toolTip";
|
CPToolTipBinding = @"CPToolTipBinding";
|
||||||
CPValueBinding = @"value";
|
CPValueBinding = @"value";
|
||||||
|
|
||||||
//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;
|
||||||
}
|
}
|
||||||
@@ -1,412 +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"
|
|
||||||
|
|
||||||
CPTickMarkBelow = 0;
|
|
||||||
CPTickMarkAbove = 1;
|
|
||||||
CPTickMarkLeft = CPTickMarkAbove;
|
|
||||||
CPTickMarkRight = CPTickMarkBelow;
|
|
||||||
|
|
||||||
CPRelevancyLevelIndicatorStyle = 0;
|
|
||||||
CPContinuousCapacityLevelIndicatorStyle = 1;
|
|
||||||
CPDiscreteCapacityLevelIndicatorStyle = 2;
|
|
||||||
CPRatingLevelIndicatorStyle = 3;
|
|
||||||
|
|
||||||
var _CPLevelIndicatorBezelColor = nil,
|
|
||||||
_CPLevelIndicatorSegmentEmptyColor = nil,
|
|
||||||
_CPLevelIndicatorSegmentNormalColor = nil,
|
|
||||||
_CPLevelIndicatorSegmentWarningColor = nil,
|
|
||||||
_CPLevelIndicatorSegmentCriticalColor = nil,
|
|
||||||
|
|
||||||
_CPLevelIndicatorSpacing = 1;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
var bundle = [CPBundle bundleForClass:CPLevelIndicator];
|
|
||||||
|
|
||||||
_CPLevelIndicatorBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
|
||||||
[
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-left.png"] size:CGSizeMake(3.0, 18.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-center.png"] size:CGSizeMake(1.0, 18.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-right.png"] size:CGSizeMake(3.0, 18.0)]
|
|
||||||
]
|
|
||||||
isVertical:NO
|
|
||||||
]];
|
|
||||||
|
|
||||||
_CPLevelIndicatorSegmentEmptyColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
|
||||||
[
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-empty-left.png"] size:CGSizeMake(3.0, 17.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-empty-center.png"] size:CGSizeMake(1.0, 17.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-empty-right.png"] size:CGSizeMake(3.0, 17.0)]
|
|
||||||
]
|
|
||||||
isVertical:NO
|
|
||||||
]];
|
|
||||||
|
|
||||||
_CPLevelIndicatorSegmentNormalColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
|
||||||
[
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-normal-left.png"] size:CGSizeMake(3.0, 17.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-normal-center.png"] size:CGSizeMake(1.0, 17.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-normal-right.png"] size:CGSizeMake(3.0, 17.0)]
|
|
||||||
]
|
|
||||||
isVertical:NO
|
|
||||||
]];
|
|
||||||
|
|
||||||
_CPLevelIndicatorSegmentWarningColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
|
||||||
[
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-warning-left.png"] size:CGSizeMake(3.0, 17.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-warning-center.png"] size:CGSizeMake(1.0, 17.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-warning-right.png"] size:CGSizeMake(3.0, 17.0)]
|
|
||||||
]
|
|
||||||
isVertical:NO
|
|
||||||
]];
|
|
||||||
|
|
||||||
_CPLevelIndicatorSegmentCriticalColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
|
||||||
[
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-critical-left.png"] size:CGSizeMake(3.0, 17.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-critical-center.png"] size:CGSizeMake(1.0, 17.0)],
|
|
||||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-critical-right.png"] size:CGSizeMake(3.0, 17.0)]
|
|
||||||
]
|
|
||||||
isVertical:NO
|
|
||||||
]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_levelIndicatorStyle = CPDiscreteCapacityLevelIndicatorStyle;
|
|
||||||
_maxValue = 2;
|
|
||||||
_warningValue = 2;
|
|
||||||
_criticalValue = 2;
|
|
||||||
|
|
||||||
[self _init];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
var bezelView = [self layoutEphemeralSubviewNamed:"bezel"
|
|
||||||
positioned:CPWindowBelow
|
|
||||||
relativeToEphemeralSubviewNamed:nil];
|
|
||||||
// TODO Make themable.
|
|
||||||
[bezelView setBackgroundColor:_CPLevelIndicatorBezelColor];
|
|
||||||
|
|
||||||
var segmentCount = _maxValue - _minValue;
|
|
||||||
|
|
||||||
if (segmentCount <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var filledColor = _CPLevelIndicatorSegmentNormalColor,
|
|
||||||
value = [self doubleValue];
|
|
||||||
|
|
||||||
if (value <= _criticalValue)
|
|
||||||
filledColor = _CPLevelIndicatorSegmentCriticalColor;
|
|
||||||
else if (value <= _warningValue)
|
|
||||||
filledColor = _CPLevelIndicatorSegmentWarningColor;
|
|
||||||
|
|
||||||
for (var i = 0; i < segmentCount; i++)
|
|
||||||
{
|
|
||||||
var segmentView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
|
|
||||||
positioned:CPWindowAbove
|
|
||||||
relativeToEphemeralSubviewNamed:bezelView];
|
|
||||||
|
|
||||||
[segmentView setBackgroundColor:(_minValue + i) < value ? filledColor : _CPLevelIndicatorSegmentEmptyColor];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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]);
|
|
||||||
|
|
||||||
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)) - _CPLevelIndicatorSpacing;
|
|
||||||
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:(CPPoint)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 _init];
|
|
||||||
|
|
||||||
[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
|
|
||||||
+48
-144
@@ -52,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
|
||||||
{
|
{
|
||||||
@@ -80,9 +80,6 @@ var _CPMenuBarVisible = NO,
|
|||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self !== CPMenu)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[[self class] setMenuBarAttributes:[CPDictionary dictionary]];
|
[[self class] setMenuBarAttributes:[CPDictionary dictionary]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +192,7 @@ var _CPMenuBarVisible = NO,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!highlightColor)
|
if (!highlightColor)
|
||||||
[_CPMenuBarAttributes setObject:[CPColor colorWithCalibratedRed:94.0 / 255.0 green:130.0 / 255.0 blue:186.0 / 255.0 alpha:1.0] 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)
|
||||||
[_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarHighlightTextColor"];
|
[_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarHighlightTextColor"];
|
||||||
@@ -277,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"]];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -334,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];
|
|
||||||
|
|
||||||
_highlightedIndex = CPNotFound;
|
|
||||||
|
|
||||||
// Because we are changing _items directly, be sure to notify KVO
|
|
||||||
[self willChangeValueForKey:@"items"];
|
|
||||||
_items = [CPMutableArray array];
|
|
||||||
[self didChangeValueForKey:@"items"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -366,11 +364,9 @@ var _CPMenuBarVisible = NO,
|
|||||||
*/
|
*/
|
||||||
- (void)itemChanged:(CPMenuItem)aMenuItem
|
- (void)itemChanged:(CPMenuItem)aMenuItem
|
||||||
{
|
{
|
||||||
if ([aMenuItem menu] !== self)
|
if ([aMenuItem menu] != self)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[aMenuItem setValue:[aMenuItem valueForKey:@"changeCount"] + 1 forKey:@"changeCount"];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
[[CPNotificationCenter defaultCenter]
|
||||||
postNotificationName:CPMenuDidChangeItemNotification
|
postNotificationName:CPMenuDidChangeItemNotification
|
||||||
object:self
|
object:self
|
||||||
@@ -559,6 +555,7 @@ var _CPMenuBarVisible = NO,
|
|||||||
*/
|
*/
|
||||||
- (void)submenuAction:(id)aSender
|
- (void)submenuAction:(id)aSender
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -606,34 +603,11 @@ var _CPMenuBarVisible = NO,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Enables or disables the receiver’s menu items.
|
Not implemented.
|
||||||
If the target does not implement the menu item's action method the item is disabled.
|
|
||||||
If the target responsds 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 || ![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
|
||||||
@@ -715,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)
|
||||||
@@ -803,7 +780,10 @@ 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:12.0];
|
aFont = [CPFont systemFontOfSize:12.0];
|
||||||
@@ -873,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
|
||||||
@@ -896,25 +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
|
|
||||||
{
|
|
||||||
var delegate = [self delegate];
|
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(menuDidClose:)])
|
|
||||||
[delegate menuDidClose:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling Tracking
|
// Handling Tracking
|
||||||
/*!
|
/*!
|
||||||
Cancels tracking.
|
Cancels tracking.
|
||||||
*/
|
*/
|
||||||
- (void)cancelTracking
|
- (void)cancelTracking
|
||||||
{
|
{
|
||||||
@@ -974,7 +930,7 @@ 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];
|
modifierMask = [item keyEquivalentModifierMask];
|
||||||
@@ -1011,6 +967,14 @@ var _CPMenuBarVisible = NO,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
/*
|
||||||
|
@ignore
|
||||||
|
*/
|
||||||
|
- (BOOL)_itemIsHighlighted:(CPMenuItem)aMenuItem
|
||||||
|
{
|
||||||
|
return _items[_highlightedIndex] == aMenuItem;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ignore
|
@ignore
|
||||||
*/
|
*/
|
||||||
@@ -1026,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
|
||||||
@@ -1074,61 +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];
|
|
||||||
[_items insertObject:aMenuItem atIndex:anIndex];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
|
||||||
postNotificationName:CPMenuDidAddItemNotification
|
|
||||||
object:self
|
|
||||||
userInfo:[CPDictionary dictionaryWithObject:anIndex forKey:@"CPMenuItemIndex"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObjectFromItemsAtIndex:(CPUInteger)anIndex
|
|
||||||
{
|
|
||||||
if (anIndex < 0 || anIndex >= [_items count])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[[_items objectAtIndex:anIndex] setMenu:nil];
|
|
||||||
[_items removeObjectAtIndex:anIndex];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter]
|
|
||||||
postNotificationName:CPMenuDidRemoveItemNotification
|
|
||||||
object:self
|
|
||||||
userInfo:[CPDictionary dictionaryWithObject:anIndex forKey:@"CPMenuItemIndex"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPMenuTitleKey = @"CPMenuTitleKey",
|
var CPMenuTitleKey = @"CPMenuTitleKey",
|
||||||
CPMenuNameKey = @"CPMenuNameKey",
|
CPMenuNameKey = @"CPMenuNameKey",
|
||||||
@@ -1155,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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+45
-351
@@ -1,17 +1,17 @@
|
|||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
|
|
||||||
|
|
||||||
_CPMenuManagerScrollingStateUp = -1;
|
_CPMenuManagerScrollingStateUp = -1,
|
||||||
_CPMenuManagerScrollingStateDown = 1;
|
_CPMenuManagerScrollingStateDown = 1,
|
||||||
_CPMenuManagerScrollingStateNone = 0;
|
_CPMenuManagerScrollingStateNone = 0;
|
||||||
|
|
||||||
var STICKY_TIME_INTERVAL = 500,
|
var STICKY_TIME_INTERVAL = 500;
|
||||||
SharedMenuManager = nil;
|
|
||||||
|
var SharedMenuManager = nil;
|
||||||
|
|
||||||
@implementation _CPMenuManager: CPObject
|
@implementation _CPMenuManager: CPObject
|
||||||
{
|
{
|
||||||
CPTimeInterval _startTime;
|
CPTimeInterval _startTime;
|
||||||
BOOL _hasMouseGoneUpAfterStartedTracking;
|
|
||||||
int _scrollingState;
|
int _scrollingState;
|
||||||
CGPoint _lastGlobalLocation;
|
CGPoint _lastGlobalLocation;
|
||||||
|
|
||||||
@@ -21,11 +21,6 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
CPMutableArray _menuContainerStack;
|
CPMutableArray _menuContainerStack;
|
||||||
|
|
||||||
Function _trackingCallback;
|
Function _trackingCallback;
|
||||||
|
|
||||||
CPString _keyBuffer;
|
|
||||||
|
|
||||||
CPMenuItem _previousActiveItem;
|
|
||||||
int _showTimerID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (_CPMenuManager)sharedMenuManager
|
+ (_CPMenuManager)sharedMenuManager
|
||||||
@@ -61,9 +56,6 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
{
|
{
|
||||||
var menu = [aMenuContainer menu];
|
var menu = [aMenuContainer menu];
|
||||||
|
|
||||||
if ([menu numberOfItems] <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
CPApp._activeMenu = menu;
|
CPApp._activeMenu = menu;
|
||||||
|
|
||||||
_startTime = [anEvent timestamp];//new Date();
|
_startTime = [anEvent timestamp];//new Date();
|
||||||
@@ -75,13 +67,13 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
|
|
||||||
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;
|
||||||
@@ -91,8 +83,6 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
return [self trackMenuBarButtonEvent:anEvent];
|
return [self trackMenuBarButtonEvent:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasMouseGoneUpAfterStartedTracking = NO;
|
|
||||||
|
|
||||||
[self trackEvent:anEvent];
|
[self trackEvent:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,34 +95,7 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
if (type === CPAppKitDefined)
|
if (type === CPAppKitDefined)
|
||||||
return [self completeTracking];
|
return [self completeTracking];
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPKeyDownMask | CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPRightMouseUpMask | CPAppKitDefinedMask | CPScrollWheelMask untilDate:nil inMode:nil dequeue:YES];
|
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES];
|
||||||
|
|
||||||
if (type === CPKeyDown)
|
|
||||||
{
|
|
||||||
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];
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_keyBuffer)
|
|
||||||
{
|
|
||||||
if (([CPDate date] - _startTime) > (STICKY_TIME_INTERVAL + [activeMenu numberOfItems] / 2))
|
|
||||||
[self selectNextItemBeginningWith:_keyBuffer inMenu:menu clearBuffer:YES];
|
|
||||||
|
|
||||||
if (type === CPPeriodic)
|
|
||||||
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];
|
||||||
@@ -140,16 +103,13 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
// Remember this for the next periodic event.
|
// Remember this for the next periodic event.
|
||||||
_lastGlobalLocation = globalLocation;
|
_lastGlobalLocation = globalLocation;
|
||||||
|
|
||||||
if (!_lastGlobalLocation)
|
|
||||||
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;
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -161,13 +121,11 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
|
|
||||||
var mouseOverMenuView = [activeItem view];
|
var mouseOverMenuView = [activeItem view];
|
||||||
|
|
||||||
if (type === CPScrollWheel)
|
|
||||||
[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];
|
||||||
}
|
}
|
||||||
@@ -193,15 +151,13 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
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
|
||||||
{
|
{
|
||||||
@@ -213,7 +169,7 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
|
|
||||||
[activeMenu _highlightItemAtIndex:activeItemIndex];
|
[activeMenu _highlightItemAtIndex:activeItemIndex];
|
||||||
|
|
||||||
if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown || type === CPPeriodic)
|
if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown)
|
||||||
{
|
{
|
||||||
var oldScrollingState = _scrollingState;
|
var oldScrollingState = _scrollingState;
|
||||||
|
|
||||||
@@ -223,71 +179,34 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
{
|
{
|
||||||
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];
|
||||||
if (_hasMouseGoneUpAfterStartedTracking)
|
|
||||||
{
|
|
||||||
// Don't close the menu if the current item has a submenu
|
|
||||||
// and did not override it's default action
|
|
||||||
if ([activeItem action] === @selector(submenuAction:))
|
|
||||||
return;
|
|
||||||
|
|
||||||
[trackingMenu cancelTracking];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_hasMouseGoneUpAfterStartedTracking = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
||||||
@@ -297,15 +216,16 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
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];
|
||||||
|
|
||||||
@@ -322,15 +242,23 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
// 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;
|
||||||
}
|
}
|
||||||
@@ -393,8 +321,6 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
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--)
|
||||||
{
|
{
|
||||||
@@ -414,8 +340,6 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
|
|
||||||
[_CPMenuWindow poolMenuWindow:menuContainer];
|
[_CPMenuWindow poolMenuWindow:menuContainer];
|
||||||
[_menuContainerStack removeObjectAtIndex:index];
|
[_menuContainerStack removeObjectAtIndex:index];
|
||||||
|
|
||||||
[menu _menuDidClose];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newMenu)
|
if (!newMenu)
|
||||||
@@ -440,234 +364,4 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
[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])
|
|
||||||
{
|
|
||||||
var aSelector = CPSelectorFromString(obj);
|
|
||||||
|
|
||||||
if ([self respondsToSelector:aSelector])
|
|
||||||
[self performSelector:aSelector withObject:menu];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!(modifierFlags & (CPCommandKeyMask | CPControlKeyMask)))
|
|
||||||
{
|
|
||||||
if (!_keyBuffer)
|
|
||||||
{
|
|
||||||
_startTime = [CPDate date];
|
|
||||||
_keyBuffer = character;
|
|
||||||
|
|
||||||
[CPEvent stopPeriodicEvents];
|
|
||||||
[CPEvent startPeriodicEventsAfterDelay:0.1 withPeriod:0.1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_keyBuffer += character;
|
|
||||||
|
|
||||||
[self selectNextItemBeginningWith:_keyBuffer inMenu:menu clearBuffer:NO];
|
|
||||||
_lastGlobalLocation = Nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)selectNextItemBeginningWith:(CPString)characters inMenu:(CPMenu)menu clearBuffer:(BOOL)shouldClear
|
|
||||||
{
|
|
||||||
var iter = [[menu itemArray] objectEnumerator],
|
|
||||||
obj;
|
|
||||||
|
|
||||||
while (obj = [iter nextObject])
|
|
||||||
{
|
|
||||||
if ([obj isHidden] || ![obj isEnabled])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ([[[obj title] commonPrefixWithString:characters options:CPCaseInsensitiveSearch] length] == [characters length])
|
|
||||||
{
|
|
||||||
[menu _highlightItemAtIndex:iter._index];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldClear)
|
|
||||||
{
|
|
||||||
[CPEvent stopPeriodicEvents];
|
|
||||||
_keyBuffer = Nil;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_startTime = [CPDate date];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)moveUp:(CPMenu)menu
|
|
||||||
{
|
|
||||||
var index = menu._highlightedIndex - 1;
|
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
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
|
||||||
|
|||||||
@@ -208,14 +208,8 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
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];
|
||||||
@@ -300,12 +294,7 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
|
|
||||||
[_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 = LEFT_MARGIN;
|
|
||||||
clipFrame.origin.y = topMargin;
|
|
||||||
clipFrame.size.width = CGRectGetWidth(constrainedFrame) - LEFT_MARGIN - RIGHT_MARGIN;
|
|
||||||
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]))];
|
||||||
@@ -358,29 +347,24 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
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
|
||||||
@@ -400,15 +384,16 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
||||||
{
|
{
|
||||||
var frame = [self frame];
|
var frame = [self frame];
|
||||||
if (!CPRectContainsPoint(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) + TOP_MARGIN + 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) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT && ![_moreBelowView isHidden])
|
if (aGlobalLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT)
|
||||||
return _CPMenuManagerScrollingStateDown;
|
return _CPMenuManagerScrollingStateDown;
|
||||||
|
|
||||||
return _CPMenuManagerScrollingStateNone;
|
return _CPMenuManagerScrollingStateNone;
|
||||||
@@ -477,7 +462,7 @@ var STICKY_TIME_INTERVAL = 500,
|
|||||||
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))
|
||||||
|
|||||||
@@ -30,25 +30,6 @@
|
|||||||
@import "_CPMenuItemView.j"
|
@import "_CPMenuItemView.j"
|
||||||
|
|
||||||
|
|
||||||
var CPMenuItemStringRepresentationDictionary = [CPDictionary dictionary];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u238B" forKey:CPEscapeFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u21E5" forKey:CPTabCharacter];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u21E4" forKey:CPBackTabCharacter];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u2423" forKey:CPSpaceFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u23CE" forKey:CPCarriageReturnCharacter];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u232B" forKey:CPBackspaceCharacter];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u232B" forKey:CPDeleteFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u2326" forKey:CPDeleteCharacter];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u21F1" forKey:CPHomeFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u21F2" forKey:CPEndFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u21DE" forKey:CPPageUpFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u21DF" forKey:CPPageDownFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u2191" forKey:CPUpArrowFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u2193" forKey:CPDownArrowFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u2190" forKey:CPLeftArrowFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u2192" forKey:CPRightArrowFunctionKey];
|
|
||||||
[CPMenuItemStringRepresentationDictionary setObject:"\u2327" forKey:CPClearDisplayFunctionKey];
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPMenuItem
|
@class CPMenuItem
|
||||||
@@ -96,8 +77,6 @@ var CPMenuItemStringRepresentationDictionary = [CPDictionary dictionary];
|
|||||||
id _representedObject;
|
id _representedObject;
|
||||||
CPView _view;
|
CPView _view;
|
||||||
|
|
||||||
int _changeCount;
|
|
||||||
|
|
||||||
_CPMenuItemView _menuItemView;
|
_CPMenuItemView _menuItemView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,14 +98,12 @@ var CPMenuItemStringRepresentationDictionary = [CPDictionary dictionary];
|
|||||||
|
|
||||||
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;
|
||||||
@@ -149,12 +126,13 @@ var CPMenuItemStringRepresentationDictionary = [CPDictionary dictionary];
|
|||||||
*/
|
*/
|
||||||
- (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];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,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
|
||||||
@@ -481,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:)];
|
||||||
@@ -618,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
|
||||||
{
|
{
|
||||||
@@ -670,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
|
||||||
@@ -873,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",
|
||||||
@@ -921,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];
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ var _CPMenuItemSelectionColor = nil,
|
|||||||
BOOL _isDirty;
|
BOOL _isDirty;
|
||||||
BOOL _showsStateColumn;
|
BOOL _showsStateColumn;
|
||||||
|
|
||||||
|
CPImageView _stateView;
|
||||||
_CPImageAndTextView _imageAndTextView;
|
_CPImageAndTextView _imageAndTextView;
|
||||||
CPView _submenuView;
|
CPView _submenuView;
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-240
@@ -1,47 +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 "CPController.j"
|
@import "CPController.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;
|
||||||
@@ -83,20 +52,11 @@
|
|||||||
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])
|
||||||
@@ -111,19 +71,11 @@
|
|||||||
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"];
|
||||||
@@ -131,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)
|
||||||
@@ -331,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;
|
||||||
@@ -353,8 +218,7 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPObjectControllerContentKey = @"CPObjectControllerContentKey",
|
var CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
|
||||||
CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
|
|
||||||
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
|
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
|
||||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
|
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
|
||||||
|
|
||||||
@@ -369,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] || NO];
|
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey] || NO];
|
||||||
[self setContent:[aCoder decodeObjectForKey:CPObjectControllerContentKey]];
|
|
||||||
|
|
||||||
_observedKeys = [[CPCountedSet alloc] init];
|
_observedKeys = [[CPCountedSet alloc] init];
|
||||||
}
|
}
|
||||||
@@ -382,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
|
||||||
@@ -415,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;
|
||||||
@@ -464,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
|
||||||
@@ -476,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;
|
||||||
}
|
}
|
||||||
@@ -498,20 +352,30 @@ 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 (self = [super initWithObjects:objects count:count])
|
||||||
|
{
|
||||||
|
_observationProxies = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
|
||||||
{
|
{
|
||||||
if (aKeyPath.indexOf("@") === 0)
|
if (aKeyPath.indexOf("@") === 0)
|
||||||
{
|
{
|
||||||
@@ -523,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];
|
||||||
@@ -545,7 +409,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
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];
|
||||||
@@ -559,7 +423,7 @@ 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],
|
||||||
@@ -579,7 +443,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
|
|
||||||
- (void)removeObjectAtIndex:(unsigned)anIndex
|
- (void)removeObjectAtIndex:(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],
|
||||||
@@ -616,7 +480,7 @@ 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],
|
||||||
@@ -639,13 +503,11 @@ 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
|
||||||
@@ -655,79 +517,46 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
_cachedValues = [CPDictionary dictionary];
|
_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];
|
||||||
|
|
||||||
|
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];
|
|
||||||
value = [self _controllerMarkerForValues:values];
|
|
||||||
|
|
||||||
[_cachedValues setObject:value forKey:theKeyPath];
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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];
|
||||||
@@ -738,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;
|
||||||
@@ -776,11 +610,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
|||||||
[proxy setNotifyObject:YES];
|
[proxy setNotifyObject:YES];
|
||||||
[_observationProxies addObject:proxy];
|
[_observationProxies addObject:proxy];
|
||||||
|
|
||||||
// We keep are reference to the observed objects
|
[[_controller selectedObjects] addObserver:proxy forKeyPath:aKeyPath options:options context:context];
|
||||||
// because the removeObserver: will be called after the selection changes
|
|
||||||
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
|
||||||
@@ -788,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];
|
||||||
|
|
||||||
_observedObjects = nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -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
-708
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -51,7 +51,7 @@ CPCancelButton = 0;
|
|||||||
@global
|
@global
|
||||||
@class CPWindow
|
@class CPWindow
|
||||||
*/
|
*/
|
||||||
CPDocModalWindowMask = 1 << 6;
|
CPDocModalWindowMask = 1 << 6;
|
||||||
|
|
||||||
@implementation CPPanel : CPWindow
|
@implementation CPPanel : CPWindow
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -150,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];
|
||||||
|
|||||||
+194
-242
@@ -26,7 +26,7 @@
|
|||||||
@import "CPMenuItem.j"
|
@import "CPMenuItem.j"
|
||||||
|
|
||||||
|
|
||||||
var VISIBLE_MARGIN = 7.0;
|
var VISIBLE_MARGIN = 7.0;
|
||||||
|
|
||||||
CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||||
|
|
||||||
@@ -38,8 +38,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
@implementation CPPopUpButton : CPButton
|
@implementation CPPopUpButton : CPButton
|
||||||
{
|
{
|
||||||
CPUInteger _selectedIndex;
|
int _selectedIndex;
|
||||||
CPRectEdge _preferredEdge;
|
CPRectEdge _preferredEdge;
|
||||||
|
|
||||||
|
CPMenu _menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)defaultThemeClass
|
||||||
@@ -47,21 +49,6 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
return "popup-button";
|
return "popup-button";
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingSelectedIndex
|
|
||||||
{
|
|
||||||
return [CPSet setWithObject:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingSelectedTag
|
|
||||||
{
|
|
||||||
return [CPSet setWithObject:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (CPSet)keyPathsForValuesAffectingSelectedItem
|
|
||||||
{
|
|
||||||
return [CPSet setWithObject:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Initializes the pop-up button to the specified size.
|
Initializes the pop-up button to the specified size.
|
||||||
@param aFrame the size for the button
|
@param aFrame the size for the button
|
||||||
@@ -74,8 +61,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
[self selectItemAtIndex:CPNotFound];
|
_selectedIndex = CPNotFound;
|
||||||
|
|
||||||
_preferredEdge = CPMaxYEdge;
|
_preferredEdge = CPMaxYEdge;
|
||||||
|
|
||||||
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
||||||
@@ -85,11 +71,6 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
[self setMenu:[[CPMenu alloc] initWithTitle:@""]];
|
[self setMenu:[[CPMenu alloc] initWithTitle:@""]];
|
||||||
|
|
||||||
[self setPullsDown:shouldPullDown];
|
[self setPullsDown:shouldPullDown];
|
||||||
|
|
||||||
var options = CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld; // | CPKeyValueObservingOptionInitial;
|
|
||||||
[self addObserver:self forKeyPath:@"menu.items" options:options context:nil];
|
|
||||||
[self addObserver:self forKeyPath:@"_firstItem.changeCount" options:options context:nil];
|
|
||||||
[self addObserver:self forKeyPath:@"selectedItem.changeCount" options:options context:nil];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -104,9 +85,6 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Specifies whether the object is a pull-down or a pop-up menu.
|
Specifies whether the object is a pull-down or a pop-up menu.
|
||||||
If the button pulls down the menu items represent actions, not states.
|
|
||||||
So the text in the button will NOT change when the user selects something different.
|
|
||||||
|
|
||||||
@param shouldPullDown \c YES makes the pop-up button
|
@param shouldPullDown \c YES makes the pop-up button
|
||||||
a pull-down menu. \c NO makes it a pop-up menu.
|
a pull-down menu. \c NO makes it a pop-up menu.
|
||||||
*/
|
*/
|
||||||
@@ -120,7 +98,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
if (!changed)
|
if (!changed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var items = [[self menu] itemArray];
|
var items = [_menu itemArray];
|
||||||
|
|
||||||
if ([items count] <= 0)
|
if ([items count] <= 0)
|
||||||
return;
|
return;
|
||||||
@@ -145,21 +123,21 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (void)addItem:(CPMenuItem)anItem
|
- (void)addItem:(CPMenuItem)anItem
|
||||||
{
|
{
|
||||||
[[self menu] addItem:anItem];
|
[_menu addItem:anItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Adds a new menu item with the specified title.
|
Adds a new menu item with the specified title.
|
||||||
@param the new menu item's title
|
@param the new menu item's tite
|
||||||
*/
|
*/
|
||||||
- (void)addItemWithTitle:(CPString)aTitle
|
- (void)addItemWithTitle:(CPString)aTitle
|
||||||
{
|
{
|
||||||
[[self menu] addItemWithTitle:aTitle action:NULL keyEquivalent:nil];
|
[_menu addItemWithTitle:aTitle action:NULL keyEquivalent:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Adds multiple new menu items with the titles specified in the provided array.
|
Adds multiple new menu items with the titles specified in the provided array.
|
||||||
@param titles an array of names for the new items
|
@param titles an arry of names for the new items
|
||||||
*/
|
*/
|
||||||
- (void)addItemsWithTitles:(CPArray)titles
|
- (void)addItemsWithTitles:(CPArray)titles
|
||||||
{
|
{
|
||||||
@@ -172,7 +150,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Inserts a new item with the specified title and index location.
|
Inserts a new item with the specified title and index location.
|
||||||
@param aTitle the new item's title
|
@param aTitle the new itme's title
|
||||||
@param anIndex the item's index in the menu
|
@param anIndex the item's index in the menu
|
||||||
*/
|
*/
|
||||||
- (void)insertItemWithTitle:(CPString)aTitle atIndex:(int)anIndex
|
- (void)insertItemWithTitle:(CPString)aTitle atIndex:(int)anIndex
|
||||||
@@ -184,7 +162,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
if ([items[count] title] == aTitle)
|
if ([items[count] title] == aTitle)
|
||||||
[self removeItemAtIndex:count];
|
[self removeItemAtIndex:count];
|
||||||
|
|
||||||
[[self menu] insertItemWithTitle:aTitle action:NULL keyEquivalent:nil atIndex:anIndex];
|
[_menu insertItemWithTitle:aTitle action:NULL keyEquivalent:nil atIndex:anIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -192,8 +170,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (void)removeAllItems
|
- (void)removeAllItems
|
||||||
{
|
{
|
||||||
[[self menu] removeAllItems];
|
var count = [_menu numberOfItems];
|
||||||
[self synchronizeTitleAndSelectedItem];
|
|
||||||
|
while (count--)
|
||||||
|
[_menu removeItemAtIndex:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -212,7 +192,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (void)removeItemAtIndex:(int)anIndex
|
- (void)removeItemAtIndex:(int)anIndex
|
||||||
{
|
{
|
||||||
[[self menu] removeItemAtIndex:anIndex];
|
[_menu removeItemAtIndex:anIndex];
|
||||||
[self synchronizeTitleAndSelectedItem];
|
[self synchronizeTitleAndSelectedItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,12 +202,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (CPMenuItem)selectedItem
|
- (CPMenuItem)selectedItem
|
||||||
{
|
{
|
||||||
var indexOfSelectedItem = [self indexOfSelectedItem];
|
if (_selectedIndex < 0 || _selectedIndex > [self numberOfItems] - 1)
|
||||||
|
|
||||||
if (indexOfSelectedItem < 0 || indexOfSelectedItem > [self numberOfItems] - 1)
|
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
return [[self menu] itemAtIndex:indexOfSelectedItem];
|
return [_menu itemAtIndex:_selectedIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -246,20 +224,13 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
return _selectedIndex;
|
return _selectedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For us, CPNumber is toll-free bridged to Number, so just return the selected index.
|
||||||
/*!
|
/*!
|
||||||
@ignore
|
Returns the selected item's index. If no item is selected, it returns CPNotFound.
|
||||||
*/
|
*/
|
||||||
- (int)selectedTag
|
- (id)objectValue
|
||||||
{
|
{
|
||||||
return [[self selectedItem] tag];
|
return _selectedIndex;
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_setSelectedTag:(int)aTag
|
|
||||||
{
|
|
||||||
[self selectItemWithTag:aTag];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting the Current Selection
|
// Setting the Current Selection
|
||||||
@@ -276,50 +247,22 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
Selects the item at the specified index
|
Selects the item at the specified index
|
||||||
@param anIndex the index of the item to select
|
@param anIndex the index of the item to select
|
||||||
*/
|
*/
|
||||||
- (void)selectItemAtIndex:(CPUInteger)anIndex
|
- (void)selectItemAtIndex:(int)anIndex
|
||||||
{
|
{
|
||||||
[self setObjectValue:anIndex];
|
if (_selectedIndex == anIndex)
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setSelectedIndex:(CPUInteger)anIndex
|
|
||||||
{
|
|
||||||
[self setObjectValue:anIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPUInteger)selectedIndex
|
|
||||||
{
|
|
||||||
return [self objectValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Selects the item at the specified index
|
|
||||||
@param anIndex the index of the item to select
|
|
||||||
*/
|
|
||||||
- (void)setObjectValue:(int)anIndex
|
|
||||||
{
|
|
||||||
var indexOfSelectedItem = [self objectValue];
|
|
||||||
|
|
||||||
anIndex = parseInt(+anIndex, 10);
|
|
||||||
|
|
||||||
if (indexOfSelectedItem === anIndex)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (indexOfSelectedItem >= 0 && ![self pullsDown])
|
if (_selectedIndex >= 0 && ![self pullsDown])
|
||||||
[[self selectedItem] setState:CPOffState];
|
[[self selectedItem] setState:CPOffState];
|
||||||
|
|
||||||
_selectedIndex = anIndex;
|
_selectedIndex = anIndex;
|
||||||
|
|
||||||
if (indexOfSelectedItem >= 0 && ![self pullsDown])
|
if (_selectedIndex >= 0 && ![self pullsDown])
|
||||||
[[self selectedItem] setState:CPOnState];
|
[[self selectedItem] setState:CPOnState];
|
||||||
|
|
||||||
[self synchronizeTitleAndSelectedItem];
|
[self synchronizeTitleAndSelectedItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)objectValue
|
|
||||||
{
|
|
||||||
return _selectedIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Selects the menu item with the specified tag
|
Selects the menu item with the specified tag
|
||||||
@param the tag of the item to select
|
@param the tag of the item to select
|
||||||
@@ -338,14 +281,84 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
[self selectItemAtIndex:[self indexOfItemWithTitle:aTitle]];
|
[self selectItemAtIndex:[self indexOfItemWithTitle:aTitle]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the object for the selected item. If no item is selected, then this method has no effect.
|
||||||
|
@param the object set for the selected item
|
||||||
|
*/
|
||||||
|
- (void)setObjectValue:(id)aValue
|
||||||
|
{
|
||||||
|
[self selectItemAtIndex:[aValue intValue]];
|
||||||
|
}
|
||||||
|
|
||||||
// Getting Menu Items
|
// Getting Menu Items
|
||||||
|
/*!
|
||||||
|
Returns the button's menu of items.
|
||||||
|
*/
|
||||||
|
- (CPMenu)menu
|
||||||
|
{
|
||||||
|
return _menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the menu for the button
|
||||||
|
*/
|
||||||
|
- (void)setMenu:(CPMenu)aMenu
|
||||||
|
{
|
||||||
|
if (_menu === aMenu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||||
|
|
||||||
|
if (_menu)
|
||||||
|
{
|
||||||
|
[defaultCenter
|
||||||
|
removeObserver:self
|
||||||
|
name:CPMenuDidAddItemNotification
|
||||||
|
object:_menu];
|
||||||
|
|
||||||
|
[defaultCenter
|
||||||
|
removeObserver:self
|
||||||
|
name:CPMenuDidChangeItemNotification
|
||||||
|
object:_menu];
|
||||||
|
|
||||||
|
[defaultCenter
|
||||||
|
removeObserver:self
|
||||||
|
name:CPMenuDidRemoveItemNotification
|
||||||
|
object:_menu];
|
||||||
|
}
|
||||||
|
|
||||||
|
_menu = aMenu;
|
||||||
|
|
||||||
|
if (_menu)
|
||||||
|
{
|
||||||
|
[defaultCenter
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(menuDidAddItem:)
|
||||||
|
name:CPMenuDidAddItemNotification
|
||||||
|
object:_menu];
|
||||||
|
|
||||||
|
[defaultCenter
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(menuDidChangeItem:)
|
||||||
|
name:CPMenuDidChangeItemNotification
|
||||||
|
object:_menu];
|
||||||
|
|
||||||
|
[defaultCenter
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(menuDidRemoveItem:)
|
||||||
|
name:CPMenuDidRemoveItemNotification
|
||||||
|
object:_menu];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self synchronizeTitleAndSelectedItem];
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a count of the number of items in the button's menu.
|
Returns a count of the number of items in the button's menu.
|
||||||
*/
|
*/
|
||||||
- (int)numberOfItems
|
- (int)numberOfItems
|
||||||
{
|
{
|
||||||
return [[self menu] numberOfItems];
|
return [_menu numberOfItems];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -353,7 +366,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (CPArray)itemArray
|
- (CPArray)itemArray
|
||||||
{
|
{
|
||||||
return [[self menu] itemArray];
|
return [_menu itemArray];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -362,7 +375,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (CPMenuItem)itemAtIndex:(unsigned)anIndex
|
- (CPMenuItem)itemAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
return [[self menu] itemAtIndex:anIndex];
|
return [_menu itemAtIndex:anIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -371,7 +384,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (CPString)itemTitleAtIndex:(unsigned)anIndex
|
- (CPString)itemTitleAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
return [[[self menu] itemAtIndex:anIndex] title];
|
return [[_menu itemAtIndex:anIndex] title];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -396,13 +409,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (CPMenuItem)itemWithTitle:(CPString)aTitle
|
- (CPMenuItem)itemWithTitle:(CPString)aTitle
|
||||||
{
|
{
|
||||||
var menu = [self menu],
|
return [_menu itemAtIndex:[_menu indexOfItemWithTitle:aTitle]];
|
||||||
itemIndex = [menu indexOfItemWithTitle:aTitle];
|
|
||||||
|
|
||||||
if (itemIndex === CPNotFound)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return [menu itemAtIndex:itemIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -410,7 +417,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (CPMenuItem)lastItem
|
- (CPMenuItem)lastItem
|
||||||
{
|
{
|
||||||
return [[[self menu] itemArray] lastObject];
|
return [[_menu itemArray] lastObject];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getting the Indices of Menu Items
|
// Getting the Indices of Menu Items
|
||||||
@@ -420,7 +427,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (int)indexOfItem:(CPMenuItem)aMenuItem
|
- (int)indexOfItem:(CPMenuItem)aMenuItem
|
||||||
{
|
{
|
||||||
return [[self menu] indexOfItem:aMenuItem];
|
return [_menu indexOfItem:aMenuItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -429,16 +436,16 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (int)indexOfItemWithTag:(int)aTag
|
- (int)indexOfItemWithTag:(int)aTag
|
||||||
{
|
{
|
||||||
return [[self menu] indexOfItemWithTag:aTag];
|
return [_menu indexOfItemWithTag:aTag];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the index of the item with the specified title or CPNotFound.
|
Returns the index of the item with the specified title or CPNotFound.
|
||||||
@param aTitle the item's title
|
@param aTitle the item's titel
|
||||||
*/
|
*/
|
||||||
- (int)indexOfItemWithTitle:(CPString)aTitle
|
- (int)indexOfItemWithTitle:(CPString)aTitle
|
||||||
{
|
{
|
||||||
return [[self menu] indexOfItemWithTitle:aTitle];
|
return [_menu indexOfItemWithTitle:aTitle];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -449,7 +456,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (int)indexOfItemWithRepresentedObject:(id)anObject
|
- (int)indexOfItemWithRepresentedObject:(id)anObject
|
||||||
{
|
{
|
||||||
return [[self menu] indexOfItemWithRepresentedObject:anObject];
|
return [_menu indexOfItemWithRepresentedObject:anObject];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -461,7 +468,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
*/
|
*/
|
||||||
- (int)indexOfItemWithTarget:(id)aTarget action:(SEL)anAction
|
- (int)indexOfItemWithTarget:(id)aTarget action:(SEL)anAction
|
||||||
{
|
{
|
||||||
return [[self menu] indexOfItemWithTarget:aTarget action:anAction];
|
return [_menu indexOfItemWithTarget:aTarget action:anAction];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting the Cell Edge to Pop out in Restricted Situations
|
// Setting the Cell Edge to Pop out in Restricted Situations
|
||||||
@@ -476,7 +483,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the preferred edge of the button to display the
|
Sets the preffered edge of the button to display the
|
||||||
pop-up when there is a limited amount of screen space.
|
pop-up when there is a limited amount of screen space.
|
||||||
By default, the pop-up should draw on top of the button.
|
By default, the pop-up should draw on top of the button.
|
||||||
*/
|
*/
|
||||||
@@ -497,7 +504,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
|
|
||||||
if ([self pullsDown])
|
if ([self pullsDown])
|
||||||
{
|
{
|
||||||
var items = [[self menu] itemArray];
|
var items = [_menu itemArray];
|
||||||
|
|
||||||
if ([items count] <= 0)
|
if ([items count] <= 0)
|
||||||
[self addItemWithTitle:aTitle];
|
[self addItemWithTitle:aTitle];
|
||||||
@@ -545,7 +552,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
|
|
||||||
if ([self pullsDown])
|
if ([self pullsDown])
|
||||||
{
|
{
|
||||||
var items = [[self menu] itemArray];
|
var items = [_menu itemArray];
|
||||||
|
|
||||||
if ([items count] > 0)
|
if ([items count] > 0)
|
||||||
item = items[0];
|
item = items[0];
|
||||||
@@ -557,117 +564,73 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
[super setTitle:[item title]];
|
[super setTitle:[item title]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)aContext
|
//
|
||||||
|
/*!
|
||||||
|
Called when the menu has a new item added to it.
|
||||||
|
@param aNotification information about the event
|
||||||
|
*/
|
||||||
|
- (void)menuDidAddItem:(CPNotification)aNotification
|
||||||
{
|
{
|
||||||
var pullsDown = [self pullsDown];
|
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"];
|
||||||
|
|
||||||
if (!pullsDown && aKeyPath === @"selectedItem.changeCount" ||
|
if (_selectedIndex < 0)
|
||||||
pullsDown && (aKeyPath === @"_firstItem" || aKeyPath === @"_firstItem.changeCount"))
|
[self selectItemAtIndex:0];
|
||||||
|
|
||||||
|
else if (index == _selectedIndex)
|
||||||
[self synchronizeTitleAndSelectedItem];
|
[self synchronizeTitleAndSelectedItem];
|
||||||
|
|
||||||
// FIXME: This is due to a bug in KVO, we should never get it for "menu".
|
else if (index < _selectedIndex)
|
||||||
if (aKeyPath === @"menu")
|
++_selectedIndex;
|
||||||
{
|
|
||||||
aKeyPath = @"menu.items";
|
|
||||||
|
|
||||||
[changes setObject:CPKeyValueChangeSetting forKey:CPKeyValueChangeKindKey];
|
if (index == 0 && [self pullsDown])
|
||||||
[changes setObject:[[self menu] itemArray] forKey:CPKeyValueChangeNewKey];
|
{
|
||||||
|
var items = [_menu itemArray];
|
||||||
|
|
||||||
|
[items[0] setHidden:YES];
|
||||||
|
|
||||||
|
if (items.length > 0)
|
||||||
|
[items[1] setHidden:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aKeyPath === @"menu.items")
|
var item = [_menu itemArray][index],
|
||||||
|
action = [item action];
|
||||||
|
|
||||||
|
if (!action || (action === @selector(_popUpItemAction:)))
|
||||||
{
|
{
|
||||||
var changeKind = [changes objectForKey:CPKeyValueChangeKindKey],
|
[item setTarget:self];
|
||||||
indexOfSelectedItem = [self indexOfSelectedItem];
|
[item setAction:@selector(_popUpItemAction:)];
|
||||||
|
|
||||||
if (changeKind === CPKeyValueChangeRemoval)
|
|
||||||
{
|
|
||||||
var index = CPNotFound,
|
|
||||||
indexes = [changes objectForKey:CPKeyValueChangeIndexesKey];
|
|
||||||
|
|
||||||
if ([indexes containsIndex:0] && [self pullsDown])
|
|
||||||
[self _firstItemDidChange];
|
|
||||||
|
|
||||||
// See whether the index has changed, despite the actual item not changing.
|
|
||||||
while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound &&
|
|
||||||
index <= indexOfSelectedItem)
|
|
||||||
--indexOfSelectedItem;
|
|
||||||
|
|
||||||
[self selectItemAtIndex:indexOfSelectedItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (changeKind === CPKeyValueChangeReplacement)
|
|
||||||
{
|
|
||||||
var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey];
|
|
||||||
|
|
||||||
if (pullsDown && [indexes containsIndex:0] ||
|
|
||||||
!pullsDown && [indexes containsIndex:indexOfSelectedItem])
|
|
||||||
[self synchronizeTitleAndSelectedItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No matter what, we want to prepare the new items.
|
|
||||||
var newItems = [changes objectForKey:CPKeyValueChangeNewKey];
|
|
||||||
|
|
||||||
[newItems enumerateObjectsUsingBlock:function(aMenuItem)
|
|
||||||
{
|
|
||||||
var action = [aMenuItem action];
|
|
||||||
|
|
||||||
if (!action)
|
|
||||||
[aMenuItem setAction:action = @selector(_popUpItemAction:)];
|
|
||||||
|
|
||||||
if (action === @selector(_popUpItemAction:))
|
|
||||||
[aMenuItem setTarget:self];
|
|
||||||
}];
|
|
||||||
|
|
||||||
if (changeKind === CPKeyValueChangeSetting)
|
|
||||||
{
|
|
||||||
[self _firstItemDidChange];
|
|
||||||
|
|
||||||
[self selectItemAtIndex:CPNotFound];
|
|
||||||
[self selectItemAtIndex:MIN([newItems count] - 1, indexOfSelectedItem)];
|
|
||||||
}
|
|
||||||
|
|
||||||
else //if (changeKind === CPKeyValueChangeInsertion)
|
|
||||||
{
|
|
||||||
var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey];
|
|
||||||
|
|
||||||
if ([self pullsDown] && [indexes containsIndex:0])
|
|
||||||
{
|
|
||||||
[self _firstItemDidChange];
|
|
||||||
|
|
||||||
if ([self numberOfItems] > 1)
|
|
||||||
{
|
|
||||||
var index = CPNotFound,
|
|
||||||
originalIndex = 0;
|
|
||||||
|
|
||||||
while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound &&
|
|
||||||
index <= originalIndex)
|
|
||||||
++originalIndex;
|
|
||||||
|
|
||||||
[[self itemAtIndex:originalIndex] setHidden:NO];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexOfSelectedItem < 0)
|
|
||||||
[self selectItemAtIndex:0];
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var index = CPNotFound;
|
|
||||||
|
|
||||||
// See whether the index has changed, despite the actual item not changing.
|
|
||||||
while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound &&
|
|
||||||
index <= indexOfSelectedItem)
|
|
||||||
++indexOfSelectedItem;
|
|
||||||
|
|
||||||
[self selectItemAtIndex:indexOfSelectedItem];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// [super observeValueForKeyPath:aKeyPath ofObject:anObject change:changes context:aContext];
|
/*!
|
||||||
|
Called when a menu item has changed.
|
||||||
|
@param aNotification information about the event
|
||||||
|
*/
|
||||||
|
- (void)menuDidChangeItem:(CPNotification)aNotification
|
||||||
|
{
|
||||||
|
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"];
|
||||||
|
|
||||||
|
if ([self pullsDown] && index != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (![self pullsDown] && index != _selectedIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
[self synchronizeTitleAndSelectedItem];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Called when an item was removed from the menu.
|
||||||
|
@param aNotification information about the event
|
||||||
|
*/
|
||||||
|
- (void)menuDidRemoveItem:(CPNotification)aNotification
|
||||||
|
{
|
||||||
|
var numberOfItems = [self numberOfItems];
|
||||||
|
|
||||||
|
if (numberOfItems <= _selectedIndex && numberOfItems > 0)
|
||||||
|
[self selectItemAtIndex:numberOfItems - 1];
|
||||||
|
else
|
||||||
|
[self synchronizeTitleAndSelectedItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
- (void)mouseDown:(CPEvent)anEvent
|
||||||
@@ -739,7 +702,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
|
|
||||||
- (void)rightMouseDown:(CPEvent)anEvent
|
- (void)rightMouseDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
// Disable standard CPView behavior which incorrectly displays the menu as a 'context menu'.
|
// Disable standard CPView behaviour which incorrectly displays the menu as a 'context menu'.
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_popUpItemAction:(id)aSender
|
- (void)_popUpItemAction:(id)aSender
|
||||||
@@ -747,22 +710,6 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
[self sendAction:[self action] to:[self target]];
|
[self sendAction:[self action] to:[self target]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_firstItemDidChange
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"_firstItem"];
|
|
||||||
[self didChangeValueForKey:@"_firstItem"];
|
|
||||||
|
|
||||||
[[self _firstItem] setHidden:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPMenuItem)_firstItem
|
|
||||||
{
|
|
||||||
if ([self numberOfItems] <= 0)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return [[self menu] itemAtIndex:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
||||||
{
|
{
|
||||||
var count = objects.length,
|
var count = objects.length,
|
||||||
@@ -772,14 +719,19 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
|||||||
[self setEnabled:YES];
|
[self setEnabled:YES];
|
||||||
|
|
||||||
while (count-- > 1)
|
while (count-- > 1)
|
||||||
|
{
|
||||||
if (value !== [objects[count] valueForKeyPath:aKeyPath])
|
if (value !== [objects[count] valueForKeyPath:aKeyPath])
|
||||||
|
{
|
||||||
[[self selectedItem] setState:CPOffState];
|
[[self selectedItem] setState:CPOffState];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var DEPRECATED_CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
||||||
DEPRECATED_CPPopUpButtonSelectedIndexKey = @"CPPopUpButtonSelectedIndexKey";
|
CPPopUpButtonSelectedIndexKey = @"CPPopUpButtonSelectedIndexKey",
|
||||||
|
CPPopUpButtonPullsDownKey = @"CPPopUpButtonPullsDownKey";
|
||||||
|
|
||||||
@implementation CPPopUpButton (CPCoding)
|
@implementation CPPopUpButton (CPCoding)
|
||||||
/*!
|
/*!
|
||||||
@@ -795,27 +747,27 @@ var DEPRECATED_CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
// FIXME: (or not?) _title is nulled in - [CPButton initWithCoder:],
|
// Nothing is currently selected
|
||||||
// so we need to do this again.
|
_selectedIndex = -1;
|
||||||
[self synchronizeTitleAndSelectedItem];
|
|
||||||
|
|
||||||
// FIXME: Remove deprecation leniency for 1.0
|
[self setMenu:[aCoder decodeObjectForKey:CPPopUpButtonMenuKey]];
|
||||||
if ([aCoder containsValueForKey:DEPRECATED_CPPopUpButtonMenuKey])
|
[self selectItemAtIndex:[aCoder decodeObjectForKey:CPPopUpButtonSelectedIndexKey]];
|
||||||
{
|
|
||||||
CPLog.warn(self + " was encoded with an older version of Cappuccino. Please nib2cib the original nib again or open and re-save in Atlas.");
|
|
||||||
|
|
||||||
[self setMenu:[aCoder decodeObjectForKey:DEPRECATED_CPPopUpButtonMenuKey]];
|
|
||||||
[self setObjectValue:[aCoder decodeObjectForKey:DEPRECATED_CPPopUpButtonSelectedIndexKey]];
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld;/* | CPKeyValueObservingOptionInitial */
|
|
||||||
|
|
||||||
[self addObserver:self forKeyPath:@"menu.items" options:options context:nil];
|
|
||||||
[self addObserver:self forKeyPath:@"_firstItem.changeCount" options:options context:nil];
|
|
||||||
[self addObserver:self forKeyPath:@"selectedItem.changeCount" options:options context:nil];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Encodes the data of the pop-up button into a coder
|
||||||
|
@param aCoder the coder to which the data
|
||||||
|
will be written
|
||||||
|
*/
|
||||||
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
|
{
|
||||||
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
|
[aCoder encodeObject:_menu forKey:CPPopUpButtonMenuKey];
|
||||||
|
[aCoder encodeInt:_selectedIndex forKey:CPPopUpButtonSelectedIndexKey];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,325 +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 "_CPAttachedWindow.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 attached
|
|
||||||
view relative to another one.
|
|
||||||
|
|
||||||
Delegate can implement:
|
|
||||||
– popoverShouldClose:(CPPopover)aPopOver
|
|
||||||
– popoverWillShow:(CPPopover)aPopOver
|
|
||||||
– popoverDidShow:(CPPopover)aPopOver
|
|
||||||
– popoverWillClose:(CPPopover)aPopOver
|
|
||||||
– popoverDidClose:(CPPopover)aPopOver
|
|
||||||
*/
|
|
||||||
@implementation CPPopover : CPResponder
|
|
||||||
{
|
|
||||||
@outlet CPViewController _contentViewController @accessors(property=contentViewController);
|
|
||||||
@outlet id _delegate @accessors(getter=delegate);
|
|
||||||
|
|
||||||
BOOL _animates @accessors(property=animates);
|
|
||||||
BOOL _shown @accessors(getter=shown);
|
|
||||||
int _appearance @accessors(property=appearance);
|
|
||||||
int _behavior @accessors(getter=behavior);
|
|
||||||
|
|
||||||
BOOL _needsCompute;
|
|
||||||
_CPAttachedWindow _attachedWindow;
|
|
||||||
int _implementedDelegateMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Initialization
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initialize the CPPopover witn default values
|
|
||||||
|
|
||||||
@returns anInitialized CPPopover
|
|
||||||
*/
|
|
||||||
- (CPPopover)init
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_animates = YES;
|
|
||||||
_appearance = CPPopoverAppearanceMinimal;
|
|
||||||
_behavior = CPPopoverBehaviorApplicationDefined;
|
|
||||||
_needsCompute = YES;
|
|
||||||
_shown = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Getters / Setters
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the current rect of the popover
|
|
||||||
|
|
||||||
@return CPRect represeting the frame of the popover
|
|
||||||
*/
|
|
||||||
- (CPRect)positioningRect
|
|
||||||
{
|
|
||||||
if (!_attachedWindow || ![_attachedWindow isVisible])
|
|
||||||
return nil;
|
|
||||||
return [_attachedWindow frame];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Sets the frame of the popover
|
|
||||||
@param aRect the desired frame
|
|
||||||
*/
|
|
||||||
- (void)setPositioningRect:(CPRect)aRect
|
|
||||||
{
|
|
||||||
if (!_attachedWindow || ![_attachedWindow isVisible])
|
|
||||||
return;
|
|
||||||
[_attachedWindow setFrame:aRect];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the size of the popover's view
|
|
||||||
|
|
||||||
@return CPSize represeting the size of the popover's view
|
|
||||||
*/
|
|
||||||
- (CPRect)contentSize
|
|
||||||
{
|
|
||||||
if (!_attachedWindow || ![_attachedWindow isVisible])
|
|
||||||
return nil;
|
|
||||||
return [[_contentViewController view] frameSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the size of of the popover's view
|
|
||||||
|
|
||||||
@param aSize the desired size
|
|
||||||
*/
|
|
||||||
- (void)setContentSize:(CPSize)aSize
|
|
||||||
{
|
|
||||||
[[_contentViewController view] setFrameSize:aSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Indicates if CPPopover is visible
|
|
||||||
|
|
||||||
@returns YES if visible
|
|
||||||
*/
|
|
||||||
- (BOOL)shown
|
|
||||||
{
|
|
||||||
if (!_attachedWindow)
|
|
||||||
return NO;
|
|
||||||
return [_attachedWindow isVisible];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the behaviour of the CPPopover. It can be
|
|
||||||
- CPPopoverBehaviorTransient: the popover will be close if another control outside the popover become the responder
|
|
||||||
- CPPopoverBehaviorApplicationDefined: (DEFAULT) the application is responsible for closing the popover
|
|
||||||
|
|
||||||
@param aBehaviour the desired behaviour
|
|
||||||
*/
|
|
||||||
- (void)setBehaviour:(int)aBehaviour
|
|
||||||
{
|
|
||||||
if (_behavior == aBehaviour)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_behavior = aBehaviour;
|
|
||||||
_needsCompute = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (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: CPRectEdge representing the preferred positioning.
|
|
||||||
*/
|
|
||||||
- (void)showRelativeToRect:(CPRect)positioningRect ofView:(CPView)positioningView preferredEdge:(CPRectEdge)preferredEdge
|
|
||||||
{
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_)
|
|
||||||
[_delegate popoverWillShow:self];
|
|
||||||
|
|
||||||
if (!_contentViewController)
|
|
||||||
[CPException raise:CPInternalInconsistencyException reason:@"contentViewController must not be nil"];
|
|
||||||
|
|
||||||
if (_needsCompute || !_attachedWindow)
|
|
||||||
{
|
|
||||||
var styleMask = (_behavior == CPPopoverBehaviorTransient) ? CPClosableOnBlurWindowMask : nil;
|
|
||||||
_attachedWindow = [[_CPAttachedWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:styleMask];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_attachedWindow setAppearance:_appearance];
|
|
||||||
[_attachedWindow setAnimates:_animates];
|
|
||||||
[_attachedWindow setDelegate:self];
|
|
||||||
[_attachedWindow setMovableByWindowBackground:NO];
|
|
||||||
[_attachedWindow setFrame:[_attachedWindow frameRectForContentRect:[[_contentViewController view] frame]]];
|
|
||||||
[_attachedWindow setContentView:[_contentViewController view]];
|
|
||||||
|
|
||||||
if (positioningRect)
|
|
||||||
[_attachedWindow positionRelativeToRect:positioningRect preferredEdge:preferredEdge];
|
|
||||||
else if (positioningView)
|
|
||||||
[_attachedWindow positionRelativeToView:positioningView preferredEdge:preferredEdge];
|
|
||||||
else
|
|
||||||
[CPException raise:CPInvalidArgumentException reason:@"a value must be passed for positioningRect or positioningView"];
|
|
||||||
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
|
|
||||||
[_delegate popoverDidShow:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Closes the popover
|
|
||||||
*/
|
|
||||||
- (void)close
|
|
||||||
{
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_shouldClose_)
|
|
||||||
if (![_delegate popoverShouldClose:self])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_)
|
|
||||||
[_delegate popoverWillClose:self];
|
|
||||||
|
|
||||||
[_attachedWindow close];
|
|
||||||
|
|
||||||
if (_implementedDelegateMethods & CPPopoverDelegate_popover_didClose_)
|
|
||||||
[_delegate popoverDidClose:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Action
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Close the popover
|
|
||||||
|
|
||||||
@param aSender the sender of the action
|
|
||||||
*/
|
|
||||||
- (IBAction)performClose:(id)aSender
|
|
||||||
{
|
|
||||||
[self close];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Delegates
|
|
||||||
|
|
||||||
/*! @ignore */
|
|
||||||
- (BOOL)attachedWindowShouldClose:(_CPAttachedWindow)anAttachedWindow
|
|
||||||
{
|
|
||||||
[self close];
|
|
||||||
|
|
||||||
// we return NO, because we want the CPPopover to compute
|
|
||||||
// if the attached can be close in order to send delegate messages
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
var CPPopoverNeedsComputeKey = @"CPPopoverNeedsComputeKey",
|
|
||||||
CPPopoverAppearanceKey = @"CPPopoverAppearanceKey",
|
|
||||||
CPPopoverAnimatesKey = @"CPPopoverAnimatesKey",
|
|
||||||
CPPopoverContentViewControllerKey = @"CPPopoverContentViewControllerKey",
|
|
||||||
CPPopoverDelegateKey = @"CPPopoverDelegateKey",
|
|
||||||
CPPopoverBehaviorKey = @"CPPopoverBehaviorKey";
|
|
||||||
|
|
||||||
@implementation CPPopover (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_needsCompute = [aCoder decodeIntForKey:CPPopoverNeedsComputeKey];
|
|
||||||
_appearance = [aCoder decodeIntForKey:CPPopoverAppearanceKey];
|
|
||||||
_animates = [aCoder decodeBoolForKey:CPPopoverAnimatesKey];
|
|
||||||
_contentViewController = [aCoder decodeObjectForKey:CPPopoverContentViewControllerKey];
|
|
||||||
[self setDelegate:[aCoder decodeObjectForKey:CPPopoverDelegateKey]];
|
|
||||||
[self setBehaviour:[aCoder decodeIntForKey:CPPopoverBehaviorKey]];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeBool:_needsCompute forKey:CPPopoverNeedsComputeKey];
|
|
||||||
[aCoder encodeInt:_appearance forKey:CPPopoverAppearanceKey];
|
|
||||||
[aCoder encodeObject:_animates forKey:CPPopoverAnimatesKey];
|
|
||||||
[aCoder encodeObject:_contentViewController forKey:CPPopoverContentViewControllerKey];
|
|
||||||
[aCoder encodeObject:_delegate forKey:CPPopoverDelegateKey];
|
|
||||||
[aCoder encodeInt:_behavior forKey:CPPopoverBehaviorKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -120,7 +120,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
|
|
||||||
CPProgressIndicatorStyleSizes = [];
|
CPProgressIndicatorStyleSizes = [];
|
||||||
|
|
||||||
// Bar Style
|
// Bar Sttyle
|
||||||
var prefixes = [
|
var prefixes = [
|
||||||
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
|
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
|
||||||
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
|
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
|
||||||
@@ -128,7 +128,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle]
|
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle]
|
||||||
];
|
];
|
||||||
|
|
||||||
for (var i = 0, count = prefixes.length; i < count; i++)
|
for (var i = 0, count = prefixes.length; i<count; i++)
|
||||||
{
|
{
|
||||||
var prefix = prefixes[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 + regularIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
|
||||||
@@ -290,7 +290,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Not yet implemented.
|
Not yet impemented.
|
||||||
*/
|
*/
|
||||||
- (CPControlTint)controlTint
|
- (CPControlTint)controlTint
|
||||||
{
|
{
|
||||||
@@ -357,7 +357,8 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
[self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]];
|
[self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]];
|
||||||
else
|
else
|
||||||
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), CPProgressIndicatorStyleSizes[
|
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), CPProgressIndicatorStyleSizes[
|
||||||
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] + _CPControlIdentifierForControlSize(_controlSize)][0].height)];
|
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] +
|
||||||
|
_CPControlIdentifierForControlSize(_controlSize)][0].height)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -464,41 +465,3 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@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"];
|
|
||||||
_isIndeterminate = [aCoder decodeObjectForKey:@"_isIndeterminate"];
|
|
||||||
_style = [aCoder decodeObjectForKey:@"_style"];
|
|
||||||
_isAnimating = [aCoder decodeObjectForKey:@"_isAnimating"];
|
|
||||||
_isDisplayedWhenStoppedSet = [aCoder decodeObjectForKey:@"_isDisplayedWhenStoppedSet"];
|
|
||||||
_isDisplayedWhenStopped = [aCoder decodeObjectForKey:@"_isDisplayedWhenStopped"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_minValue forKey:@"_minValue"];
|
|
||||||
[aCoder encodeObject:_maxValue forKey:@"_maxValue"];
|
|
||||||
[aCoder encodeObject:_doubleValue forKey:@"_doubleValue"];
|
|
||||||
[aCoder encodeObject:_controlSize forKey:@"_controlSize"];
|
|
||||||
[aCoder encodeObject:_isIndeterminate forKey:@"_isIndeterminate"];
|
|
||||||
[aCoder encodeObject:_style forKey:@"_style"];
|
|
||||||
[aCoder encodeObject:_isAnimating forKey:@"_isAnimating"];
|
|
||||||
[aCoder encodeObject:_isDisplayedWhenStoppedSet forKey:@"_isDisplayedWhenStoppedSet"];
|
|
||||||
[aCoder encodeObject:_isDisplayedWhenStopped forKey:@"_isDisplayedWhenStopped"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|||||||
+2
-8
@@ -150,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";
|
||||||
@@ -228,6 +220,8 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
|||||||
|
|
||||||
[_selectedRadio setState:CPOffState];
|
[_selectedRadio setState:CPOffState];
|
||||||
_selectedRadio = aRadio;
|
_selectedRadio = aRadio;
|
||||||
|
|
||||||
|
[CPApp sendAction:_action to:_target from:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPRadio)selectedRadio
|
- (CPRadio)selectedRadio
|
||||||
|
|||||||
+7
-14
@@ -231,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
|
||||||
{
|
{
|
||||||
@@ -365,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,504 +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 */
|
|
||||||
- (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_mergeTree:(id)tree
|
|
||||||
{
|
|
||||||
var merged = [CPMutableArray array],
|
|
||||||
titles = [CPMutableArray array],
|
|
||||||
count = [tree count];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var t = tree[i],
|
|
||||||
title = [CPString stringWithString:[t title]];
|
|
||||||
|
|
||||||
if ([titles containsObject:title])
|
|
||||||
{
|
|
||||||
CPLogConsole("CPPredicateEditor does not support templates merging yet. Ignoring duplicate template: " + [t description]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[merged addObject:t];
|
|
||||||
[titles addObject:title];
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_constructTreeForTemplate:(CPPredicateEditorRowTemplate)aTemplate
|
|
||||||
{
|
|
||||||
var tree = [CPArray array],
|
|
||||||
templateViews = [aTemplate templateViews],
|
|
||||||
count = [templateViews count];
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
var children = [CPArray array],
|
|
||||||
itemsCount = 0,
|
|
||||||
menuIndex = -1,
|
|
||||||
itemsArray,
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
if (![[objectValue predicateFormat] isEqualToString:[[super predicate] predicateFormat]]) // ??
|
|
||||||
[self _reflectPredicate:objectValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_reflectPredicate:(id)predicate
|
|
||||||
{
|
|
||||||
var animation = _currentAnimation;
|
|
||||||
_currentAnimation = nil;
|
|
||||||
|
|
||||||
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]],
|
|
||||||
menuItemIndex = [treeChild menuItemIndex];
|
|
||||||
|
|
||||||
if (menuItemIndex == -1 || [[treeChild 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 = [CPDictionary dictionaryWithObjectsAndKeys:criteria, @"criteria", values, @"displayValues", rowType, @"rowType"];
|
|
||||||
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Get the predicate
|
|
||||||
|
|
||||||
- (void)_updatePredicate
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"objectValue"];
|
|
||||||
[self _updatePredicateFromRows];
|
|
||||||
[self didChangeValueForKey:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_updatePredicateFromRows
|
|
||||||
{
|
|
||||||
var rootRowsArray = [super _rootRowsArray],
|
|
||||||
subpredicates = [CPMutableArray array],
|
|
||||||
count = count2 = [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];
|
|
||||||
|
|
||||||
[super _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];
|
|
||||||
var 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
|
|
||||||
{
|
|
||||||
[self _updatePredicate];
|
|
||||||
[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
|
|
||||||
|
|
||||||
/*! @endcond */
|
|
||||||
@@ -1,788 +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
|
|
||||||
*/
|
|
||||||
|
|
||||||
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:CPMakeRect(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]];
|
|
||||||
|
|
||||||
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,132 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@class _CPPredicateEditorTree;
|
|
||||||
@implementation _CPPredicateEditorRowNode : CPObject
|
|
||||||
{
|
|
||||||
_CPPredicateEditorTree tree @accessors;
|
|
||||||
CPMutableArray templateViews @accessors;
|
|
||||||
CPMutableArray copiedTemplateContainer @accessors;
|
|
||||||
CPArray children @accessors(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)rowNodeFromTree:(id)aTree
|
|
||||||
{
|
|
||||||
var mapTable = {};
|
|
||||||
return [_CPPredicateEditorRowNode _rowNodeFromTree:aTree withTemplateTable:mapTable];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)_rowNodeFromTree:(id)aTree withTemplateTable:(id)templateTable
|
|
||||||
{
|
|
||||||
var node,
|
|
||||||
views,
|
|
||||||
copiedContainer;
|
|
||||||
|
|
||||||
node = [[_CPPredicateEditorRowNode alloc] init];
|
|
||||||
node.tree = aTree;
|
|
||||||
|
|
||||||
var template = [aTree template],
|
|
||||||
uuid = [template UID];
|
|
||||||
|
|
||||||
var cachedNode = templateTable[uuid];
|
|
||||||
|
|
||||||
if (cachedNode == nil)
|
|
||||||
{
|
|
||||||
views = [CPMutableArray array];
|
|
||||||
copiedContainer = [CPMutableArray array];
|
|
||||||
templateTable[uuid] = node;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
views = [cachedNode templateViews];
|
|
||||||
copiedContainer = [cachedNode copiedTemplateContainer];
|
|
||||||
}
|
|
||||||
|
|
||||||
node.templateViews = views;
|
|
||||||
node.copiedTemplateContainer = 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];
|
|
||||||
if (t !== template)
|
|
||||||
{
|
|
||||||
[templateViews setArray:views];
|
|
||||||
[copiedTemplateContainer removeAllObjects];
|
|
||||||
[copiedTemplateContainer addObject:template];
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = [children count];
|
|
||||||
for (var i; 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)
|
|
||||||
{
|
|
||||||
CPLogConsole("COPYING TEMPLATE");
|
|
||||||
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,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@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,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
|
||||||
@import <Foundation/CPDictionary.j>
|
|
||||||
@import <Foundation/CPString.j>
|
|
||||||
|
|
||||||
var regex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)?");
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorLocalizer : CPObject
|
|
||||||
{
|
|
||||||
CPDictionary _dictionary @accessors(property=dictionary);
|
|
||||||
CPURLConnection connection;
|
|
||||||
CPURLRequest resquest;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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 = [CPDictionary dictionary],
|
|
||||||
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 = regex.exec(line);
|
|
||||||
if (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;
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var GRADIENT_NORMAL,
|
|
||||||
GRADIENT_HIGHLIGHTED,
|
|
||||||
IE_FILTER = "progid:DXImageTransform.Microsoft.gradient(startColorstr='#fcfcfc', endColorstr='#dfdfdf')";
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorPopUpButton : CPPopUpButton
|
|
||||||
{
|
|
||||||
CPInteger radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)initialize
|
|
||||||
{
|
|
||||||
if (CPBrowserIsEngine(CPWebKitBrowserEngine))
|
|
||||||
{
|
|
||||||
GRADIENT_NORMAL = "-webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223)))";
|
|
||||||
GRADIENT_HIGHLIGHTED = "-webkit-gradient(linear, left top, left bottom, from(rgb(223, 223, 223)), to(rgb(252, 252, 252)))";
|
|
||||||
}
|
|
||||||
else if (CPBrowserIsEngine(CPGeckoBrowserEngine))
|
|
||||||
{
|
|
||||||
GRADIENT_NORMAL = "-moz-linear-gradient(top, rgb(252, 252, 252), rgb(223, 223, 223))";
|
|
||||||
GRADIENT_HIGHLIGHTED = "-moz-linear-gradient(top, rgb(223, 223, 223), rgb(252, 252, 252))";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame])
|
|
||||||
{
|
|
||||||
var style = _DOMElement.style;
|
|
||||||
style.backgroundImage = GRADIENT_NORMAL;
|
|
||||||
style.border = "1px solid rgb(189, 189, 189)";
|
|
||||||
style.filter = IE_FILTER;
|
|
||||||
|
|
||||||
[self setTextColor:[CPColor colorWithWhite:101 / 255 alpha:1]];
|
|
||||||
[self setBordered:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)hitTest:(CPPoint)point
|
|
||||||
{
|
|
||||||
var slice = [self superview];
|
|
||||||
if (!CPRectContainsPoint([self frame], point) || ![self sliceIsEditable])
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setHighlighted:(BOOL)shouldHighlight
|
|
||||||
{
|
|
||||||
_DOMElement.style.backgroundImage = (shouldHighlight) ? GRADIENT_HIGHLIGHTED : GRADIENT_NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)sliceIsEditable
|
|
||||||
{
|
|
||||||
return [[self 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
|
|
||||||
{
|
|
||||||
radius = FLOOR(CGRectGetHeight([self bounds])/2);
|
|
||||||
|
|
||||||
var style = _DOMElement.style,
|
|
||||||
radiusCSS = radius + "px";
|
|
||||||
|
|
||||||
//style.webkitBorderRadius = radiusCSS;
|
|
||||||
//style.mozBorderRadius = radiusCSS;
|
|
||||||
style.borderRadius = radiusCSS;
|
|
||||||
|
|
||||||
[super layoutSubviews];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
var bounds = [self bounds],
|
|
||||||
context = [[CPGraphicsContext currentContext] graphicsPort];
|
|
||||||
|
|
||||||
var 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
[self setFont:[CPFont boldFontWithName:@"Apple Symbol" size:12.0]];
|
|
||||||
[self setTextColor:[CPColor colorWithWhite:150/255 alpha:1]];
|
|
||||||
[self setAlignment:CPCenterTextAlignment];
|
|
||||||
[self setAutoresizingMask:CPViewMinXMargin];
|
|
||||||
[self setImagePosition:CPImageOnly];
|
|
||||||
[self setBordered:NO];
|
|
||||||
var style = _DOMElement.style;
|
|
||||||
style.border = "1px solid rgb(189, 189, 189)";
|
|
||||||
style.filter = IE_FILTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
radius = FLOOR(CGRectGetHeight([self bounds])/2);
|
|
||||||
|
|
||||||
var style = _DOMElement.style,
|
|
||||||
radiusCSS = radius + "px";
|
|
||||||
|
|
||||||
style.borderRadius = radiusCSS;
|
|
||||||
style.backgroundImage = ([self isHighlighted]) ? GRADIENT_HIGHLIGHTED : GRADIENT_NORMAL;
|
|
||||||
|
|
||||||
[super layoutSubviews];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorViewSlice : CPView
|
|
||||||
{
|
|
||||||
CPRuleEditor _ruleEditor;
|
|
||||||
int _indentation @accessors(property=indentation);
|
|
||||||
int _rowIndex @accessors(property=rowIndex);
|
|
||||||
CPRect _animationTargetRect @accessors(property=_animationTargetRect);
|
|
||||||
BOOL _selected @accessors(getter=_isSelected, setter=_setSelected:);
|
|
||||||
BOOL _lastSelected @accessors(getter=_isLastSelected, setter=_setLastSelected:);
|
|
||||||
CPColor _backgroundColor @accessors(property=backgroundColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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 ? @"setThemeState:" : @"unsetThemeState:";
|
|
||||||
[[self subviews] makeObjectsPerformSelector:CPSelectorFromString(selector) withObject:CPThemeStateSelectedDataView];
|
|
||||||
_selected = select;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(CPRect)rect
|
|
||||||
{
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
bounds = [self bounds],
|
|
||||||
maxX = CGRectGetWidth(bounds) - 2,
|
|
||||||
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, 1, 0);
|
|
||||||
CGContextAddLineToPoint(context, maxX, 0);
|
|
||||||
CGContextClosePath(context);
|
|
||||||
CGContextSetStrokeColor(context, [_ruleEditor _sliceTopBorderColor]);
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
|
|
||||||
// Draw Bottom Border
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
CGContextMoveToPoint(context, 1, maxY - 0.5);
|
|
||||||
CGContextAddLineToPoint(context, maxX, maxY - 0.5);
|
|
||||||
CGContextClosePath(context);
|
|
||||||
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,512 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by cacaodev@gmail.com.
|
|
||||||
* Copyright (c) 2011 Pear, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "_CPRuleEditorViewSlice.j"
|
|
||||||
@import "_CPRuleEditorPopUpButton.j"
|
|
||||||
@import "CPRuleEditor.j"
|
|
||||||
|
|
||||||
var CONTROL_HEIGHT = 16.,
|
|
||||||
BUTTON_HEIGHT = 16.;
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorViewSliceRow : _CPRuleEditorViewSlice
|
|
||||||
{
|
|
||||||
CPMutableArray _ruleOptionViews;
|
|
||||||
CPMutableArray _ruleOptionFrames;
|
|
||||||
CPMutableArray _correspondingRuleItems;
|
|
||||||
CPMutableArray _ruleOptionInitialViewFrames;
|
|
||||||
CPButton _addButton;
|
|
||||||
CPButton _subtractButton;
|
|
||||||
BOOL editable;
|
|
||||||
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)_createAddRowButton
|
|
||||||
{
|
|
||||||
var button = [[_CPRuleEditorButton alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[button setImage:[_ruleEditor _addImage]];
|
|
||||||
|
|
||||||
[button setAction:@selector(_addOption:)];
|
|
||||||
[button setTarget:self];
|
|
||||||
[button setAutoresizingMask:CPViewMinXMargin];
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPButton)_createDeleteRowButton
|
|
||||||
{
|
|
||||||
var button = [[_CPRuleEditorButton alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[button setImage:[_ruleEditor _removeImage]];
|
|
||||||
|
|
||||||
[button setAction:@selector(_deleteOption:)];
|
|
||||||
[button setTarget:self];
|
|
||||||
[button setAutoresizingMask:CPViewMinXMargin];
|
|
||||||
|
|
||||||
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];
|
|
||||||
var font = [_ruleEditor font],
|
|
||||||
width = [title sizeWithFont:font].width + 20,
|
|
||||||
rect = CGRectMake(0, 0, (width - width % 40) + 80, CONTROL_HEIGHT);
|
|
||||||
|
|
||||||
var popup = [[_CPRuleEditorPopUpButton alloc] initWithFrame:rect];
|
|
||||||
[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
|
|
||||||
{
|
|
||||||
text = [[_ruleEditor standardLocalizer] localizedStringForString:text];
|
|
||||||
|
|
||||||
var textField = [[_CPRuleEditorTextField alloc] initWithFrame:CPMakeRect(0, 0, 200, CONTROL_HEIGHT)];
|
|
||||||
var font = [_ruleEditor font];
|
|
||||||
font = [CPFont fontWithName:font._name size:font._size + 2];
|
|
||||||
[textField setValue:font forThemeAttribute:@"font"];
|
|
||||||
[textField setStringValue:text];
|
|
||||||
[textField sizeToFit];
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
var ruleItems = [CPMutableArray array];
|
|
||||||
|
|
||||||
[self _emptyRulePartSubviews];
|
|
||||||
|
|
||||||
criteria = [_ruleEditor criteriaForRow:_rowIndex];
|
|
||||||
numberOfCriteria = [criteria count];
|
|
||||||
|
|
||||||
firstResponderIndex = numberOfCriteria - 1;
|
|
||||||
var responder = [[self window] firstResponder];
|
|
||||||
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];
|
|
||||||
|
|
||||||
var selectedIndex = [childItems indexOfObject:criterion];
|
|
||||||
if (selectedIndex == CPNotFound)
|
|
||||||
break;
|
|
||||||
|
|
||||||
for (var j = 0; j < numberOfChildren; ++j)
|
|
||||||
{
|
|
||||||
var childItem = [childItems objectAtIndex:j];
|
|
||||||
var 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 = [CPDictionary dictionaryWithObjectsAndKeys:childItem, @"item", childValue, @"value", i, @"indexInCriteria"];
|
|
||||||
[menuItem setRepresentedObject:repObject];
|
|
||||||
[menuItems addObject:menuItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleView = [self _createPopUpButtonWithItems:menuItems selectedItemIndex:selectedIndex];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var value = [childValues objectAtIndex:0];
|
|
||||||
var 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
|
|
||||||
{
|
|
||||||
// CPLogConsole(_cmd);
|
|
||||||
[self _relayoutSubviewsWidthChanged:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_relayoutSubviewsWidthChanged:(BOOL)widthChanged
|
|
||||||
{
|
|
||||||
var optionViewOriginX,
|
|
||||||
leftHorizontalPadding,
|
|
||||||
leftButtonMinX,
|
|
||||||
rowHeight = [_ruleEditor rowHeight],
|
|
||||||
count = [_ruleOptionViews count],
|
|
||||||
sliceFrame = [self frame];
|
|
||||||
|
|
||||||
var 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEditable
|
|
||||||
{
|
|
||||||
return editable;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setEditable:(BOOL)value
|
|
||||||
{
|
|
||||||
editable = 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 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:(CPRect)rect
|
|
||||||
{
|
|
||||||
[super drawRect:rect];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_isRulePopup:(CPView)view
|
|
||||||
{
|
|
||||||
if ([view isKindOfClass:[_CPRuleEditorPopUpButton class]])
|
|
||||||
return YES;
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_isRuleStaticTextField:(CPView)view
|
|
||||||
{
|
|
||||||
if ([view isKindOfClass:[_CPRuleEditorTextField class]])
|
|
||||||
return YES;
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_sendRuleAction:(id)sender
|
|
||||||
{
|
|
||||||
[_ruleEditor _sendRuleAction];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_textDidChange:(CPNotification)aNotif
|
|
||||||
{
|
|
||||||
if ([[aNotif object] superview] == self && [_ruleEditor _sendsActionOnIncompleteTextChange])
|
|
||||||
[_ruleEditor _sendRuleAction];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _CPRuleEditorTextField : CPTextField
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:frame];
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
[self setBordered:NO];
|
|
||||||
[self setEditable:NO];
|
|
||||||
[self setDrawsBackground:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)hitTest:(CPPoint)point
|
|
||||||
{
|
|
||||||
if (!CPRectContainsPoint([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"
|
||||||
|
|
||||||
|
|||||||
+1
-35
@@ -1,45 +1,11 @@
|
|||||||
/*
|
|
||||||
* 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)
|
||||||
|
|||||||
+533
-958
File diff suppressed because it is too large
Load Diff
+75
-270
@@ -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
|
||||||
@@ -25,6 +22,7 @@
|
|||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
|
|
||||||
|
|
||||||
// CPScroller Constants
|
// CPScroller Constants
|
||||||
CPScrollerNoPart = 0;
|
CPScrollerNoPart = 0;
|
||||||
CPScrollerDecrementPage = 1;
|
CPScrollerDecrementPage = 1;
|
||||||
@@ -56,17 +54,6 @@ 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;
|
||||||
@@ -81,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";
|
||||||
@@ -102,43 +78,49 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return [CPDictionary dictionaryWithJSObject:{
|
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
|
||||||
{
|
{
|
||||||
var scroller = [[self alloc] init];
|
return [[[CPScroller alloc] init] currentValueForThemeAttribute:@"scroller-width"];
|
||||||
|
|
||||||
if (aStyle == CPScrollerStyleLegacy)
|
|
||||||
return [scroller valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
|
|
||||||
return [scroller currentValueForThemeAttribute:@"scroller-width"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the CPScroller's overlay value.
|
|
||||||
*/
|
|
||||||
+ (float)scrollerOverlay
|
|
||||||
{
|
|
||||||
return [[[self alloc] init] currentValueForThemeAttribute:@"track-border-overlay"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -150,87 +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 = [CPDictionary dictionaryWithObjects:[self, CPViewAnimationFadeOutEffect]
|
|
||||||
forKeys:[CPViewAnimationTargetKey, CPViewAnimationEffectKey]];
|
|
||||||
|
|
||||||
_animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
|
|
||||||
[_animationScroller setViewAnimations:[paramAnimFadeOut]];
|
|
||||||
[_animationScroller setDelegate:self];
|
|
||||||
[self setAlphaValue:0.0];
|
|
||||||
[self _calculateIsVertical];
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
@@ -247,17 +148,18 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Return's the knob's proportion
|
Returns the scroller's control size
|
||||||
*/
|
*/
|
||||||
- (float)knobProportion
|
- (CPControlSize)controlSize
|
||||||
{
|
{
|
||||||
return _knobProportion;
|
return _controlSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setObjectValue:(id)aValue
|
||||||
|
{
|
||||||
|
[super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the knob's proportion
|
|
||||||
@param aProportion the desired proportion
|
|
||||||
*/
|
|
||||||
- (void)setKnobProportion:(float)aProportion
|
- (void)setKnobProportion:(float)aProportion
|
||||||
{
|
{
|
||||||
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
||||||
@@ -266,35 +168,25 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
#pragma mark -
|
Return's the knob's proportion
|
||||||
#pragma mark Privates
|
*/
|
||||||
|
- (float)knobProportion
|
||||||
/*! @ignore */
|
|
||||||
- (void)_adjustScrollerSize
|
|
||||||
{
|
{
|
||||||
var frame = [self frame],
|
return _knobProportion;
|
||||||
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 */
|
- (id)currentValueForThemeAttribute:(CPString)anAttributeName
|
||||||
- (void)_performFadeOut:(CPTimer)aTimer
|
|
||||||
{
|
{
|
||||||
[self fadeOut];
|
var themeState = _themeState;
|
||||||
_timerFadeOut = nil;
|
|
||||||
|
if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
|
||||||
|
themeState &= ~CPThemeStateHighlighted;
|
||||||
|
|
||||||
|
return [self valueForThemeAttribute:anAttributeName inState:themeState];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculating Layout
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Utilities
|
|
||||||
|
|
||||||
- (CGRect)rectForPart:(CPScrollerPart)aPart
|
- (CGRect)rectForPart:(CPScrollerPart)aPart
|
||||||
{
|
{
|
||||||
@@ -316,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;
|
||||||
|
|
||||||
@@ -378,16 +267,16 @@ 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);
|
||||||
|
|
||||||
@@ -407,16 +296,16 @@ 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);
|
||||||
|
|
||||||
@@ -427,7 +316,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
{
|
{
|
||||||
_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,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
|
||||||
@@ -597,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]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -704,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])
|
||||||
@@ -736,52 +582,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (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",
|
||||||
CPScrollerKnobProportionKey = @"CPScrollerKnobProportion",
|
CPScrollerKnobProportionKey = "CPScrollerKnobProportion";
|
||||||
CPScrollerStyleKey = @"CPScrollerStyleKey";
|
|
||||||
|
|
||||||
@implementation CPScroller (CPCoding)
|
@implementation CPScroller (CPCoding)
|
||||||
|
|
||||||
@@ -801,18 +605,20 @@ var CPScrollerControlSizeKey = @"CPScrollerControlSize",
|
|||||||
|
|
||||||
_hitPart = CPScrollerNoPart;
|
_hitPart = CPScrollerNoPart;
|
||||||
|
|
||||||
_allowFadingOut = YES;
|
|
||||||
_isMouseOver = NO;
|
|
||||||
var paramAnimFadeOut = [CPDictionary dictionaryWithObjects:[self, CPViewAnimationFadeOutEffect]
|
|
||||||
forKeys:[CPViewAnimationTargetKey, CPViewAnimationEffectKey]];
|
|
||||||
_animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
|
|
||||||
[_animationScroller setViewAnimations:[paramAnimFadeOut]];
|
|
||||||
[_animationScroller setDelegate:self];
|
|
||||||
[self setAlphaValue:0.0];
|
|
||||||
|
|
||||||
[self _calculateIsVertical];
|
[self _calculateIsVertical];
|
||||||
|
|
||||||
[self setStyle:[aCoder decodeIntForKey:CPScrollerStyleKey]];
|
// Adjust the size of the scroller if the size from cib
|
||||||
|
// isn't equal to the scrollerWidth
|
||||||
|
var frame = [self frame],
|
||||||
|
scrollerWidth = [CPScroller scrollerWidth];
|
||||||
|
|
||||||
|
if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
|
||||||
|
frame.size.width = scrollerWidth;
|
||||||
|
|
||||||
|
if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
|
||||||
|
frame.size.height = scrollerWidth;
|
||||||
|
|
||||||
|
[self setFrame:frame];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -824,7 +630,6 @@ var CPScrollerControlSizeKey = @"CPScrollerControlSize",
|
|||||||
|
|
||||||
[aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
|
[aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
|
||||||
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
|
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
|
||||||
[aCoder encodeInt:_style forKey:CPScrollerStyleKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
+46
-33
@@ -23,10 +23,12 @@
|
|||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
@import "CPTextField.j"
|
@import "CPTextField.j"
|
||||||
|
|
||||||
|
|
||||||
CPSearchFieldRecentsTitleMenuItemTag = 1000;
|
CPSearchFieldRecentsTitleMenuItemTag = 1000;
|
||||||
CPSearchFieldRecentsMenuItemTag = 1001;
|
CPSearchFieldRecentsMenuItemTag = 1001;
|
||||||
CPSearchFieldClearRecentsMenuItemTag = 1002;
|
CPSearchFieldClearRecentsMenuItemTag = 1002;
|
||||||
CPSearchFieldNoRecentsMenuItemTag = 1003;
|
CPSearchFieldNoRecentsMenuItemTag = 1003;
|
||||||
|
CPSearchFieldSeparatorMenuItemTag = 1004;
|
||||||
|
|
||||||
var CPSearchFieldSearchImage = nil,
|
var CPSearchFieldSearchImage = nil,
|
||||||
CPSearchFieldFindImage = nil,
|
CPSearchFieldFindImage = nil,
|
||||||
@@ -37,10 +39,9 @@ var SEARCH_BUTTON_DEFAULT_WIDTH = 25.0,
|
|||||||
CANCEL_BUTTON_DEFAULT_WIDTH = 22.0,
|
CANCEL_BUTTON_DEFAULT_WIDTH = 22.0,
|
||||||
BUTTON_DEFAULT_HEIGHT = 22.0;
|
BUTTON_DEFAULT_HEIGHT = 22.0;
|
||||||
|
|
||||||
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification";
|
|
||||||
|
|
||||||
var RECENT_SEARCH_PREFIX = @" ";
|
var RECENT_SEARCH_PREFIX = @" ";
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPSearchField
|
@class CPSearchField
|
||||||
@@ -109,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],
|
||||||
@@ -124,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
|
||||||
@@ -192,7 +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:)];
|
||||||
[self _updateCancelButtonVisibility];
|
[self _updateCancelButtonVisibility];
|
||||||
[self addSubview:_cancelButton];
|
[self addSubview:_cancelButton];
|
||||||
}
|
}
|
||||||
@@ -209,7 +199,7 @@ 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
|
||||||
{
|
{
|
||||||
@@ -220,7 +210,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
|||||||
[button setAlternateImage:CPSearchFieldCancelPressedImage];
|
[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
|
||||||
@@ -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])
|
||||||
{
|
{
|
||||||
@@ -539,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.
|
||||||
@@ -638,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];
|
||||||
@@ -688,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];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,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
|
||||||
@@ -789,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"
|
||||||
|
|
||||||
|
|||||||
@@ -94,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)
|
||||||
{
|
{
|
||||||
@@ -115,14 +113,6 @@ CPSegmentSwitchTrackingMomentary = 2;
|
|||||||
if (_selectedSegment >= _segments.length)
|
if (_selectedSegment >= _segments.length)
|
||||||
_selectedSegment = -1;
|
_selectedSegment = -1;
|
||||||
|
|
||||||
// Make space for/remove space used by dividers.
|
|
||||||
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
|
|
||||||
delta = thickness * (dividersAfter - dividersBefore),
|
|
||||||
frame = [self frame];
|
|
||||||
|
|
||||||
if (delta)
|
|
||||||
[self setFrameSize:CGSizeMake(frame.size.width + delta, frame.size.height)];
|
|
||||||
|
|
||||||
[self tileWithChangedSegment:0];
|
[self tileWithChangedSegment:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,9 +351,9 @@ 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)isEnabled forSegment:(unsigned)aSegment
|
- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
|
||||||
@@ -617,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)
|
||||||
{
|
{
|
||||||
|
|||||||
+3
-6
@@ -382,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
|
||||||
{
|
{
|
||||||
@@ -430,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];
|
||||||
|
|||||||
@@ -63,22 +63,22 @@
|
|||||||
return [super initWithPickerMask:mask colorPanel: owningColorPanel];
|
return [super initWithPickerMask:mask colorPanel: owningColorPanel];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initView
|
-(id)initView
|
||||||
{
|
{
|
||||||
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];
|
||||||
@@ -86,18 +86,18 @@
|
|||||||
[_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];
|
||||||
@@ -105,18 +105,18 @@
|
|||||||
[_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];
|
||||||
@@ -124,22 +124,22 @@
|
|||||||
[_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: 359.0];
|
[_hueSlider setMaxValue: 359.0];
|
||||||
[_hueSlider setMinValue: 0.0];
|
[_hueSlider setMinValue: 0.0];
|
||||||
[_hueSlider setTarget: self];
|
[_hueSlider setTarget: self];
|
||||||
@@ -147,18 +147,18 @@
|
|||||||
[_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: 100.0];
|
[_saturationSlider setMaxValue: 100.0];
|
||||||
[_saturationSlider setMinValue: 0.0];
|
[_saturationSlider setMinValue: 0.0];
|
||||||
[_saturationSlider setTarget: self];
|
[_saturationSlider setTarget: self];
|
||||||
@@ -166,18 +166,18 @@
|
|||||||
[_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: 100.0];
|
[_brightnessSlider setMaxValue: 100.0];
|
||||||
[_brightnessSlider setMinValue: 0.0];
|
[_brightnessSlider setMinValue: 0.0];
|
||||||
[_brightnessSlider setTarget: self];
|
[_brightnessSlider setTarget: self];
|
||||||
@@ -185,19 +185,19 @@
|
|||||||
[_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];
|
||||||
@@ -240,13 +240,13 @@
|
|||||||
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:
|
||||||
@@ -274,7 +274,7 @@
|
|||||||
[colorPanel setColor: newColor];
|
[colorPanel setColor: newColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setColor:(CPColor)aColor
|
-(void)setColor:(CPColor)aColor
|
||||||
{
|
{
|
||||||
[self updateRGBSliders: aColor];
|
[self updateRGBSliders: aColor];
|
||||||
[self updateHSBSliders: aColor];
|
[self updateHSBSliders: aColor];
|
||||||
@@ -282,7 +282,7 @@
|
|||||||
[self updateLabels];
|
[self updateLabels];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateHSBSliders:(CPColor)aColor
|
-(void)updateHSBSliders:(CPColor)aColor
|
||||||
{
|
{
|
||||||
var hsb = [aColor hsbComponents];
|
var hsb = [aColor hsbComponents];
|
||||||
|
|
||||||
@@ -330,21 +330,14 @@
|
|||||||
{
|
{
|
||||||
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];
|
var newColor = [CPColor colorWithHexString: value];
|
||||||
|
if (newColor) {
|
||||||
if (newColor)
|
|
||||||
{
|
|
||||||
[self setColor: newColor];
|
[self setColor: newColor];
|
||||||
[[self colorPanel] 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;
|
||||||
|
|||||||
@@ -1,317 +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>
|
|
||||||
|
|
||||||
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
|
|
||||||
+50
-430
@@ -20,81 +20,41 @@
|
|||||||
* 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 "CPButtonBar.j"
|
@import "CPButtonBar.j"
|
||||||
@import "CPImage.j"
|
@import "CPImage.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
#define SPLIT_VIEW_MAYBE_POST_WILL_RESIZE() \
|
|
||||||
if ((_suppressResizeNotificationsMask & DidPostWillResizeNotification) === 0) \
|
|
||||||
{ \
|
|
||||||
[self _postNotificationWillResize]; \
|
|
||||||
_suppressResizeNotificationsMask |= DidPostWillResizeNotification; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPLIT_VIEW_MAYBE_POST_DID_RESIZE() \
|
|
||||||
if ((_suppressResizeNotificationsMask & ShouldSuppressResizeNotifications) !== 0) \
|
|
||||||
_suppressResizeNotificationsMask |= DidSuppressResizeNotification; \
|
|
||||||
else \
|
|
||||||
[self _postNotificationDidResize];
|
|
||||||
|
|
||||||
#define SPLIT_VIEW_DID_SUPPRESS_RESIZE_NOTIFICATION() \
|
|
||||||
((_suppressResizeNotificationsMask & DidSuppressResizeNotification) !== 0)
|
|
||||||
|
|
||||||
#define SPLIT_VIEW_SUPPRESS_RESIZE_NOTIFICATIONS(shouldSuppress) \
|
|
||||||
if (shouldSuppress) \
|
|
||||||
_suppressResizeNotificationsMask |= ShouldSuppressResizeNotifications; \
|
|
||||||
else \
|
|
||||||
_suppressResizeNotificationsMask = 0;
|
|
||||||
|
|
||||||
CPSplitViewDidResizeSubviewsNotification = @"CPSplitViewDidResizeSubviewsNotification";
|
CPSplitViewDidResizeSubviewsNotification = @"CPSplitViewDidResizeSubviewsNotification";
|
||||||
CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotification";
|
CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotification";
|
||||||
|
|
||||||
var CPSplitViewHorizontalImage = nil,
|
var CPSplitViewHorizontalImage = nil,
|
||||||
CPSplitViewVerticalImage = nil,
|
CPSplitViewVerticalImage = nil;
|
||||||
|
|
||||||
ShouldSuppressResizeNotifications = 1,
|
|
||||||
DidPostWillResizeNotification = 1 << 1,
|
|
||||||
DidSuppressResizeNotification = 1 << 2;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPSplitView
|
|
||||||
|
|
||||||
CPSplitView is a view that allows you to stack several subviews vertically or horizontally. The user is given divider to resize the subviews.
|
|
||||||
The divider indices are zero-based. So the divider on the top (or left for vertical dividers) will be index 0.
|
|
||||||
|
|
||||||
CPSplitView can be supplied a delegate to provide control over the resizing of the splitview and subviews. Those methods are documented in setDelegate:
|
|
||||||
|
|
||||||
CPSplitView will add dividers for each subview you add. So just like adding subviews to a CPView you should call addSubview: to add new resizable subviews in your splitview.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@implementation CPSplitView : CPView
|
@implementation CPSplitView : CPView
|
||||||
{
|
{
|
||||||
id _delegate;
|
id _delegate;
|
||||||
BOOL _isVertical;
|
BOOL _isVertical;
|
||||||
BOOL _isPaneSplitter;
|
BOOL _isPaneSplitter;
|
||||||
|
|
||||||
int _currentDivider;
|
int _currentDivider;
|
||||||
float _initialOffset;
|
float _initialOffset;
|
||||||
CPDictionary _preCollapsePositions;
|
float _preCollapsePosition;
|
||||||
|
|
||||||
CPString _originComponent;
|
CPString _originComponent;
|
||||||
CPString _sizeComponent;
|
CPString _sizeComponent;
|
||||||
|
|
||||||
CPArray _DOMDividerElements;
|
CPArray _DOMDividerElements;
|
||||||
CPString _dividerImagePath;
|
CPString _dividerImagePath;
|
||||||
int _drawingDivider;
|
int _drawingDivider;
|
||||||
|
|
||||||
CPString _autosaveName;
|
BOOL _needsResizeSubviews;
|
||||||
BOOL _shouldAutosave;
|
|
||||||
BOOL _needsRestoreFromAutosave;
|
|
||||||
|
|
||||||
BOOL _needsResizeSubviews;
|
CPArray _buttonBars;
|
||||||
int _suppressResizeNotificationsMask;
|
|
||||||
|
|
||||||
CPArray _buttonBars;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)defaultThemeClass
|
||||||
@@ -104,7 +64,7 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return [CPDictionary dictionaryWithObjects:[1.0, 10.0, [CPColor grayColor]]
|
return [CPDictionary dictionaryWithObjects:[10.0, 1.0, [CPColor grayColor]]
|
||||||
forKeys:[@"divider-thickness", @"pane-divider-thickness", @"pane-divider-color"]];
|
forKeys:[@"divider-thickness", @"pane-divider-thickness", @"pane-divider-color"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,43 +85,27 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
{
|
{
|
||||||
if (self = [super initWithFrame:aFrame])
|
if (self = [super initWithFrame:aFrame])
|
||||||
{
|
{
|
||||||
_suppressResizeNotificationsMask = 0;
|
|
||||||
_preCollapsePositions = [CPMutableDictionary new];
|
|
||||||
_currentDivider = CPNotFound;
|
_currentDivider = CPNotFound;
|
||||||
|
|
||||||
_DOMDividerElements = [];
|
_DOMDividerElements = [];
|
||||||
_buttonBars = [];
|
_buttonBars = [];
|
||||||
|
|
||||||
_shouldAutosave = YES;
|
|
||||||
|
|
||||||
[self _setVertical:YES];
|
[self _setVertical:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the thickness of the divider.
|
|
||||||
@return float - the thickness of the divider.
|
|
||||||
*/
|
|
||||||
- (float)dividerThickness
|
- (float)dividerThickness
|
||||||
{
|
{
|
||||||
return [self currentValueForThemeAttribute:[self isPaneSplitter] ? @"pane-divider-thickness" : @"divider-thickness"];
|
return [self currentValueForThemeAttribute:[self isPaneSplitter] ? @"pane-divider-thickness" : @"divider-thickness"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns YES if the dividers are vertical, otherwise NO.
|
|
||||||
@return YES if vertical, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)isVertical
|
- (BOOL)isVertical
|
||||||
{
|
{
|
||||||
return _isVertical;
|
return _isVertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets if the splitview dividers are vertical.
|
|
||||||
@param shouldBeVertical - YES if the splitview dividers should be vertical, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setVertical:(BOOL)shouldBeVertical
|
- (void)setVertical:(BOOL)shouldBeVertical
|
||||||
{
|
{
|
||||||
if (![self _setVertical:shouldBeVertical])
|
if (![self _setVertical:shouldBeVertical])
|
||||||
@@ -203,21 +147,11 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Use to find if the divider is a larger pane splitter.
|
|
||||||
|
|
||||||
@return BOOL - YES if the dividers are the larger pane splitters. Otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)isPaneSplitter
|
- (BOOL)isPaneSplitter
|
||||||
{
|
{
|
||||||
return _isPaneSplitter;
|
return _isPaneSplitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Used to set if the split view dividers should be the larger pane splitter.
|
|
||||||
|
|
||||||
@param shouldBePaneSplitter - YES if the dividers should be the thicker pane splitter, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setIsPaneSplitter:(BOOL)shouldBePaneSplitter
|
- (void)setIsPaneSplitter:(BOOL)shouldBePaneSplitter
|
||||||
{
|
{
|
||||||
if (_isPaneSplitter == shouldBePaneSplitter)
|
if (_isPaneSplitter == shouldBePaneSplitter)
|
||||||
@@ -239,22 +173,11 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
_needsResizeSubviews = YES;
|
_needsResizeSubviews = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns YES if the supplied subview is collapsed, otherwise NO.
|
|
||||||
@param aSubview - the subview you are interested in.
|
|
||||||
@return BOOL - YES if the subview is collapsed, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)isSubviewCollapsed:(CPView)subview
|
- (BOOL)isSubviewCollapsed:(CPView)subview
|
||||||
{
|
{
|
||||||
return [subview frame].size[_sizeComponent] < 1 ? YES : NO;
|
return [subview frame].size[_sizeComponent] < 1 ? YES : NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the CGRect of the divider at a given index.
|
|
||||||
|
|
||||||
@param int - The index of a divider.
|
|
||||||
@return CGRect - The rect of a divider.
|
|
||||||
*/
|
|
||||||
- (CGRect)rectOfDividerAtIndex:(int)aDivider
|
- (CGRect)rectOfDividerAtIndex:(int)aDivider
|
||||||
{
|
{
|
||||||
var frame = [_subviews[aDivider] frame],
|
var frame = [_subviews[aDivider] frame],
|
||||||
@@ -268,12 +191,6 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the rect of the divider which the user is able to drag to resize.
|
|
||||||
|
|
||||||
@param int - The index of the divider.
|
|
||||||
@return CGRect - The rect the user can drag.
|
|
||||||
*/
|
|
||||||
- (CGRect)effectiveRectOfDividerAtIndex:(int)aDivider
|
- (CGRect)effectiveRectOfDividerAtIndex:(int)aDivider
|
||||||
{
|
{
|
||||||
var realRect = [self rectOfDividerAtIndex:aDivider],
|
var realRect = [self rectOfDividerAtIndex:aDivider],
|
||||||
@@ -296,35 +213,6 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
Because we're use drawRect: to draw the dividers, but use DOM elements instead of CoreGraphics
|
|
||||||
We must remove those DOM elements (the splitters) when the subview is removed.
|
|
||||||
*/
|
|
||||||
- (void)willRemoveSubview:(CPView)aView
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var dividerToRemove = _DOMDividerElements.pop();
|
|
||||||
|
|
||||||
// The divider may not exist if we never rendered out the DOM.
|
|
||||||
if (dividerToRemove)
|
|
||||||
CPDOMDisplayServerRemoveChild(_DOMElement, dividerToRemove);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_needsResizeSubviews = YES;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
[self _adjustSubviewsWithCalculatedSize]
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Draws the divider at a given rect.
|
|
||||||
@param aRect - the rect of the divider to draw.
|
|
||||||
*/
|
|
||||||
- (void)drawDividerInRect:(CGRect)aRect
|
- (void)drawDividerInRect:(CGRect)aRect
|
||||||
{
|
{
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
@@ -348,13 +236,13 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
{
|
{
|
||||||
if (_isPaneSplitter)
|
if (_isPaneSplitter)
|
||||||
{
|
{
|
||||||
_DOMDividerElements[_drawingDivider].style.backgroundColor = "";
|
_DOMDividerElements[_drawingDivider].style.backgroundColor = [[self currentValueForThemeAttribute:@"pane-divider-color"] cssString];
|
||||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
|
_DOMDividerElements[_drawingDivider].style.backgroundImage = "";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_DOMDividerElements[_drawingDivider].style.backgroundColor = [[self currentValueForThemeAttribute:@"pane-divider-color"] cssString];
|
_DOMDividerElements[_drawingDivider].style.backgroundColor = "";
|
||||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "";
|
_DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +336,7 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
{
|
{
|
||||||
_currentDivider = CPNotFound;
|
_currentDivider = CPNotFound;
|
||||||
[self _updateResizeCursor:anEvent];
|
[self _updateResizeCursor:anEvent];
|
||||||
|
[self _postNotificationDidResize];
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -471,8 +360,7 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
[_delegate respondsToSelector:@selector(splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex:)])
|
[_delegate respondsToSelector:@selector(splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex:)])
|
||||||
{
|
{
|
||||||
var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
|
var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
|
||||||
maxPosition = [self maxPossiblePositionOfDividerAtIndex:i],
|
maxPosition = [self maxPossiblePositionOfDividerAtIndex:i];
|
||||||
_preCollapsePosition = [_preCollapsePositions objectForKey:"" + i] || 0;
|
|
||||||
|
|
||||||
if ([_delegate splitView:self canCollapseSubview:_subviews[i]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
|
if ([_delegate splitView:self canCollapseSubview:_subviews[i]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
|
||||||
{
|
{
|
||||||
@@ -562,34 +450,20 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
// If we are currently tracking, keep the resize cursor active even outside of hit areas.
|
// If we are currently tracking, keep the resize cursor active even outside of hit areas.
|
||||||
if (_currentDivider === i || (_currentDivider == CPNotFound && [self cursorAtPoint:point hitDividerAtIndex:i]))
|
if (_currentDivider === i || (_currentDivider == CPNotFound && [self cursorAtPoint:point hitDividerAtIndex:i]))
|
||||||
{
|
{
|
||||||
var frameA = [_subviews[i] frame],
|
var frame = [_subviews[i] frame],
|
||||||
sizeA = frameA.size[_sizeComponent],
|
size = frame.size[_sizeComponent],
|
||||||
startPosition = frameA.origin[_originComponent] + sizeA,
|
startPosition = frame.origin[_originComponent] + size,
|
||||||
frameB = [_subviews[i + 1] frame],
|
|
||||||
sizeB = frameB.size[_sizeComponent],
|
|
||||||
canShrink = [self _realPositionForPosition:startPosition - 1 ofDividerAtIndex:i] < startPosition,
|
canShrink = [self _realPositionForPosition:startPosition - 1 ofDividerAtIndex:i] < startPosition,
|
||||||
canGrow = [self _realPositionForPosition:startPosition + 1 ofDividerAtIndex:i] > startPosition,
|
canGrow = [self _realPositionForPosition:startPosition + 1 ofDividerAtIndex:i] > startPosition,
|
||||||
cursor = [CPCursor arrowCursor];
|
cursor = [CPCursor arrowCursor];
|
||||||
|
|
||||||
if (sizeA === 0)
|
if (size === 0)
|
||||||
canGrow = YES; // Subview is collapsed.
|
canGrow = YES; // Subview is collapsed.
|
||||||
else if (!canShrink &&
|
else if (!canShrink &&
|
||||||
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
|
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
|
||||||
[_delegate splitView:self canCollapseSubview:_subviews[i]])
|
[_delegate splitView:self canCollapseSubview:_subviews[i]])
|
||||||
canShrink = YES; // Subview is collapsible.
|
canShrink = YES; // Subview is collapsible.
|
||||||
|
|
||||||
if (sizeB === 0)
|
|
||||||
{
|
|
||||||
// Right/lower subview is collapsed.
|
|
||||||
canGrow = NO;
|
|
||||||
// It's safe to assume it can always be uncollapsed.
|
|
||||||
canShrink = YES;
|
|
||||||
}
|
|
||||||
else if (!canGrow &&
|
|
||||||
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
|
|
||||||
[_delegate splitView:self canCollapseSubview:_subviews[i + 1]])
|
|
||||||
canGrow = YES; // Right/lower subview is collapsible.
|
|
||||||
|
|
||||||
if (_isVertical && canShrink && canGrow)
|
if (_isVertical && canShrink && canGrow)
|
||||||
cursor = [CPCursor resizeLeftRightCursor];
|
cursor = [CPCursor resizeLeftRightCursor];
|
||||||
else if (_isVertical && canShrink)
|
else if (_isVertical && canShrink)
|
||||||
@@ -611,11 +485,6 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
[[CPCursor arrowCursor] set];
|
[[CPCursor arrowCursor] set];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the maximum possible position of a divider at a given index.
|
|
||||||
@param the index of the divider.
|
|
||||||
@return float - the max possible position.
|
|
||||||
*/
|
|
||||||
- (float)maxPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
- (float)maxPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
||||||
{
|
{
|
||||||
var frame = [_subviews[dividerIndex + 1] frame];
|
var frame = [_subviews[dividerIndex + 1] frame];
|
||||||
@@ -626,11 +495,6 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
return [self frame].size[_sizeComponent] - [self dividerThickness];
|
return [self frame].size[_sizeComponent] - [self dividerThickness];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the minimum possible position of a divider at a given index.
|
|
||||||
@param the index of the divider.
|
|
||||||
@return float - the min possible position.
|
|
||||||
*/
|
|
||||||
- (float)minPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
- (float)minPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
||||||
{
|
{
|
||||||
if (dividerIndex > 0)
|
if (dividerIndex > 0)
|
||||||
@@ -647,15 +511,7 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
{
|
{
|
||||||
// not sure where this should override other positions?
|
// not sure where this should override other positions?
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:constrainSplitPosition:ofSubviewAt:)])
|
if ([_delegate respondsToSelector:@selector(splitView:constrainSplitPosition:ofSubviewAt:)])
|
||||||
{
|
position = [_delegate splitView:self constrainSplitPosition:position ofSubviewAt:dividerIndex];
|
||||||
var proposedPosition = [_delegate splitView:self constrainSplitPosition:position ofSubviewAt:dividerIndex];
|
|
||||||
|
|
||||||
// Silently ignore bad positions which could result from odd delegate responses. We don't want these
|
|
||||||
// bad results to go into the system and cause havoc with frame sizes as the split view tries to resize
|
|
||||||
// its subviews.
|
|
||||||
if (_IS_NUMERIC(proposedPosition))
|
|
||||||
position = proposedPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
var proposedMax = [self maxPossiblePositionOfDividerAtIndex:dividerIndex],
|
var proposedMax = [self maxPossiblePositionOfDividerAtIndex:dividerIndex],
|
||||||
proposedMin = [self minPossiblePositionOfDividerAtIndex:dividerIndex],
|
proposedMin = [self minPossiblePositionOfDividerAtIndex:dividerIndex],
|
||||||
@@ -663,45 +519,24 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
actualMin = proposedMin;
|
actualMin = proposedMin;
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
|
if ([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
|
||||||
{
|
actualMin = [_delegate splitView:self constrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex];
|
||||||
var proposedActualMin = [_delegate splitView:self constrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex];
|
|
||||||
if (_IS_NUMERIC(proposedActualMin))
|
|
||||||
actualMin = proposedActualMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
|
if ([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
|
||||||
{
|
actualMax = [_delegate splitView:self constrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
|
||||||
var proposedActualMax = [_delegate splitView:self constrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
|
|
||||||
if (_IS_NUMERIC(proposedActualMax))
|
|
||||||
actualMax = proposedActualMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
var viewA = _subviews[dividerIndex],
|
var viewA = _subviews[dividerIndex],
|
||||||
viewB = _subviews[dividerIndex + 1],
|
|
||||||
realPosition = MAX(MIN(position, actualMax), actualMin);
|
realPosition = MAX(MIN(position, actualMax), actualMin);
|
||||||
|
|
||||||
// Is this position past the halfway point to collapse?
|
if (position < proposedMin + (actualMin - proposedMin) / 2)
|
||||||
if (position < proposedMin + (actualMin - proposedMin) / 2)
|
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
|
if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
|
||||||
if ([_delegate splitView:self canCollapseSubview:viewA])
|
if ([_delegate splitView:self canCollapseSubview:viewA])
|
||||||
realPosition = proposedMin;
|
realPosition = proposedMin;
|
||||||
// We can also collapse to the right.
|
|
||||||
if (position > proposedMax - (proposedMax - actualMax) / 2)
|
|
||||||
if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
|
|
||||||
if ([_delegate splitView:self canCollapseSubview:viewB])
|
|
||||||
realPosition = proposedMax;
|
|
||||||
|
|
||||||
return realPosition;
|
return realPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the position of a divider at a given index.
|
|
||||||
@param position - The float value of the position to place the divider.
|
|
||||||
@param dividerIndex - The index of the divider to position.
|
|
||||||
*/
|
|
||||||
- (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
|
- (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
|
||||||
{
|
{
|
||||||
SPLIT_VIEW_SUPPRESS_RESIZE_NOTIFICATIONS(YES);
|
|
||||||
[self _adjustSubviewsWithCalculatedSize];
|
[self _adjustSubviewsWithCalculatedSize];
|
||||||
|
|
||||||
var realPosition = [self _realPositionForPosition:position ofDividerAtIndex:dividerIndex];
|
var realPosition = [self _realPositionForPosition:position ofDividerAtIndex:dividerIndex];
|
||||||
@@ -709,59 +544,32 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
var viewA = _subviews[dividerIndex],
|
var viewA = _subviews[dividerIndex],
|
||||||
frameA = [viewA frame],
|
frameA = [viewA frame],
|
||||||
viewB = _subviews[dividerIndex + 1],
|
viewB = _subviews[dividerIndex + 1],
|
||||||
frameB = [viewB frame],
|
frameB = [viewB frame];
|
||||||
_preCollapsePosition = 0;
|
|
||||||
|
_preCollapsePosition = 0;
|
||||||
|
|
||||||
var preSize = frameA.size[_sizeComponent];
|
var preSize = frameA.size[_sizeComponent];
|
||||||
frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
|
frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
|
||||||
if (preSize !== 0 && frameA.size[_sizeComponent] === 0)
|
if (preSize !== 0 && frameA.size[_sizeComponent] === 0)
|
||||||
_preCollapsePosition = preSize;
|
_preCollapsePosition = preSize;
|
||||||
if (preSize !== frameA.size[_sizeComponent])
|
[_subviews[dividerIndex] setFrame:frameA];
|
||||||
{
|
|
||||||
SPLIT_VIEW_MAYBE_POST_WILL_RESIZE();
|
|
||||||
[_subviews[dividerIndex] setFrame:frameA];
|
|
||||||
SPLIT_VIEW_MAYBE_POST_DID_RESIZE();
|
|
||||||
}
|
|
||||||
|
|
||||||
preSize = frameB.size[_sizeComponent];
|
preSize = frameB.size[_sizeComponent];
|
||||||
var preOrigin = frameB.origin[_originComponent];
|
|
||||||
frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
|
frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
|
||||||
if (preSize !== 0 && frameB.size[_sizeComponent] === 0)
|
if (preSize !== 0 && frameB.size[_sizeComponent] === 0)
|
||||||
_preCollapsePosition = frameB.origin[_originComponent];
|
_preCollapsePosition = preSize;
|
||||||
frameB.origin[_originComponent] = realPosition + [self dividerThickness];
|
frameB.origin[_originComponent] = realPosition + [self dividerThickness];
|
||||||
if (preSize !== frameB.size[_sizeComponent] || preOrigin !== frameB.origin[_originComponent])
|
[_subviews[dividerIndex + 1] setFrame:frameB];
|
||||||
{
|
|
||||||
SPLIT_VIEW_MAYBE_POST_WILL_RESIZE();
|
|
||||||
[_subviews[dividerIndex + 1] setFrame:frameB];
|
|
||||||
SPLIT_VIEW_MAYBE_POST_DID_RESIZE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_preCollapsePosition)
|
|
||||||
[_preCollapsePositions setObject:_preCollapsePosition forKey:"" + dividerIndex];
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
|
|
||||||
if (SPLIT_VIEW_DID_SUPPRESS_RESIZE_NOTIFICATION())
|
|
||||||
[self _postNotificationDidResize];
|
|
||||||
SPLIT_VIEW_SUPPRESS_RESIZE_NOTIFICATIONS(NO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFrameSize:(CGSize)aSize
|
- (void)setFrameSize:(CGSize)aSize
|
||||||
{
|
{
|
||||||
if (_needsRestoreFromAutosave)
|
[self _adjustSubviewsWithCalculatedSize];
|
||||||
_shouldAutosave = NO;
|
|
||||||
else
|
|
||||||
[self _adjustSubviewsWithCalculatedSize];
|
|
||||||
|
|
||||||
[super setFrameSize:aSize];
|
[super setFrameSize:aSize];
|
||||||
|
|
||||||
if (_needsRestoreFromAutosave)
|
|
||||||
{
|
|
||||||
_needsRestoreFromAutosave = NO;
|
|
||||||
[self _restoreFromAutosave];
|
|
||||||
_shouldAutosave = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,7 +581,6 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPLIT_VIEW_MAYBE_POST_WILL_RESIZE();
|
|
||||||
[self _postNotificationWillResize];
|
[self _postNotificationWillResize];
|
||||||
|
|
||||||
var index = 0,
|
var index = 0,
|
||||||
@@ -814,90 +621,26 @@ var CPSplitViewHorizontalImage = nil,
|
|||||||
viewFrame = CGRectMakeCopy(bounds),
|
viewFrame = CGRectMakeCopy(bounds),
|
||||||
isSizable = isVertical ? [view autoresizingMask] & CPViewWidthSizable : [view autoresizingMask] & CPViewHeightSizable;
|
isSizable = isVertical ? [view autoresizingMask] & CPViewWidthSizable : [view autoresizingMask] & CPViewHeightSizable;
|
||||||
|
|
||||||
if (index + 1 === count)
|
if (index + 1 == count)
|
||||||
viewFrame.size[_sizeComponent] = bounds.size[_sizeComponent] - viewFrame.origin[_originComponent];
|
viewFrame.size[_sizeComponent] = bounds.size[_sizeComponent] - viewFrame.origin[_originComponent];
|
||||||
|
else if (totalSizableSpace && isSizable && lastSizableIndex === index)
|
||||||
else if (totalSizableSpace && isSizable && lastSizableIndex === index)
|
viewFrame.size[_sizeComponent] = MAX(0, ROUND([view frame].size[_sizeComponent] + remainingFlexibleSpace))
|
||||||
viewFrame.size[_sizeComponent] = MAX(0, ROUND([view frame].size[_sizeComponent] + remainingFlexibleSpace))
|
else if (isSizable || !totalSizableSpace)
|
||||||
|
{
|
||||||
else if (isSizable || !totalSizableSpace)
|
viewFrame.size[_sizeComponent] = MAX(0, ROUND(ratio * [view frame].size[_sizeComponent]));
|
||||||
{
|
remainingFlexibleSpace -= (viewFrame.size[_sizeComponent] - [view frame].size[_sizeComponent]);
|
||||||
viewFrame.size[_sizeComponent] = MAX(0, ROUND(ratio * [view frame].size[_sizeComponent]));
|
}
|
||||||
remainingFlexibleSpace -= (viewFrame.size[_sizeComponent] - [view frame].size[_sizeComponent]);
|
else if (totalSizableSpace && !isSizable)
|
||||||
}
|
viewFrame.size[_sizeComponent] = [view frame].size[_sizeComponent];
|
||||||
|
|
||||||
else if (totalSizableSpace && !isSizable)
|
|
||||||
viewFrame.size[_sizeComponent] = [view frame].size[_sizeComponent];
|
|
||||||
|
|
||||||
bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness;
|
bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness;
|
||||||
|
|
||||||
[view setFrame:viewFrame];
|
[view setFrame:viewFrame];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SPLIT_VIEW_MAYBE_POST_DID_RESIZE();
|
[self _postNotificationDidResize];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the delegate of the receiver.
|
|
||||||
Possible delegate methods to implement are listed below.
|
|
||||||
|
|
||||||
Notifies the delegate when the subviews have resized.
|
|
||||||
@code
|
|
||||||
- (void)splitViewDidResizeSubviews:(CPNotification)aNotification;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Notifies the delegate when the subviews will be resized.
|
|
||||||
@code
|
|
||||||
- (void)splitViewWillResizeSubviews:(CPNotification)aNotification;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Lets the delegate specify a different rect for which the user can drag the splitView divider.
|
|
||||||
@code
|
|
||||||
- (CGRect)splitView:(CPSplitView)aSplitView effectiveRect:(CGRect)aRect forDrawnRect:(CGRect)aDrawnRect ofDividerAtIndex:(int)aDividerIndex;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Lets the delegate specify an additional rect for which the user can drag the splitview divider.
|
|
||||||
@code
|
|
||||||
- (CGRect)splitView:(CPSplitView)aSplitView additionalEffectiveRectOfDividerAtIndex:(int)indexOfDivider;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Notifies the delegate that the splitview is about to be collapsed. This usually happens when the user
|
|
||||||
Double clicks on the divider. Return YES if the subview can be collapsed, otherwise NO.
|
|
||||||
@code
|
|
||||||
- (BOOL)splitView:(CPSplitView)aSplitView canCollapseSubview:(CPView)aSubview;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Notifies the delegate that the subview at indexOfDivider is about to be collapsed. This usually happens when the user
|
|
||||||
Double clicks on the divider. Return YES if the subview should be collapsed, otherwise NO.
|
|
||||||
@code
|
|
||||||
- (BOOL)splitView:(CPSplitView)aSplitView shouldCollapseSubview:(CPView)aSubview forDoubleClickOnDividerAtIndex:(int)indexOfDivider;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Allows the delegate to constrain the subview beings resized. This method is called continuously as the user resizes the divider.
|
|
||||||
For example if the subview needs to have a width which is a multiple of a certain number you could return that multiple with this method.
|
|
||||||
@code
|
|
||||||
- (float)splitView:(CPSplitView)aSpiltView constrainSplitPosition:(float)proposedPosition ofSubviewAt:(int)subviewIndex;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Allows the delegate to constrain the minimum position of a subview.
|
|
||||||
@code
|
|
||||||
- (float)splitView:(CPSplitView)aSplitView constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)subviewIndex;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Allows the delegate to constrain the maximum position of a subview.
|
|
||||||
@code
|
|
||||||
- (float)splitView:(CPSplitView)aSplitView constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)subviewIndex;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Allows the splitview to specify a custom resizing behavior. This is called when the splitview is resized.
|
|
||||||
The sum of the views and the sum of the dividers should be equal to the size of the splitview.
|
|
||||||
@code
|
|
||||||
- (void)splitView:(CPSplitView)aSplitView resizeSubviewsWithOldSize:(CGSize)oldSize;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
@param delegate - The delegate of the splitview.
|
|
||||||
*/
|
|
||||||
- (void)setDelegate:(id)delegate
|
- (void)setDelegate:(id)delegate
|
||||||
{
|
{
|
||||||
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
||||||
@@ -930,11 +673,7 @@ The sum of the views and the sum of the dividers should be equal to the size of
|
|||||||
|
|
||||||
This method will automatically configure the hasResizeControl and resizeControlIsLeftAligned
|
This method will automatically configure the hasResizeControl and resizeControlIsLeftAligned
|
||||||
parameters of the button bar, and will override any currently set values.
|
parameters of the button bar, and will override any currently set values.
|
||||||
|
|
||||||
@param CPButtonBar - The supplied button bar.
|
|
||||||
@param unsigned int - The divider index the button bar will be assigned to.
|
|
||||||
*/
|
*/
|
||||||
// FIXME Should be renamed to setButtonBar:ofDividerAtIndex:.
|
|
||||||
- (void)setButtonBar:(CPButtonBar)aButtonBar forDividerAtIndex:(unsigned)dividerIndex
|
- (void)setButtonBar:(CPButtonBar)aButtonBar forDividerAtIndex:(unsigned)dividerIndex
|
||||||
{
|
{
|
||||||
if (!aButtonBar)
|
if (!aButtonBar)
|
||||||
@@ -971,121 +710,15 @@ The sum of the views and the sum of the dividers should be equal to the size of
|
|||||||
|
|
||||||
- (void)_postNotificationDidResize
|
- (void)_postNotificationDidResize
|
||||||
{
|
{
|
||||||
[self _autosave];
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification object:self];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification object:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Set the name under which the split view divider positions is automatically saved to CPUserDefaults.
|
|
||||||
|
|
||||||
@param autosaveName the name to save under or nil to not save
|
|
||||||
*/
|
|
||||||
- (void)setAutosaveName:(CPString)autosaveName
|
|
||||||
{
|
|
||||||
if (_autosaveName == autosaveName)
|
|
||||||
return;
|
|
||||||
_autosaveName = autosaveName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Get the name under which the split view divider position is automatically saved to CPUserDefaults.
|
|
||||||
|
|
||||||
@return the name to save under or nil if no auto save is active
|
|
||||||
*/
|
|
||||||
- (CPString)autosaveName
|
|
||||||
{
|
|
||||||
return _autosaveName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_autosave
|
|
||||||
{
|
|
||||||
if (!_shouldAutosave)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var userDefaults = [CPUserDefaults standardUserDefaults],
|
|
||||||
autosaveName = [self _framesKeyForAutosaveName:[self autosaveName]],
|
|
||||||
autosavePrecollapseName = [self _precollapseKeyForAutosaveName:[self autosaveName]],
|
|
||||||
count = [_subviews count],
|
|
||||||
positions = [CPMutableArray new],
|
|
||||||
preCollapseArray = [CPMutableArray new];
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var frame = [_subviews[i] frame];
|
|
||||||
[positions addObject:CPStringFromRect(frame)];
|
|
||||||
[preCollapseArray addObject:[_preCollapsePositions objectForKey:"" + i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[userDefaults setObject:positions forKey:autosaveName];
|
|
||||||
[userDefaults setObject:preCollapseArray forKey:autosavePrecollapseName];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (void)_restoreFromAutosave
|
|
||||||
{
|
|
||||||
if (!_autosaveName)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var autosaveName = [self _framesKeyForAutosaveName:[self autosaveName]],
|
|
||||||
autosavePrecollapseName = [self _precollapseKeyForAutosaveName:[self autosaveName]],
|
|
||||||
userDefaults = [CPUserDefaults standardUserDefaults],
|
|
||||||
frames = [userDefaults objectForKey:autosaveName],
|
|
||||||
preCollapseArray = [userDefaults objectForKey:autosavePrecollapseName];
|
|
||||||
|
|
||||||
if (frames)
|
|
||||||
{
|
|
||||||
var dividerThickness = [self dividerThickness],
|
|
||||||
position = 0;
|
|
||||||
|
|
||||||
_shouldAutosave = NO;
|
|
||||||
for (var i = 0, count = [frames count] - 1; i < count; i++)
|
|
||||||
{
|
|
||||||
var frame = CPRectFromString(frames[i]);
|
|
||||||
position += frame.size[_sizeComponent];
|
|
||||||
|
|
||||||
[self setPosition:position ofDividerAtIndex:i];
|
|
||||||
|
|
||||||
position += dividerThickness;
|
|
||||||
}
|
|
||||||
_shouldAutosave = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preCollapseArray)
|
|
||||||
{
|
|
||||||
_preCollapsePositions = [CPMutableDictionary new];
|
|
||||||
for (var i = 0, count = [preCollapseArray count]; i < count; i++)
|
|
||||||
[_preCollapsePositions setObject:preCollapseArray[i] forKey:i + ""];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (CPString)_framesKeyForAutosaveName:(CPString)theAutosaveName
|
|
||||||
{
|
|
||||||
return @"CPSplitView Subview Frames " + theAutosaveName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (CPString)_precollapseKeyForAutosaveName:(CPString)theAutosaveName
|
|
||||||
{
|
|
||||||
return @"CPSplitView Subview Precollapse Positions " + theAutosaveName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
||||||
CPSplitViewIsVerticalKey = "CPSplitViewIsVerticalKey",
|
CPSplitViewIsVerticalKey = "CPSplitViewIsVerticalKey",
|
||||||
CPSplitViewIsPaneSplitterKey = "CPSplitViewIsPaneSplitterKey",
|
CPSplitViewIsPaneSplitterKey = "CPSplitViewIsPaneSplitterKey",
|
||||||
CPSplitViewButtonBarsKey = "CPSplitViewButtonBarsKey",
|
CPSplitViewButtonBarsKey = "CPSplitViewButtonBarsKey";
|
||||||
CPSplitViewAutosaveNameKey = "CPSplitViewAutosaveNameKey";
|
|
||||||
|
|
||||||
@implementation CPSplitView (CPCoding)
|
@implementation CPSplitView (CPCoding)
|
||||||
|
|
||||||
@@ -1099,27 +732,16 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
|||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_suppressResizeNotificationsMask = 0;
|
|
||||||
_preCollapsePositions = [CPMutableDictionary new];
|
|
||||||
|
|
||||||
_currentDivider = CPNotFound;
|
_currentDivider = CPNotFound;
|
||||||
_shouldAutosave = YES;
|
|
||||||
|
|
||||||
_DOMDividerElements = [];
|
_DOMDividerElements = [];
|
||||||
|
|
||||||
_buttonBars = [aCoder decodeObjectForKey:CPSplitViewButtonBarsKey] || [];
|
_buttonBars = [aCoder decodeObjectForKey:CPSplitViewButtonBarsKey] || [];
|
||||||
|
|
||||||
[self setDelegate:[aCoder decodeObjectForKey:CPSplitViewDelegateKey]];
|
_delegate = [aCoder decodeObjectForKey:CPSplitViewDelegateKey];
|
||||||
|
|
||||||
_isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
|
_isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
|
||||||
[self _setVertical:[aCoder decodeBoolForKey:CPSplitViewIsVerticalKey]];
|
[self _setVertical:[aCoder decodeBoolForKey:CPSplitViewIsVerticalKey]];
|
||||||
|
|
||||||
[self setAutosaveName:[aCoder decodeObjectForKey:CPSplitViewAutosaveNameKey]];
|
|
||||||
|
|
||||||
// We have to wait until we know our frame size before restoring, or the frame resize later will throw
|
|
||||||
// away the restored size.
|
|
||||||
if (_autosaveName)
|
|
||||||
_needsRestoreFromAutosave = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -1140,8 +762,6 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
|||||||
|
|
||||||
[aCoder encodeBool:_isVertical forKey:CPSplitViewIsVerticalKey];
|
[aCoder encodeBool:_isVertical forKey:CPSplitViewIsVerticalKey];
|
||||||
[aCoder encodeBool:_isPaneSplitter forKey:CPSplitViewIsPaneSplitterKey];
|
[aCoder encodeBool:_isPaneSplitter forKey:CPSplitViewIsPaneSplitterKey];
|
||||||
|
|
||||||
[aCoder encodeObject:_autosaveName forKey:CPSplitViewAutosaveNameKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,265 +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 <AppKit/CPControl.j>
|
|
||||||
@import <AppKit/CPButton.j>
|
|
||||||
@import <AppKit/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);
|
|
||||||
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:_CGRectMake(0, 0, 19, 25)];
|
|
||||||
[stepper setDoubleValue:aValue];
|
|
||||||
[stepper setMinValue:aMinValue];
|
|
||||||
[stepper setMaxValue:aMaxValue];
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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;
|
|
||||||
|
|
||||||
[self setDoubleValue:0.0];
|
|
||||||
|
|
||||||
_buttonUp = [[CPButton alloc] initWithFrame:_CGRectMakeZero()];
|
|
||||||
[_buttonUp setContinuous:YES];
|
|
||||||
[_buttonUp setTarget:self];
|
|
||||||
[_buttonUp setAction:@selector(_buttonDidClick:)];
|
|
||||||
[_buttonUp setAutoresizingMask:CPViewNotSizable];
|
|
||||||
[self addSubview:_buttonUp];
|
|
||||||
|
|
||||||
_buttonDown = [[CPButton alloc] initWithFrame:_CGRectMakeZero()];
|
|
||||||
[_buttonDown setContinuous:YES];
|
|
||||||
[_buttonDown setTarget:self];
|
|
||||||
[_buttonDown setAction:@selector(_buttonDidClick:)];
|
|
||||||
[_buttonDown setAutoresizingMask:CPViewNotSizable];
|
|
||||||
[self addSubview:_buttonDown];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
#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 = Math.max(minSize.width, frame.size.width);
|
|
||||||
frame.size.height = Math.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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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
|
|
||||||
{
|
|
||||||
[_buttonUp setContinuous:shouldAutoRepeat];
|
|
||||||
[_buttonDown 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)];
|
|
||||||
|
|
||||||
if (_target && _action && [_target respondsToSelector:_action])
|
|
||||||
[self sendAction:_action to:_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 [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null], _CGSizeMakeZero(), _CGSizeMakeZero()]
|
|
||||||
forKeys:[@"bezel-color-up-button", @"bezel-color-down-button", @"up-button-size", @"down-button-size"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPStepper (CPCodingCompliance)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if (self = [super initWithCoder:aCoder])
|
|
||||||
{
|
|
||||||
_maxValue = [aCoder decodeObjectForKey:@"_maxValue"];
|
|
||||||
_minValue = [aCoder decodeObjectForKey:@"_minValue"];
|
|
||||||
_increment = [aCoder decodeObjectForKey:@"_increment"];
|
|
||||||
_buttonUp = [aCoder decodeObjectForKey:@"_buttonUp"];
|
|
||||||
_buttonDown = [aCoder decodeObjectForKey:@"_buttonDown"];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_maxValue forKey:@"_maxValue"];
|
|
||||||
[aCoder encodeObject:_minValue forKey:@"_minValue"];
|
|
||||||
[aCoder encodeObject:_increment forKey:@"_increment"];
|
|
||||||
[aCoder encodeObject:_buttonUp forKey:@"_buttonUp"];
|
|
||||||
[aCoder encodeObject:_buttonDown forKey:@"_buttonDown"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
+132
-183
@@ -3,75 +3,83 @@
|
|||||||
@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];
|
||||||
_selectedIndex = CPNotFound;
|
|
||||||
|
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
|
|
||||||
{
|
|
||||||
_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]];
|
|
||||||
|
|
||||||
[_box setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
||||||
[_tabs setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
|
|
||||||
|
|
||||||
[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.
|
||||||
@@ -79,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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -106,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;
|
||||||
@@ -152,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
|
||||||
@@ -184,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];
|
||||||
@@ -196,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];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -208,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;
|
||||||
|
|
||||||
@@ -226,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.
|
||||||
@@ -252,56 +250,32 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
|||||||
*/
|
*/
|
||||||
- (void)selectTabViewItemAtIndex:(unsigned)anIndex
|
- (void)selectTabViewItemAtIndex:(unsigned)anIndex
|
||||||
{
|
{
|
||||||
var aTabViewItem = [_items objectAtIndex:anIndex];
|
var aTabViewItem = [items objectAtIndex:anIndex];
|
||||||
|
|
||||||
if (anIndex === _selectedIndex)
|
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;
|
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];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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
|
||||||
{
|
{
|
||||||
return [_items objectAtIndex:_selectedIndex];
|
return [items objectAtIndex:selectedIndex];
|
||||||
}
|
|
||||||
|
|
||||||
// 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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -311,60 +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)
|
||||||
{
|
&& (aTabViewType !== CPNoTabsBezelBorder && aTabViewType !== CPNoTabsBezelBorder && aTabViewType !== CPNoTabsNoBorder))
|
||||||
[_box setFrame:[self bounds]];
|
[self addSubview:tabs];
|
||||||
[_tabs removeFromSuperview];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var aFrame = [self frame],
|
|
||||||
segmentedHeight = CGRectGetHeight([_tabs frame]),
|
|
||||||
origin = _type === CPTopTabsBezelBorder ? segmentedHeight / 2 : 0;
|
|
||||||
|
|
||||||
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
|
type = aTabViewType;
|
||||||
CGRectGetHeight(aFrame) - segmentedHeight / 2)];
|
|
||||||
|
|
||||||
[self addSubview:_tabs];
|
switch (type)
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -373,77 +336,71 @@ 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)
|
if (segmentIndex != CPNotFound)
|
||||||
{
|
{
|
||||||
[self selectTabViewItemAtIndex:segmentIndex];
|
[self selectTabViewItemAtIndex:segmentIndex];
|
||||||
[_tabs trackSegment:anEvent];
|
[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;
|
||||||
|
|
||||||
[_box setContentView:[[_items objectAtIndex:_selectedIndex] view]];
|
[box setContentView:[[items objectAtIndex:selectedIndex] 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];
|
||||||
}
|
}
|
||||||
@@ -454,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)
|
||||||
@@ -463,24 +419,18 @@ 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];
|
|
||||||
|
|
||||||
[self _updateItems];
|
|
||||||
[self _repositionTabs];
|
|
||||||
|
|
||||||
var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
||||||
|
|
||||||
if (selected)
|
if (selected)
|
||||||
[self selectTabViewItem:selected];
|
[self selectTabViewItem:selected];
|
||||||
|
|
||||||
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
||||||
|
|
||||||
[self setTabViewType:[aCoder decodeIntForKey:CPTabViewTypeKey]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -490,13 +440,12 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
|||||||
{
|
{
|
||||||
[super encodeWithCoder:aCoder];
|
[super encodeWithCoder:aCoder];
|
||||||
|
|
||||||
[aCoder encodeObject:_items forKey:CPTabViewItemsKey];
|
[aCoder encodeObject:items forKey:CPTabViewItemsKey];;
|
||||||
[aCoder encodeObject:[self selectedTabViewItem] forKey:CPTabViewSelectedItemKey];
|
[aCoder encodeObject:[self selectedTabViewItem] forKey:CPTabViewSelectedItemKey];
|
||||||
|
|
||||||
[aCoder encodeInt:_type forKey:CPTabViewTypeKey];
|
[aCoder encodeInt:type forKey:CPTabViewTypeKey];
|
||||||
[aCoder encodeObject:_font forKey:CPTabViewFontKey];
|
|
||||||
|
|
||||||
[aCoder encodeConditionalObject:_delegate forKey:CPTabViewDelegateKey];
|
[aCoder encodeConditionalObject:delegate forKey:CPTabViewDelegateKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -145,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
|
||||||
{
|
{
|
||||||
@@ -154,7 +154,7 @@ CPPressedTab = 2;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the tab's auxiliary view
|
Returns the tab's auxillary view
|
||||||
*/
|
*/
|
||||||
- (CPView)auxiliaryView
|
- (CPView)auxiliaryView
|
||||||
{
|
{
|
||||||
|
|||||||
+34
-302
@@ -33,17 +33,6 @@ 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;
|
||||||
@@ -65,18 +54,11 @@ 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];
|
||||||
@@ -102,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;
|
||||||
@@ -186,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;
|
||||||
@@ -214,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;
|
||||||
@@ -242,42 +177,21 @@ 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]);
|
||||||
@@ -291,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)
|
||||||
@@ -314,80 +219,26 @@ 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
|
||||||
{
|
{
|
||||||
@@ -409,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.
|
||||||
*/
|
*/
|
||||||
@@ -418,9 +269,6 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
return [self dataView];
|
return [self dataView];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (id)_newDataViewForRow:(int)aRowIndex
|
- (id)_newDataViewForRow:(int)aRowIndex
|
||||||
{
|
{
|
||||||
var dataView = [self dataViewForRow:aRowIndex],
|
var dataView = [self dataViewForRow:aRowIndex],
|
||||||
@@ -428,7 +276,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
|
|
||||||
var x = [self tableView]._cachedDataViews[dataViewUID];
|
var x = [self tableView]._cachedDataViews[dataViewUID];
|
||||||
if (x && x.length)
|
if (x && x.length)
|
||||||
return x.pop();
|
return x.pop();
|
||||||
|
|
||||||
// if we haven't cached an archive of the data view, do it now
|
// if we haven't cached an archive of the data view, do it now
|
||||||
if (!_dataViewData[dataViewUID])
|
if (!_dataViewData[dataViewUID])
|
||||||
@@ -446,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
|
||||||
@@ -454,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
|
||||||
@@ -464,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
|
||||||
@@ -472,35 +320,28 @@ 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
|
||||||
{
|
{
|
||||||
return _sortDescriptorPrototype;
|
return _sortDescriptorPrototype;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
//Setting Column Visibility
|
||||||
If NO the tablecolumn will no longer be visible in the tableview
|
|
||||||
If YES the tablecolumn will be visible in the tableview.
|
|
||||||
*/
|
|
||||||
- (void)setHidden:(BOOL)shouldBeHidden
|
- (void)setHidden:(BOOL)shouldBeHidden
|
||||||
{
|
{
|
||||||
shouldBeHidden = !!shouldBeHidden
|
shouldBeHidden = !!shouldBeHidden
|
||||||
@@ -513,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;
|
||||||
@@ -523,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.
|
||||||
*/
|
*/
|
||||||
@@ -532,21 +370,13 @@ 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]
|
||||||
@@ -555,69 +385,19 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
|
|
||||||
@end
|
@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];
|
|
||||||
}
|
|
||||||
|
|
||||||
@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++)
|
||||||
@@ -657,42 +437,9 @@ 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]);
|
||||||
@ignore
|
[aDataView setValue:value forKey:bindingPath];
|
||||||
*/
|
|
||||||
- (void)_reverseSetDataView:(CPView)aDataView forRow:(unsigned)aRow
|
|
||||||
{
|
|
||||||
var bindingsDictionary = [CPBinder allBindingsForObject:self],
|
|
||||||
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],
|
|
||||||
dotIndex = keyPath.lastIndexOf(".");
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -701,6 +448,11 @@ CPTableColumnUserResizingMask = 1 << 1;
|
|||||||
// return nil;
|
// return nil;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
- (void)setValue:(CPArray)content
|
||||||
|
{
|
||||||
|
[[self tableView] reloadData];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||||
@@ -716,9 +468,6 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
|||||||
|
|
||||||
@implementation CPTableColumn (CPCoding)
|
@implementation CPTableColumn (CPCoding)
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
- (id)initWithCoder:(CPCoder)aCoder
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
@@ -746,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];
|
||||||
@@ -770,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
|
||||||
|
|||||||
+25
-32
@@ -56,7 +56,7 @@
|
|||||||
{
|
{
|
||||||
_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];
|
||||||
@@ -102,20 +102,20 @@
|
|||||||
|
|
||||||
- (void)setFont:(CPFont)aFont
|
- (void)setFont:(CPFont)aFont
|
||||||
{
|
{
|
||||||
[self setValue:aFont forThemeAttribute:"text-font"];
|
[_textField setFont:aFont];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (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
|
||||||
@@ -212,7 +212,7 @@ 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);
|
||||||
@@ -592,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];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,7 +634,7 @@ 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],
|
||||||
@@ -642,9 +642,10 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
|||||||
|
|
||||||
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)));
|
CGContextAddLineToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMaxY(columnToStroke)));
|
||||||
}
|
}
|
||||||
|
|
||||||
CGContextClosePath(context);
|
CGContextClosePath(context);
|
||||||
CGContextStrokePath(context);
|
CGContextStrokePath(context);
|
||||||
|
|
||||||
@@ -668,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;
|
||||||
|
|||||||
+526
-1623
File diff suppressed because it is too large
Load Diff
+104
-350
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* CPTextField.j
|
* CPTextField.j
|
||||||
* AppKit
|
* AppKit
|
||||||
@@ -20,15 +21,13 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "../Foundation/Ref.h"
|
|
||||||
|
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPStringDrawing.j"
|
@import "CPStringDrawing.j"
|
||||||
@import "CPCompatibility.j"
|
@import "CPCompatibility.j"
|
||||||
@import "_CPImageAndTextView.j"
|
@import "_CPImageAndTextView.j"
|
||||||
|
|
||||||
|
|
||||||
CPTextFieldSquareBezel = 0; /*! A textfield bezel with squared corners. */
|
CPTextFieldSquareBezel = 0; /*! A textfield bezel with a squared corners. */
|
||||||
CPTextFieldRoundedBezel = 1; /*! A textfield bezel with rounded corners. */
|
CPTextFieldRoundedBezel = 1; /*! A textfield bezel with rounded corners. */
|
||||||
|
|
||||||
CPTextFieldDidFocusNotification = @"CPTextFieldDidFocusNotification";
|
CPTextFieldDidFocusNotification = @"CPTextFieldDidFocusNotification";
|
||||||
@@ -84,11 +83,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
|
|
||||||
CPColor _textFieldBackgroundColor;
|
CPColor _textFieldBackgroundColor;
|
||||||
|
|
||||||
CPString _placeholderString;
|
id _placeholderString;
|
||||||
CPString _stringValue;
|
id _originalPlaceholderString;
|
||||||
|
BOOL _currentValueIsPlaceholder;
|
||||||
|
|
||||||
id _delegate;
|
id _delegate;
|
||||||
|
|
||||||
|
CPString _textDidChangeValue;
|
||||||
|
|
||||||
// NS-style Display Properties
|
// NS-style Display Properties
|
||||||
CPTextFieldBezelStyle _bezelStyle;
|
CPTextFieldBezelStyle _bezelStyle;
|
||||||
BOOL _isBordered;
|
BOOL _isBordered;
|
||||||
@@ -158,14 +160,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
return "textfield";
|
return "textfield";
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
|
||||||
{
|
|
||||||
if (theBinding === CPValueBinding)
|
|
||||||
return [_CPTextFieldValueBinder class];
|
|
||||||
|
|
||||||
return [super _binderClassForBinding:theBinding];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)themeAttributes
|
+ (id)themeAttributes
|
||||||
{
|
{
|
||||||
return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(2.0, 2.0, 2.0, 2.0), [CPNull null]]
|
return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(2.0, 2.0, 2.0, 2.0), [CPNull null]]
|
||||||
@@ -481,19 +475,15 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
|
|
||||||
_isEditing = NO;
|
_isEditing = NO;
|
||||||
_stringValue = [self stringValue];
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
|
|
||||||
var element = [self _inputElement],
|
var string = [self stringValue],
|
||||||
font = [self currentValueForThemeAttribute:@"font"];
|
element = [self _inputElement];
|
||||||
|
|
||||||
// generate the font metric
|
element.value = string;
|
||||||
[font _getMetrics];
|
|
||||||
|
|
||||||
element.value = _stringValue;
|
|
||||||
element.style.color = [[self currentValueForThemeAttribute:@"text-color"] cssString];
|
element.style.color = [[self currentValueForThemeAttribute:@"text-color"] cssString];
|
||||||
element.style.font = [font cssString];
|
element.style.font = [[self currentValueForThemeAttribute:@"font"] cssString];
|
||||||
element.style.zIndex = 1000;
|
element.style.zIndex = 1000;
|
||||||
|
|
||||||
switch ([self alignment])
|
switch ([self alignment])
|
||||||
@@ -505,32 +495,12 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
default: element.style.textAlign = "left";
|
default: element.style.textAlign = "left";
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentRect = [self contentRectForBounds:[self bounds]],
|
var contentRect = [self contentRectForBounds:[self bounds]];
|
||||||
verticalAlign = [self currentValueForThemeAttribute:"vertical-alignment"];
|
|
||||||
|
|
||||||
switch (verticalAlign)
|
element.style.top = _CGRectGetMinY(contentRect) + "px";
|
||||||
{
|
|
||||||
case CPTopVerticalTextAlignment:
|
|
||||||
var topPoint = (_CGRectGetMinY(contentRect) + 1) + "px"; // for the same reason we have a -1 for the left, we also have a + 1 here
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPCenterVerticalTextAlignment:
|
|
||||||
var topPoint = (_CGRectGetMidY(contentRect) - (font._lineHeight / 2) + 1) + "px";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPBottomVerticalTextAlignment:
|
|
||||||
var topPoint = (_CGRectGetMaxY(contentRect) - font._lineHeight) + "px";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var topPoint = (_CGRectGetMinY(contentRect) + 1) + "px";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
element.style.top = topPoint;
|
|
||||||
element.style.left = (_CGRectGetMinX(contentRect) - 1) + "px"; // why -1?
|
element.style.left = (_CGRectGetMinX(contentRect) - 1) + "px"; // why -1?
|
||||||
element.style.width = _CGRectGetWidth(contentRect) + "px";
|
element.style.width = _CGRectGetWidth(contentRect) + "px";
|
||||||
element.style.height = font._lineHeight + "px"; // private ivar for the line height of the DOM text at this particular size
|
element.style.height = _CGRectGetHeight(contentRect) + "px";
|
||||||
|
|
||||||
_DOMElement.appendChild(element);
|
_DOMElement.appendChild(element);
|
||||||
|
|
||||||
@@ -548,6 +518,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
CPTextFieldInputOwner = self;
|
CPTextFieldInputOwner = self;
|
||||||
}, 0.0);
|
}, 0.0);
|
||||||
|
|
||||||
|
element.value = [self stringValue];
|
||||||
|
|
||||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||||
|
|
||||||
CPTextFieldInputIsActive = YES;
|
CPTextFieldInputIsActive = YES;
|
||||||
@@ -570,43 +542,19 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
{
|
{
|
||||||
[self unsetThemeState:CPThemeStateEditing];
|
[self unsetThemeState:CPThemeStateEditing];
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
|
|
||||||
var element = [self _inputElement],
|
|
||||||
newValue = element.value,
|
|
||||||
error = @"";
|
|
||||||
|
|
||||||
if (newValue !== _stringValue)
|
|
||||||
{
|
|
||||||
[self _setStringValue:newValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is a formatter, always give it a chance to reject the resignation,
|
|
||||||
// even if the value has not changed.
|
|
||||||
if ([self _valueIsValid:newValue] === NO)
|
|
||||||
{
|
|
||||||
[self setThemeState:CPThemeStateEditing];
|
|
||||||
element.focus();
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Cache the formatted string
|
|
||||||
_stringValue = [self stringValue];
|
|
||||||
|
|
||||||
_willBecomeFirstResponderByClick = NO;
|
|
||||||
|
|
||||||
[self _updatePlaceholderState];
|
[self _updatePlaceholderState];
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
|
|
||||||
CPTextFieldInputResigning = YES;
|
var element = [self _inputElement];
|
||||||
|
|
||||||
if (CPTextFieldInputIsActive)
|
if ([self stringValue] !== element.value)
|
||||||
element.blur();
|
[self _setStringValue:element.value];
|
||||||
|
|
||||||
|
CPTextFieldInputResigning = YES;
|
||||||
|
element.blur();
|
||||||
|
|
||||||
if (!CPTextFieldInputDidBlur)
|
if (!CPTextFieldInputDidBlur)
|
||||||
CPTextFieldBlurFunction();
|
CPTextFieldBlurFunction();
|
||||||
@@ -630,7 +578,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// post CPControlTextDidEndEditingNotification
|
//post CPControlTextDidEndEditingNotification
|
||||||
if (_isEditing)
|
if (_isEditing)
|
||||||
{
|
{
|
||||||
_isEditing = NO;
|
_isEditing = NO;
|
||||||
@@ -645,28 +593,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_valueIsValid:(CPString)aValue
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
|
|
||||||
var error = @"";
|
|
||||||
|
|
||||||
if ([self _setStringValue:aValue isNewValue:NO errorDescription:AT_REF(error)] === NO)
|
|
||||||
{
|
|
||||||
var acceptInvalidValue = NO;
|
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(control:didFailToFormatString:errorDescription:)])
|
|
||||||
acceptInvalidValue = [_delegate control:self didFailToFormatString:[self _inputElement] errorDescription:error];
|
|
||||||
|
|
||||||
if (acceptInvalidValue === NO)
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Text fields require panels to become key window, so this returns \c YES.
|
Text fields require panels to become key window, so this returns \c YES.
|
||||||
*/
|
*/
|
||||||
@@ -727,14 +653,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
|
|
||||||
- (void)keyUp:(CPEvent)anEvent
|
- (void)keyUp:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
#if PLATFORM(DOM)
|
var oldValue = [self stringValue];
|
||||||
|
[self _setStringValue:[self _inputElement].value];
|
||||||
|
|
||||||
var newValue = [self _inputElement].value;
|
if (oldValue !== [self stringValue])
|
||||||
|
|
||||||
if (newValue !== _stringValue)
|
|
||||||
{
|
{
|
||||||
[self _setStringValue:newValue];
|
|
||||||
|
|
||||||
if (!_isEditing)
|
if (!_isEditing)
|
||||||
{
|
{
|
||||||
_isEditing = YES;
|
_isEditing = YES;
|
||||||
@@ -744,106 +667,28 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)keyDown:(CPEvent)anEvent
|
- (void)keyDown:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
// CPTextField uses an HTML input element to take the input so we need to
|
if ([anEvent keyCode] === CPReturnKeyCode)
|
||||||
// propagate the dom event so the element is updated. This has to be done
|
|
||||||
// before interpretKeyEvents: though so individual commands have a chance
|
|
||||||
// to override this (escape to clear the text in a search field for example).
|
|
||||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
|
||||||
|
|
||||||
[self interpretKeyEvents:[anEvent]];
|
|
||||||
|
|
||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Invoke the action specified by aSelector on the current responder.
|
|
||||||
|
|
||||||
This is implemented by CPResponder and by default it passes any unrecognized
|
|
||||||
actions on to the next responder but text fields apparently aren't supposed
|
|
||||||
to do that according to this documentation by Apple:
|
|
||||||
|
|
||||||
http://developer.apple.com/mac/library/documentation/cocoa/reference/NSTextInputClient_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/NSTextInputClient/doCommandBySelector:
|
|
||||||
*/
|
|
||||||
- (void)doCommandBySelector:(SEL)aSelector
|
|
||||||
{
|
|
||||||
if ([self respondsToSelector:aSelector])
|
|
||||||
[self performSelector:aSelector];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertNewline:(id)sender
|
|
||||||
{
|
|
||||||
var newValue = [self _inputElement].value;
|
|
||||||
|
|
||||||
if (newValue !== _stringValue)
|
|
||||||
{
|
{
|
||||||
[self _setStringValue:newValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([self _valueIsValid:_stringValue])
|
|
||||||
{
|
|
||||||
// If _isEditing == YES then the target action can also be called via
|
|
||||||
// resignFirstResponder, and it is possible that the target action
|
|
||||||
// itself will change this textfield's responder status, so start by
|
|
||||||
// setting the _isEditing flag to NO to prevent the target action being
|
|
||||||
// called twice (once below and once from resignFirstResponder).
|
|
||||||
if (_isEditing)
|
if (_isEditing)
|
||||||
{
|
{
|
||||||
_isEditing = NO;
|
_isEditing = NO;
|
||||||
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
|
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no target action, or the sendAction call returns
|
[self sendAction:[self action] to:[self target]];
|
||||||
// success.
|
[self selectText:nil];
|
||||||
if (![self action] || [self sendAction:[self action] to:[self target]])
|
|
||||||
{
|
[[[self window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||||
[self selectAll:nil];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||||
|
|
||||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:NO];
|
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertNewlineIgnoringFieldEditor:(id)sender
|
|
||||||
{
|
|
||||||
[self _insertCharacterIgnoringFieldEditor:CPNewlineCharacter];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertTabIgnoringFieldEditor:(id)sender
|
|
||||||
{
|
|
||||||
[self _insertCharacterIgnoringFieldEditor:CPTabCharacter];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_insertCharacterIgnoringFieldEditor:(CPString)aCharacter
|
|
||||||
{
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
|
|
||||||
var oldValue = _stringValue,
|
|
||||||
range = [self selectedRange],
|
|
||||||
element = [self _inputElement];
|
|
||||||
|
|
||||||
element.value = [element.value stringByReplacingCharactersInRange:[self selectedRange] withString:aCharacter];
|
|
||||||
[self _setStringValue:element.value];
|
|
||||||
|
|
||||||
// NOTE: _stringValue is now the current input element value
|
|
||||||
if (oldValue !== _stringValue)
|
|
||||||
{
|
|
||||||
if (!_isEditing)
|
|
||||||
{
|
|
||||||
_isEditing = YES;
|
|
||||||
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)textDidBlur:(CPNotification)note
|
- (void)textDidBlur:(CPNotification)note
|
||||||
@@ -864,18 +709,17 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
[[CPNotificationCenter defaultCenter] postNotification:note];
|
[[CPNotificationCenter defaultCenter] postNotification:note];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)textDidChange:(CPNotification)note
|
- (void)sendAction:(SEL)anAction to:(id)anObject
|
||||||
{
|
{
|
||||||
if ([note object] !== self)
|
// Don't reverse set our empty value
|
||||||
return;
|
if (!_currentValueIsPlaceholder)
|
||||||
|
[self _reverseSetBinding];
|
||||||
|
|
||||||
[self _continuouslyReverseSetBinding];
|
[CPApp sendAction:anAction to:anObject from:self];
|
||||||
|
|
||||||
[super textDidChange:note];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the string in the text field.
|
Returns the string the text field.
|
||||||
*/
|
*/
|
||||||
- (id)objectValue
|
- (id)objectValue
|
||||||
{
|
{
|
||||||
@@ -884,84 +728,23 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
@ignore
|
@ignore
|
||||||
Sets the internal string value without updating the value in the input element.
|
|
||||||
This should only be invoked when the underlying text element's value has changed.
|
|
||||||
*/
|
*/
|
||||||
- (BOOL)_setStringValue:(CPString)aValue
|
- (void)_setStringValue:(id)aValue
|
||||||
{
|
{
|
||||||
return [self _setStringValue:aValue isNewValue:YES errorDescription:nil];
|
[self willChangeValueForKey:@"objectValue"];
|
||||||
}
|
[super setObjectValue:String(aValue)];
|
||||||
|
[self _updatePlaceholderState];
|
||||||
/*
|
[self didChangeValueForKey:@"objectValue"];
|
||||||
@ignore
|
|
||||||
Sets the internal string value without updating the value in the input element.
|
|
||||||
If there is a formatter and formatting fails, returns NO. Otherwise returns YES.
|
|
||||||
*/
|
|
||||||
- (BOOL)_setStringValue:(CPString)aValue isNewValue:(BOOL)isNewValue errorDescription:(CPStringRef)anError
|
|
||||||
{
|
|
||||||
_stringValue = aValue;
|
|
||||||
|
|
||||||
var objectValue = aValue,
|
|
||||||
formatter = [self formatter],
|
|
||||||
result = YES;
|
|
||||||
|
|
||||||
if (formatter)
|
|
||||||
{
|
|
||||||
var object = nil;
|
|
||||||
|
|
||||||
if ([formatter getObjectValue:AT_REF(object) forString:aValue errorDescription:anError])
|
|
||||||
objectValue = object;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
objectValue = undefined; // Mark the value as invalid
|
|
||||||
result = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
isNewValue |= objectValue !== [super objectValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNewValue)
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:@"objectValue"];
|
|
||||||
[super setObjectValue:objectValue];
|
|
||||||
[self _updatePlaceholderState];
|
|
||||||
[self didChangeValueForKey:@"objectValue"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setObjectValue:(id)aValue
|
- (void)setObjectValue:(id)aValue
|
||||||
{
|
{
|
||||||
[super setObjectValue:aValue];
|
[super setObjectValue:aValue];
|
||||||
|
|
||||||
var formatter = [self formatter];
|
|
||||||
|
|
||||||
if (formatter)
|
|
||||||
{
|
|
||||||
// If there is a formatter, make sure the object value can be formatted successfully
|
|
||||||
var formattedString = [self hasThemeState:CPThemeStateEditing] ? [formatter editingStringForObjectValue:aValue] : [formatter stringForObjectValue:aValue];
|
|
||||||
|
|
||||||
if (formattedString === nil)
|
|
||||||
{
|
|
||||||
var value = nil;
|
|
||||||
|
|
||||||
// Formatting failed, get an "empty" object by formatting an empty string.
|
|
||||||
// If that fails, the value is undefined.
|
|
||||||
if ([formatter getObjectValue:AT_REF(value) forString:@"" errorDescription:nil] === NO)
|
|
||||||
value = undefined;
|
|
||||||
|
|
||||||
[super setObjectValue:value];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_stringValue = [self stringValue];
|
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
|
|
||||||
if (CPTextFieldInputOwner === self || [[self window] firstResponder] === self)
|
if (CPTextFieldInputOwner === self || [[self window] firstResponder] === self)
|
||||||
[self _inputElement].value = _stringValue;
|
[self _inputElement].value = aValue;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[self _updatePlaceholderState];
|
[self _updatePlaceholderState];
|
||||||
@@ -969,7 +752,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
|
|
||||||
- (void)_updatePlaceholderState
|
- (void)_updatePlaceholderState
|
||||||
{
|
{
|
||||||
if ((!_stringValue || _stringValue.length === 0) && ![self hasThemeState:CPThemeStateEditing])
|
var string = [self stringValue];
|
||||||
|
|
||||||
|
if ((!string || string.length === 0) && ![self hasThemeState:CPThemeStateEditing])
|
||||||
[self setThemeState:CPTextFieldStatePlaceholder];
|
[self setThemeState:CPTextFieldStatePlaceholder];
|
||||||
else
|
else
|
||||||
[self unsetThemeState:CPTextFieldStatePlaceholder];
|
[self unsetThemeState:CPTextFieldStatePlaceholder];
|
||||||
@@ -1002,6 +787,34 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
return _placeholderString;
|
return _placeholderString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)_setCurrentValueIsPlaceholder:(BOOL)isPlaceholder
|
||||||
|
{
|
||||||
|
// The initial _currentValueIsPlaceholder is null so convert it to NO for comparison purposes.
|
||||||
|
if (!!_currentValueIsPlaceholder === isPlaceholder)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (isPlaceholder)
|
||||||
|
{
|
||||||
|
// Save the original placeholder value so we can restore it later
|
||||||
|
// Only do this if the placeholder is not already overridden because the bindings logic might call this method
|
||||||
|
// several times and we don't want the bindings placeholder to ever become the original placeholder
|
||||||
|
if (!_currentValueIsPlaceholder)
|
||||||
|
_originalPlaceholderString = [self placeholderString];
|
||||||
|
|
||||||
|
// Set the current string value as the current placeholder and clear the string value
|
||||||
|
[self setPlaceholderString:[self stringValue]];
|
||||||
|
[self setStringValue:@""];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Restore the original placeholder, the actual textfield value is already correct
|
||||||
|
// because it was set using setValue:forKey:
|
||||||
|
[self setPlaceholderString:_originalPlaceholderString];
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentValueIsPlaceholder = isPlaceholder;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
For non-bezeled text fields (typically a label), sizeToFit has two behaviors, depending
|
For non-bezeled text fields (typically a label), sizeToFit has two behaviors, depending
|
||||||
on the line break mode of the receiver.
|
on the line break mode of the receiver.
|
||||||
@@ -1022,6 +835,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
However, since you don't know how tall it needs to be if you change the font, sizeToFit will still be
|
However, since you don't know how tall it needs to be if you change the font, sizeToFit will still be
|
||||||
useful for making the textfield an appropriate height.
|
useful for making the textfield an appropriate height.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
- (void)sizeToFit
|
- (void)sizeToFit
|
||||||
{
|
{
|
||||||
[self setFrameSize:[self _minimumFrameSize]];
|
[self setFrameSize:[self _minimumFrameSize]];
|
||||||
@@ -1034,7 +848,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
minSize = [self currentValueForThemeAttribute:@"min-size"],
|
minSize = [self currentValueForThemeAttribute:@"min-size"],
|
||||||
maxSize = [self currentValueForThemeAttribute:@"max-size"],
|
maxSize = [self currentValueForThemeAttribute:@"max-size"],
|
||||||
lineBreakMode = [self lineBreakMode],
|
lineBreakMode = [self lineBreakMode],
|
||||||
text = (_stringValue || @" "),
|
text = ([self stringValue] || @" "),
|
||||||
textSize = _CGSizeMakeCopy(frameSize),
|
textSize = _CGSizeMakeCopy(frameSize),
|
||||||
font = [self currentValueForThemeAttribute:@"font"];
|
font = [self currentValueForThemeAttribute:@"font"];
|
||||||
|
|
||||||
@@ -1071,28 +885,21 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Make the receiver the first responder and select all the text in the field.
|
Select all the text in the CPTextField.
|
||||||
*/
|
*/
|
||||||
- (void)selectText:(id)sender
|
- (void)selectText:(id)sender
|
||||||
{
|
{
|
||||||
// FIXME Should this really make the text field the first responder?
|
#if PLATFORM(DOM)
|
||||||
|
var element = [self _inputElement];
|
||||||
|
|
||||||
if (([self isEditable] || [self isSelectable]))
|
if (([self isEditable] || [self isSelectable]))
|
||||||
{
|
{
|
||||||
#if PLATFORM(DOM)
|
|
||||||
var element = [self _inputElement];
|
|
||||||
|
|
||||||
if ([[self window] firstResponder] === self)
|
if ([[self window] firstResponder] === self)
|
||||||
window.setTimeout(function() { element.select(); }, 0);
|
window.setTimeout(function() { element.select(); }, 0);
|
||||||
else if ([self window] !== nil && [[self window] makeFirstResponder:self])
|
else if ([self window] !== nil && [[self window] makeFirstResponder:self])
|
||||||
window.setTimeout(function() {[self selectText:sender];}, 0);
|
window.setTimeout(function() {[self selectText:sender];}, 0);
|
||||||
#else
|
|
||||||
// Even if we can't actually select the text we need to preserve the first
|
|
||||||
// responder side effect.
|
|
||||||
if ([self window] !== nil && [[self window] firstResponder] !== self)
|
|
||||||
[[self window] makeFirstResponder:self];
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)copy:(id)sender
|
- (void)copy:(id)sender
|
||||||
@@ -1105,7 +912,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var pasteboard = [CPPasteboard generalPasteboard],
|
var pasteboard = [CPPasteboard generalPasteboard],
|
||||||
stringForPasting = [_stringValue substringWithRange:selectedRange];
|
stringValue = [self stringValue],
|
||||||
|
stringForPasting = [stringValue substringWithRange:selectedRange];
|
||||||
|
|
||||||
[pasteboard declareTypes:[CPStringPboardType] owner:nil];
|
[pasteboard declareTypes:[CPStringPboardType] owner:nil];
|
||||||
[pasteboard setString:stringForPasting forType:CPStringPboardType];
|
[pasteboard setString:stringForPasting forType:CPStringPboardType];
|
||||||
@@ -1119,8 +927,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
[self copy:sender];
|
[self copy:sender];
|
||||||
[self deleteBackward:sender];
|
[self deleteBackward:sender];
|
||||||
}
|
}
|
||||||
else
|
|
||||||
[CPTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(keyUp:) userInfo:nil repeats:NO];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)paste:(id)sender
|
- (void)paste:(id)sender
|
||||||
@@ -1135,14 +941,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
[self deleteBackward:sender];
|
[self deleteBackward:sender];
|
||||||
|
|
||||||
var selectedRange = [self selectedRange],
|
var selectedRange = [self selectedRange],
|
||||||
|
stringValue = [self stringValue],
|
||||||
pasteString = [pasteboard stringForType:CPStringPboardType],
|
pasteString = [pasteboard stringForType:CPStringPboardType],
|
||||||
newValue = [_stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString];
|
newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString];
|
||||||
|
|
||||||
[self setStringValue:newValue];
|
[self setStringValue:newValue];
|
||||||
[self setSelectedRange:CPMakeRange(selectedRange.location + pasteString.length, 0)];
|
[self setSelectedRange:CPMakeRange(selectedRange.location + pasteString.length, 0)];
|
||||||
}
|
}
|
||||||
else
|
|
||||||
[CPTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(keyUp:) userInfo:nil repeats:NO];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPRange)selectedRange
|
- (CPRange)selectedRange
|
||||||
@@ -1150,8 +955,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
if ([[self window] firstResponder] !== self)
|
if ([[self window] firstResponder] !== self)
|
||||||
return CPMakeRange(0, 0);
|
return CPMakeRange(0, 0);
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
|
|
||||||
// we wrap this in try catch because firefox will throw an exception in certain instances
|
// we wrap this in try catch because firefox will throw an exception in certain instances
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -1178,9 +981,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
// fall through to the return
|
// fall through to the return
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
return CGMakeRange(0, 0);
|
||||||
|
|
||||||
return CPMakeRange(0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSelectedRange:(CPRange)aRange
|
- (void)setSelectedRange:(CPRange)aRange
|
||||||
@@ -1188,8 +989,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
if (![[self window] firstResponder] === self)
|
if (![[self window] firstResponder] === self)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
|
|
||||||
var inputElement = [self _inputElement];
|
var inputElement = [self _inputElement];
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -1218,8 +1017,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
catch (e)
|
catch (e)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)selectAll:(id)sender
|
- (void)selectAll:(id)sender
|
||||||
@@ -1229,15 +1026,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
|
|
||||||
- (void)deleteBackward:(id)sender
|
- (void)deleteBackward:(id)sender
|
||||||
{
|
{
|
||||||
var selectedRange = [self selectedRange];
|
var selectedRange = [self selectedRange],
|
||||||
|
stringValue = [self stringValue],
|
||||||
if (selectedRange.length < 2)
|
newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:""];
|
||||||
return;
|
|
||||||
|
|
||||||
selectedRange.location += 1;
|
|
||||||
selectedRange.length -= 1;
|
|
||||||
|
|
||||||
var newValue = [_stringValue stringByReplacingCharactersInRange:selectedRange withString:""];
|
|
||||||
|
|
||||||
[self setStringValue:newValue];
|
[self setStringValue:newValue];
|
||||||
[self setSelectedRange:CPMakeRange(selectedRange.location, 0)];
|
[self setSelectedRange:CPMakeRange(selectedRange.location, 0)];
|
||||||
@@ -1390,7 +1181,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
|||||||
string = [self placeholderString];
|
string = [self placeholderString];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string = _stringValue;
|
string = [self stringValue];
|
||||||
|
|
||||||
if ([self isSecure])
|
if ([self isSecure])
|
||||||
string = secureStringForString(string);
|
string = secureStringForString(string);
|
||||||
@@ -1471,6 +1262,18 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
|||||||
[self setAlignment:[aCoder decodeIntForKey:CPTextFieldAlignmentKey]];
|
[self setAlignment:[aCoder decodeIntForKey:CPTextFieldAlignmentKey]];
|
||||||
|
|
||||||
[self setPlaceholderString:[aCoder decodeObjectForKey:CPTextFieldPlaceholderStringKey]];
|
[self setPlaceholderString:[aCoder decodeObjectForKey:CPTextFieldPlaceholderStringKey]];
|
||||||
|
|
||||||
|
// Make sure the frame is big enough
|
||||||
|
var minSize = [self _minimumFrameSize];
|
||||||
|
minSize.width = MAX(CGRectGetWidth([self frame]), minSize.width);
|
||||||
|
minSize.height = MAX(CGRectGetHeight([self frame]), minSize.height);
|
||||||
|
|
||||||
|
// Only size multiline bezeled textfields if it's to small to fit the content
|
||||||
|
if ([self isBezeled])
|
||||||
|
if ([self lineBreakMode] === CPLineBreakByCharWrapping || [self lineBreakMode] == CPLineBreakByWordWrapping)
|
||||||
|
minSize.height = MAX(CGRectGetHeight([self frame]), minSize.height);
|
||||||
|
|
||||||
|
[self setFrameSize:minSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -1499,52 +1302,3 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation _CPTextFieldValueBinder : CPBinder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValueFor:(CPString)theBinding
|
|
||||||
{
|
|
||||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
|
||||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
|
||||||
options = [_info objectForKey:CPOptionsKey],
|
|
||||||
newValue = [destination valueForKeyPath:keyPath],
|
|
||||||
isPlaceholder = CPIsControllerMarker(newValue);
|
|
||||||
|
|
||||||
if (isPlaceholder)
|
|
||||||
{
|
|
||||||
switch (newValue)
|
|
||||||
{
|
|
||||||
case CPMultipleValuesMarker:
|
|
||||||
newValue = [options objectForKey:CPMultipleValuesPlaceholderBindingOption] || @"Multiple Values";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPNoSelectionMarker:
|
|
||||||
newValue = [options objectForKey:CPNoSelectionPlaceholderBindingOption] || @"No Selection";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPNotApplicableMarker:
|
|
||||||
if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
|
||||||
[CPException raise:CPGenericException
|
|
||||||
reason:@"can't transform non applicable key on: "+_source+" value: "+newValue];
|
|
||||||
|
|
||||||
newValue = [options objectForKey:CPNotApplicablePlaceholderBindingOption] || @"Not Applicable";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPNullMarker:
|
|
||||||
newValue = [options objectForKey:CPNullPlaceholderBindingOption] || @"";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[_source setPlaceholderString:newValue];
|
|
||||||
[_source setObjectValue:nil];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newValue = [self transformValue:newValue withOptions:options];
|
|
||||||
[_source setObjectValue:newValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|||||||
@@ -405,7 +405,6 @@ function CPThemeStateName(aState)
|
|||||||
CPThemeStateNames[0] = "normal";
|
CPThemeStateNames[0] = "normal";
|
||||||
CPThemeStateNormal = CPThemeStates["normal"] = 0;
|
CPThemeStateNormal = CPThemeStates["normal"] = 0;
|
||||||
CPThemeStateDisabled = CPThemeState("disabled");
|
CPThemeStateDisabled = CPThemeState("disabled");
|
||||||
CPThemeStateHovered = CPThemeState("hovered");
|
|
||||||
CPThemeStateHighlighted = CPThemeState("highlighted");
|
CPThemeStateHighlighted = CPThemeState("highlighted");
|
||||||
CPThemeStateSelected = CPThemeState("selected");
|
CPThemeStateSelected = CPThemeState("selected");
|
||||||
CPThemeStateTableDataView = CPThemeState("tableDataView");
|
CPThemeStateTableDataView = CPThemeState("tableDataView");
|
||||||
|
|||||||
+71
-196
@@ -27,7 +27,6 @@
|
|||||||
@import "CPButton.j"
|
@import "CPButton.j"
|
||||||
@import "CPScrollView.j"
|
@import "CPScrollView.j"
|
||||||
@import "CPTextField.j"
|
@import "CPTextField.j"
|
||||||
@import "CPTableView.j"
|
|
||||||
@import "CPWindow.j"
|
@import "CPWindow.j"
|
||||||
@import "_CPMenuWindow.j"
|
@import "_CPMenuWindow.j"
|
||||||
|
|
||||||
@@ -80,7 +79,6 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
CPEvent _mouseDownEvent;
|
CPEvent _mouseDownEvent;
|
||||||
|
|
||||||
BOOL _preventResign;
|
BOOL _preventResign;
|
||||||
BOOL _shouldNotifyTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPCharacterSet)defaultTokenizingCharacterSet
|
+ (CPCharacterSet)defaultTokenizingCharacterSet
|
||||||
@@ -97,66 +95,61 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
{
|
{
|
||||||
if (self = [super initWithFrame:frame])
|
if (self = [super initWithFrame:frame])
|
||||||
{
|
{
|
||||||
|
_selectedRange = CPMakeRange(0, 0);
|
||||||
|
|
||||||
|
_tokenScrollView = [[CPScrollView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
[_tokenScrollView setHasHorizontalScroller:NO];
|
||||||
|
[_tokenScrollView setHasVerticalScroller:NO];
|
||||||
|
[_tokenScrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
|
|
||||||
|
var contentView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||||
|
[contentView setAutoresizingMask:CPViewWidthSizable];
|
||||||
|
[_tokenScrollView setDocumentView:contentView];
|
||||||
|
|
||||||
|
[self addSubview:_tokenScrollView];
|
||||||
|
|
||||||
|
_tokenIndex = 0;
|
||||||
|
|
||||||
|
_cachedCompletions = [];
|
||||||
_completionDelay = [CPTokenField defaultCompletionDelay];
|
_completionDelay = [CPTokenField defaultCompletionDelay];
|
||||||
|
|
||||||
_tokenizingCharacterSet = [[self class] defaultTokenizingCharacterSet];
|
_tokenizingCharacterSet = [[self class] defaultTokenizingCharacterSet];
|
||||||
|
|
||||||
|
_autocompleteContainer = [[CPView alloc] initWithFrame:CPRectMake(0.0, 0.0, frame.size.width, 92.0)];
|
||||||
|
[_autocompleteContainer setBackgroundColor:[_CPMenuWindow backgroundColorForBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle]];
|
||||||
|
|
||||||
|
_autocompleteScrollView = [[CPScrollView alloc] initWithFrame:CPRectMake(1.0, 1.0, frame.size.width - 2.0, 90.0)];
|
||||||
|
[_autocompleteScrollView setAutohidesScrollers:YES];
|
||||||
|
[_autocompleteScrollView setHasHorizontalScroller:NO];
|
||||||
|
[_autocompleteContainer addSubview:_autocompleteScrollView];
|
||||||
|
|
||||||
|
_autocompleteView = [[CPTableView alloc] initWithFrame:CPRectMakeZero()];
|
||||||
|
|
||||||
|
var tableColumn = [[CPTableColumn alloc] initWithIdentifier:CPTokenFieldTableColumnIdentifier];
|
||||||
|
[tableColumn setResizingMask:CPTableColumnAutoresizingMask];
|
||||||
|
[_autocompleteView addTableColumn:tableColumn];
|
||||||
|
|
||||||
|
[_autocompleteView setDataSource:self];
|
||||||
|
[_autocompleteView setDelegate:self];
|
||||||
|
[_autocompleteView setAllowsMultipleSelection:NO];
|
||||||
|
[_autocompleteView setHeaderView:nil];
|
||||||
|
[_autocompleteView setCornerView:nil];
|
||||||
|
[_autocompleteView setRowHeight:30.0];
|
||||||
|
[_autocompleteView setGridStyleMask:CPTableViewSolidHorizontalGridLineMask];
|
||||||
|
[_autocompleteView setBackgroundColor:[CPColor clearColor]];
|
||||||
|
[_autocompleteView setGridColor:[CPColor colorWithRed:242.0 / 255.0 green:243.0 / 255.0 blue:245.0 / 255.0 alpha:1.0]];
|
||||||
|
|
||||||
|
[_autocompleteScrollView setDocumentView:_autocompleteView];
|
||||||
|
|
||||||
[self setBezeled:YES];
|
[self setBezeled:YES];
|
||||||
|
|
||||||
[self _init];
|
|
||||||
|
|
||||||
[self setObjectValue:[]];
|
[self setObjectValue:[]];
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_init
|
|
||||||
{
|
|
||||||
_selectedRange = CPMakeRange(0, 0);
|
|
||||||
|
|
||||||
var frame = [self frame];
|
|
||||||
|
|
||||||
_tokenScrollView = [[CPScrollView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[_tokenScrollView setHasHorizontalScroller:NO];
|
|
||||||
[_tokenScrollView setHasVerticalScroller:NO];
|
|
||||||
[_tokenScrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
||||||
|
|
||||||
var contentView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
|
||||||
[contentView setAutoresizingMask:CPViewWidthSizable];
|
|
||||||
[_tokenScrollView setDocumentView:contentView];
|
|
||||||
|
|
||||||
[self addSubview:_tokenScrollView];
|
|
||||||
|
|
||||||
_cachedCompletions = [];
|
|
||||||
|
|
||||||
_autocompleteContainer = [[CPView alloc] initWithFrame:CPRectMake(0.0, 0.0, frame.size.width, 92.0)];
|
|
||||||
[_autocompleteContainer setBackgroundColor:[_CPMenuWindow backgroundColorForBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle]];
|
|
||||||
|
|
||||||
_autocompleteScrollView = [[CPScrollView alloc] initWithFrame:CPRectMake(1.0, 1.0, frame.size.width - 2.0, 90.0)];
|
|
||||||
[_autocompleteScrollView setAutohidesScrollers:YES];
|
|
||||||
[_autocompleteScrollView setHasHorizontalScroller:NO];
|
|
||||||
[_autocompleteContainer addSubview:_autocompleteScrollView];
|
|
||||||
|
|
||||||
_autocompleteView = [[CPTableView alloc] initWithFrame:CPRectMakeZero()];
|
|
||||||
|
|
||||||
var tableColumn = [[CPTableColumn alloc] initWithIdentifier:CPTokenFieldTableColumnIdentifier];
|
|
||||||
[tableColumn setResizingMask:CPTableColumnAutoresizingMask];
|
|
||||||
[_autocompleteView addTableColumn:tableColumn];
|
|
||||||
|
|
||||||
[_autocompleteView setDataSource:self];
|
|
||||||
[_autocompleteView setDelegate:self];
|
|
||||||
[_autocompleteView setAllowsMultipleSelection:NO];
|
|
||||||
[_autocompleteView setHeaderView:nil];
|
|
||||||
[_autocompleteView setCornerView:nil];
|
|
||||||
[_autocompleteView setRowHeight:30.0];
|
|
||||||
[_autocompleteView setGridStyleMask:CPTableViewSolidHorizontalGridLineMask];
|
|
||||||
[_autocompleteView setBackgroundColor:[CPColor clearColor]];
|
|
||||||
[_autocompleteView setGridColor:[CPColor colorWithRed:242.0 / 255.0 green:243.0 / 255.0 blue:245.0 / 255.0 alpha:1.0]];
|
|
||||||
|
|
||||||
[_autocompleteScrollView setDocumentView:_autocompleteView];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===============
|
// ===============
|
||||||
// = CONVENIENCE =
|
// = CONVENIENCE =
|
||||||
// ===============
|
// ===============
|
||||||
@@ -164,7 +157,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
{
|
{
|
||||||
var indexOfSelectedItem = 0;
|
var indexOfSelectedItem = 0;
|
||||||
|
|
||||||
_cachedCompletions = [self tokenField:self completionsForSubstring:[self _inputElement].value indexOfToken:0 indexOfSelectedItem:indexOfSelectedItem];
|
_cachedCompletions = [self tokenField:self completionsForSubstring:[self _inputElement].value indexOfToken:_tokenIndex indexOfSelectedItem:indexOfSelectedItem];
|
||||||
|
|
||||||
[_autocompleteView selectRowIndexes:[CPIndexSet indexSetWithIndex:indexOfSelectedItem] byExtendingSelection:NO];
|
[_autocompleteView selectRowIndexes:[CPIndexSet indexSetWithIndex:indexOfSelectedItem] byExtendingSelection:NO];
|
||||||
[_autocompleteView reloadData];
|
[_autocompleteView reloadData];
|
||||||
@@ -172,20 +165,19 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
|
|
||||||
- (void)_autocompleteWithDOMEvent:(JSObject)DOMEvent
|
- (void)_autocompleteWithDOMEvent:(JSObject)DOMEvent
|
||||||
{
|
{
|
||||||
if (![self _inputElement].value && (!_cachedCompletions || ![self hasThemeState:CPThemeStateAutoCompleting]))
|
if (!_cachedCompletions || ![self hasThemeState:CPThemeStateAutoCompleting])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self _hideCompletions];
|
[self _hideCompletions];
|
||||||
|
|
||||||
var token = _cachedCompletions ? _cachedCompletions[[_autocompleteView selectedRow]] : nil,
|
var token = _cachedCompletions[[_autocompleteView selectedRow]],
|
||||||
shouldRemoveLastObject = token !== @"" && [self _inputElement].value !== @"";
|
shouldRemoveLastObject = token !== @"" && [self _inputElement].value !== @"";
|
||||||
|
|
||||||
if (!token)
|
if (!token)
|
||||||
token = [self _inputElement].value;
|
token = [self _inputElement].value;
|
||||||
|
|
||||||
// Make sure the user typed an actual token to prevent the previous token from being emptied
|
// Make sure the user typed an actual token to prevent the previous token from being emptied
|
||||||
// If the input area is empty, we want to fall back to the normal behavior, resigning first
|
// If the input area is empty, we want to fallback to the normal behaviour, resigning first responder or select the next or previous key view
|
||||||
// responder or selecting the next or previous key view.
|
|
||||||
if (!token || token === @"")
|
if (!token || token === @"")
|
||||||
{
|
{
|
||||||
if (DOMEvent && DOMEvent.keyCode === CPTabKeyCode)
|
if (DOMEvent && DOMEvent.keyCode === CPTabKeyCode)
|
||||||
@@ -200,37 +192,26 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var objectValue = [self objectValue];
|
var objectValue = [self objectValue];
|
||||||
|
|
||||||
// Remove the uncompleted token and add the token string.
|
// Remove the uncompleted token and add the token string.
|
||||||
// Explicitly remove the last object because the array contains strings and removeObject uses isEqual to compare objects
|
// Explicitely remove the last object because the array contains strings and removeObject uses isEqual to compare objects
|
||||||
if (shouldRemoveLastObject)
|
if (shouldRemoveLastObject)
|
||||||
[objectValue removeObjectAtIndex:_selectedRange.location];
|
[objectValue removeObjectAtIndex:_selectedRange.location];
|
||||||
|
|
||||||
// Give the delegate a chance to confirm, replace or add to the list of tokens being added.
|
[objectValue insertObject:token atIndex:_selectedRange.location];
|
||||||
var delegateApprovedObjects = [self tokenField:self shouldAddObjects:[CPArray arrayWithObject:token] atIndex:_selectedRange.location],
|
|
||||||
delegateApprovedObjectsCount = [delegateApprovedObjects count];
|
|
||||||
if (delegateApprovedObjects)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < delegateApprovedObjectsCount; i++)
|
|
||||||
{
|
|
||||||
[objectValue insertObject:[delegateApprovedObjects objectAtIndex:i] atIndex:_selectedRange.location + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put the cursor after the last inserted token.
|
|
||||||
var location = _selectedRange.location;
|
var location = _selectedRange.location;
|
||||||
|
|
||||||
[self setObjectValue:objectValue];
|
[self setObjectValue:objectValue];
|
||||||
|
_selectedRange = CPMakeRange(location + 1, 0);
|
||||||
if (delegateApprovedObjectsCount)
|
|
||||||
location += delegateApprovedObjectsCount;
|
|
||||||
_selectedRange = CPMakeRange(location, 0);
|
|
||||||
|
|
||||||
[self _inputElement].value = @"";
|
[self _inputElement].value = @"";
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
|
|
||||||
[self _controlTextDidChange];
|
var theBinding = [CPKeyValueBinding getBinding:CPValueBinding forObject:self];
|
||||||
|
|
||||||
|
if (theBinding)
|
||||||
|
[theBinding reverseSetValueFor:@"objectValue"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_autocomplete
|
- (void)_autocomplete
|
||||||
@@ -270,13 +251,8 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
var indexOfToken = [[self _tokens] indexOfObject:token],
|
var indexOfToken = [[self _tokens] indexOfObject:token],
|
||||||
objectValue = [self objectValue];
|
objectValue = [self objectValue];
|
||||||
|
|
||||||
// If the selection was to the right of the deleted token, move it to the left. If the deleted token was
|
// If the token was selected, deselect it for selection preservation.
|
||||||
// selected, deselect it.
|
[self _deselectToken:token];
|
||||||
if (indexOfToken < _selectedRange.location)
|
|
||||||
_selectedRange.location--;
|
|
||||||
else
|
|
||||||
[self _deselectToken:token];
|
|
||||||
|
|
||||||
// Preserve selection.
|
// Preserve selection.
|
||||||
var selection = CPCopyRange(_selectedRange);
|
var selection = CPCopyRange(_selectedRange);
|
||||||
[objectValue removeObjectAtIndex:indexOfToken];
|
[objectValue removeObjectAtIndex:indexOfToken];
|
||||||
@@ -289,15 +265,12 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
|
|
||||||
- (void)_controlTextDidChange
|
- (void)_controlTextDidChange
|
||||||
{
|
{
|
||||||
var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
|
var theBinding = [CPKeyValueBinding getBinding:CPValueBinding forObject:self];
|
||||||
theBinding = [binderClass getBinding:CPValueBinding forObject:self];
|
|
||||||
|
|
||||||
if (theBinding)
|
if (theBinding)
|
||||||
[theBinding reverseSetValueFor:@"objectValue"];
|
[theBinding reverseSetValueFor:@"objectValue"];
|
||||||
|
|
||||||
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
||||||
|
|
||||||
_shouldNotifyTarget = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_removeSelectedTokens:(id)sender
|
- (void)_removeSelectedTokens:(id)sender
|
||||||
@@ -316,14 +289,6 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
[self _controlTextDidChange];
|
[self _controlTextDidChange];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_updatePlaceholderState
|
|
||||||
{
|
|
||||||
if (([[self _tokens] count] === 0) && ![self hasThemeState:CPThemeStateEditing])
|
|
||||||
[self setThemeState:CPTextFieldStatePlaceholder];
|
|
||||||
else
|
|
||||||
[self unsetThemeState:CPTextFieldStatePlaceholder];
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============
|
// =============
|
||||||
// = RESPONDER =
|
// = RESPONDER =
|
||||||
// =============
|
// =============
|
||||||
@@ -436,14 +401,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
|
|
||||||
if (_shouldNotifyTarget)
|
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
|
||||||
{
|
|
||||||
_shouldNotifyTarget = NO;
|
|
||||||
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
|
|
||||||
|
|
||||||
if ([self sendsActionOnEndEditing])
|
|
||||||
[self sendAction:[self action] to:[self target]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
@@ -488,7 +446,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
- (CPArray)_tokens
|
- (CPArray)_tokens
|
||||||
{
|
{
|
||||||
// We return super here because objectValue uses this method
|
// We return super here because objectValue uses this method
|
||||||
// If we called self we would loop infinitely
|
// If we called self we would loop infinitly
|
||||||
return [super objectValue];
|
return [super objectValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +465,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
if ([token isKindOfClass:[CPString class]])
|
if ([token isKindOfClass:[CPString class]])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
[objectValue addObject:[token representedObject]];
|
[objectValue addObject:[token stringValue]];
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
@@ -543,14 +501,13 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
for (var i = 0, count = [aValue count]; i < count; i++)
|
for (var i = 0, count = [aValue count]; i < count; i++)
|
||||||
{
|
{
|
||||||
// Do we have this token among the old ones?
|
// Do we have this token among the old ones?
|
||||||
var tokenObject = aValue[i],
|
var tokenValue = aValue[i],
|
||||||
tokenValue = [self tokenField:self displayStringForRepresentedObject:tokenObject],
|
|
||||||
newToken = nil;
|
newToken = nil;
|
||||||
|
|
||||||
for (var j = 0, oldCount = [oldTokens count]; j < oldCount; j++)
|
for (var j = 0, oldCount = [oldTokens count]; j < oldCount; j++)
|
||||||
{
|
{
|
||||||
var oldToken = oldTokens[j];
|
var oldToken = oldTokens[j];
|
||||||
if ([oldToken representedObject] == tokenObject)
|
if ([oldToken stringValue] == tokenValue)
|
||||||
{
|
{
|
||||||
// Yep. Reuse it.
|
// Yep. Reuse it.
|
||||||
[oldTokens removeObjectAtIndex:j];
|
[oldTokens removeObjectAtIndex:j];
|
||||||
@@ -563,7 +520,6 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
{
|
{
|
||||||
newToken = [[_CPTokenFieldToken alloc] init];
|
newToken = [[_CPTokenFieldToken alloc] init];
|
||||||
[newToken setTokenField:self];
|
[newToken setTokenField:self];
|
||||||
[newToken setRepresentedObject:tokenObject];
|
|
||||||
[newToken setStringValue:tokenValue];
|
[newToken setStringValue:tokenValue];
|
||||||
[contentView addSubview:newToken];
|
[contentView addSubview:newToken];
|
||||||
}
|
}
|
||||||
@@ -596,28 +552,20 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
|
||||||
{
|
|
||||||
_shouldNotifyTarget = NO;
|
|
||||||
[super sendAction:anAction to:anObject];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Incredible hack to disable supers implementation
|
// Incredible hack to disable supers implementation
|
||||||
// so it cannot change our object value and break the tokenfield
|
// so it cannot change our object value and break the tokenfield
|
||||||
- (void)_setStringValue:(id)aValue
|
- (void)_setStringValue:(id)aValue
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========
|
// ========
|
||||||
// = VIEW =
|
// = VIEW =
|
||||||
// ========
|
// ========
|
||||||
- (void)viewDidMoveToWindow
|
- (void)viewDidMoveToWindow
|
||||||
{
|
{
|
||||||
[[[self window] contentView] addSubview:_autocompleteContainer];
|
[[[self window] contentView] addSubview:_autocompleteContainer];
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
|
||||||
_autocompleteContainer._DOMElement.style.zIndex = 1000; // Anything else doesn't seem to work
|
_autocompleteContainer._DOMElement.style.zIndex = 1000; // Anything else doesn't seem to work
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeFromSuperview
|
- (void)removeFromSuperview
|
||||||
@@ -668,7 +616,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
|
|
||||||
CPTokenFieldTextDidChangeValue = [CPTokenFieldInputOwner stringValue];
|
CPTokenFieldTextDidChangeValue = [CPTokenFieldInputOwner stringValue];
|
||||||
|
|
||||||
// Update the selectedIndex if necessary
|
// Update the selectedIndex if necesary
|
||||||
var index = [[CPTokenFieldInputOwner autocompleteView] selectedRow];
|
var index = [[CPTokenFieldInputOwner autocompleteView] selectedRow];
|
||||||
|
|
||||||
if (aDOMEvent.keyCode === CPUpArrowKeyCode)
|
if (aDOMEvent.keyCode === CPUpArrowKeyCode)
|
||||||
@@ -700,7 +648,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
aDOMEvent.stopPropagation();
|
aDOMEvent.stopPropagation();
|
||||||
aDOMEvent.cancelBubble = true;
|
aDOMEvent.cancelBubble = true;
|
||||||
|
|
||||||
// Only resign first responder if we weren't auto-completing
|
// Only resign first responder if we weren't autocompleting
|
||||||
if (![CPTokenFieldInputOwner hasThemeState:CPThemeStateAutoCompleting])
|
if (![CPTokenFieldInputOwner hasThemeState:CPThemeStateAutoCompleting])
|
||||||
{
|
{
|
||||||
if (aDOMEvent && aDOMEvent.keyCode === CPReturnKeyCode)
|
if (aDOMEvent && aDOMEvent.keyCode === CPReturnKeyCode)
|
||||||
@@ -1126,7 +1074,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
|
|
||||||
- (void)tableView:(CPTableView)tableView objectValueForTableColumn:(CPTableColumn)tableColumn row:(int)row
|
- (void)tableView:(CPTableView)tableView objectValueForTableColumn:(CPTableColumn)tableColumn row:(int)row
|
||||||
{
|
{
|
||||||
return [self tokenField:self displayStringForRepresentedObject:[_cachedCompletions objectAtIndex:row]];
|
return [_cachedCompletions objectAtIndex:row];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)tableViewSelectionDidChange:(CPNotification)notification
|
- (void)tableViewSelectionDidChange:(CPNotification)notification
|
||||||
@@ -1158,48 +1106,20 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
{
|
{
|
||||||
if ([[self delegate] respondsToSelector:@selector(tokenField:completionsForSubstring:indexOfToken:indexOfSelectedItem:)])
|
if ([[self delegate] respondsToSelector:@selector(tokenField:completionsForSubstring:indexOfToken:indexOfSelectedItem:)])
|
||||||
{
|
{
|
||||||
return [[self delegate] tokenField:tokenField completionsForSubstring:substring indexOfToken:tokenIndex indexOfSelectedItem:selectedIndex];
|
return [[self delegate] tokenField:tokenField completionsForSubstring:substring indexOfToken:_tokenIndex indexOfSelectedItem:selectedIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Allows the delegate to provide a string to be displayed as a proxy for the given represented object.
|
|
||||||
// // If you return nil or do not implement this method, then representedObject is displayed as the string.
|
|
||||||
- (CPString)tokenField:(CPTokenField)tokenField displayStringForRepresentedObject:(id)representedObject
|
|
||||||
{
|
|
||||||
if ([[self delegate] respondsToSelector:@selector(tokenField:displayStringForRepresentedObject:)])
|
|
||||||
{
|
|
||||||
var stringForRepresentedObject = [[self delegate] tokenField:tokenField displayStringForRepresentedObject:representedObject];
|
|
||||||
if (stringForRepresentedObject !== nil)
|
|
||||||
{
|
|
||||||
return stringForRepresentedObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return representedObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// // return an array of represented objects you want to add.
|
// // return an array of represented objects you want to add.
|
||||||
// // If you want to reject the add, return an empty array.
|
// // If you want to reject the add, return an empty array.
|
||||||
// // returning nil will cause an error.
|
// // returning nil will cause an error.
|
||||||
- (CPArray)tokenField:(CPTokenField)tokenField shouldAddObjects:(CPArray)tokens atIndex:(int)index
|
// - (NSArray *)tokenField:(NSTokenField *)tokenField shouldAddObjects:(NSArray *)tokens atIndex:(NSUInteger)index;
|
||||||
{
|
|
||||||
var delegate = [self delegate];
|
|
||||||
if ([delegate respondsToSelector:@selector(tokenField:shouldAddObjects:atIndex:)])
|
|
||||||
{
|
|
||||||
var approvedObjects = [delegate tokenField:tokenField shouldAddObjects:tokens atIndex:index];
|
|
||||||
if (approvedObjects !== nil)
|
|
||||||
return approvedObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// // If you return nil or don't implement these delegate methods, we will assume
|
// // If you return nil or don't implement these delegate methods, we will assume
|
||||||
// // editing string = display string = represented object
|
// // editing string = display string = represented object
|
||||||
|
// - (NSString *)tokenField:(NSTokenField *)tokenField displayStringForRepresentedObject:(id)representedObject;
|
||||||
// - (NSString *)tokenField:(NSTokenField *)tokenField editingStringForRepresentedObject:(id)representedObject;
|
// - (NSString *)tokenField:(NSTokenField *)tokenField editingStringForRepresentedObject:(id)representedObject;
|
||||||
// - (id)tokenField:(NSTokenField *)tokenField representedObjectForEditingString: (NSString *)editingString;
|
// - (id)tokenField:(NSTokenField *)tokenField representedObjectForEditingString: (NSString *)editingString;
|
||||||
//
|
//
|
||||||
@@ -1223,7 +1143,6 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
{
|
{
|
||||||
_CPTokenFieldTokenCloseButton _deleteButton;
|
_CPTokenFieldTokenCloseButton _deleteButton;
|
||||||
CPTokenField _tokenField;
|
CPTokenField _tokenField;
|
||||||
id _representedObject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPString)defaultThemeClass
|
+ (CPString)defaultThemeClass
|
||||||
@@ -1256,16 +1175,6 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
_tokenField = tokenField;
|
_tokenField = tokenField;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)representedObject
|
|
||||||
{
|
|
||||||
return _representedObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setRepresentedObject:(id)representedObject
|
|
||||||
{
|
|
||||||
_representedObject = representedObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGSize)_minimumFrameSize
|
- (CGSize)_minimumFrameSize
|
||||||
{
|
{
|
||||||
var size = CGSizeMakeZero(),
|
var size = CGSizeMakeZero(),
|
||||||
@@ -1339,37 +1248,3 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
var CPTokenFieldTokenizingCharacterSetKey = "CPTokenFieldTokenizingCharacterSetKey",
|
|
||||||
CPTokenFieldCompletionDelayKey = "CPTokenFieldCompletionDelay";
|
|
||||||
|
|
||||||
@implementation CPTokenField (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
_tokenizingCharacterSet = [aCoder decodeObjectForKey:CPTokenFieldTokenizingCharacterSetKey] || [[self class] defaultTokenizingCharacterSet];
|
|
||||||
_completionDelay = [aCoder decodeDoubleForKey:CPTokenFieldCompletionDelayKey] || [[self class] defaultCompletionDelay];
|
|
||||||
|
|
||||||
[self _init];
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeInt:_tokenizingCharacterSet forKey:CPTokenFieldTokenizingCharacterSetKey];
|
|
||||||
[aCoder encodeDouble:_completionDelay forKey:CPTokenFieldCompletionDelayKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|||||||
+7
-24
@@ -50,8 +50,8 @@ CPToolbarDisplayModeIconOnly = 2;
|
|||||||
*/
|
*/
|
||||||
CPToolbarDisplayModeLabelOnly = 3;
|
CPToolbarDisplayModeLabelOnly = 3;
|
||||||
|
|
||||||
var CPToolbarsByIdentifier = nil,
|
var CPToolbarsByIdentifier = nil;
|
||||||
CPToolbarConfigurationsByIdentifier = nil;
|
var CPToolbarConfigurationsByIdentifier = nil;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -464,7 +464,7 @@ var CPToolbarIdentifierKey = @"CPToolbarIdentifierKey",
|
|||||||
// Because we don't know if a delegate will be set later (it is optional
|
// Because we don't know if a delegate will be set later (it is optional
|
||||||
// as of OS X 10.5), we need to call -_reloadToolbarItems here.
|
// as of OS X 10.5), we need to call -_reloadToolbarItems here.
|
||||||
// In order to load any toolbar items that may have been configured in the
|
// In order to load any toolbar items that may have been configured in the
|
||||||
// Cib. Unfortunately this means that if there is a delegate
|
// Cib. Unfortunatelly this means that if there is a delegate
|
||||||
// specified, it will be read later and the resulting call to -setDelegate:
|
// specified, it will be read later and the resulting call to -setDelegate:
|
||||||
// will cause -_reloadToolbarItems] to run again :-(
|
// will cause -_reloadToolbarItems] to run again :-(
|
||||||
// FIXME: Can we make this better?
|
// FIXME: Can we make this better?
|
||||||
@@ -667,7 +667,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
|||||||
|
|
||||||
// Determine all the items that have flexible width.
|
// Determine all the items that have flexible width.
|
||||||
// Also determine the height of the toolbar.
|
// Also determine the height of the toolbar.
|
||||||
var count = _visibleItems.length,
|
var count = _visibleItems.length
|
||||||
flexibleItemIndexes = [CPIndexSet indexSet];
|
flexibleItemIndexes = [CPIndexSet indexSet];
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
@@ -799,11 +799,10 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
|||||||
|
|
||||||
hasNonSeparatorItem = YES;
|
hasNonSeparatorItem = YES;
|
||||||
|
|
||||||
var menuItem = [[CPMenuItem alloc] initWithTitle:[item label] action:@selector(didSelectMenuItem:) keyEquivalent:nil];
|
var menuItem = [[CPMenuItem alloc] initWithTitle:[item label] action:[item action] keyEquivalent:nil];
|
||||||
|
|
||||||
[menuItem setRepresentedObject:item];
|
|
||||||
[menuItem setImage:[item image]];
|
[menuItem setImage:[item image]];
|
||||||
[menuItem setTarget:self];
|
[menuItem setTarget:[item target]];
|
||||||
[menuItem setEnabled:[item isEnabled]];
|
[menuItem setEnabled:[item isEnabled]];
|
||||||
|
|
||||||
[_additionalItemsButton addItem:menuItem];
|
[_additionalItemsButton addItem:menuItem];
|
||||||
@@ -813,18 +812,6 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
|||||||
[_additionalItemsButton removeFromSuperview];
|
[_additionalItemsButton removeFromSuperview];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Used privately.
|
|
||||||
@ignore
|
|
||||||
*/
|
|
||||||
|
|
||||||
- (void)didSelectMenuItem:(id)aSender
|
|
||||||
{
|
|
||||||
var toolbarItem = [aSender representedObject];
|
|
||||||
|
|
||||||
[CPApp sendAction:[toolbarItem action] to:[toolbarItem target] from:toolbarItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reloadToolbarItems
|
- (void)reloadToolbarItems
|
||||||
{
|
{
|
||||||
// Get rid of all our current subviews.
|
// Get rid of all our current subviews.
|
||||||
@@ -849,10 +836,6 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
|||||||
view = [[_CPToolbarItemView alloc] initWithToolbarItem:item toolbar:self];
|
view = [[_CPToolbarItemView alloc] initWithToolbarItem:item toolbar:self];
|
||||||
|
|
||||||
_viewsForToolbarItems[[item UID]] = view;
|
_viewsForToolbarItems[[item UID]] = view;
|
||||||
|
|
||||||
if ([item toolTip] && [view respondsToSelector:@selector(setToolTip:)])
|
|
||||||
[view setToolTip:[item toolTip]];
|
|
||||||
|
|
||||||
[self addSubview:view];
|
[self addSubview:view];
|
||||||
|
|
||||||
_minWidth += [view minSize].width + TOOLBAR_ITEM_MARGIN;
|
_minWidth += [view minSize].width + TOOLBAR_ITEM_MARGIN;
|
||||||
@@ -1015,7 +998,7 @@ var TOP_MARGIN = 5.0,
|
|||||||
_labelSize = [_labelField frame].size;
|
_labelSize = [_labelField frame].size;
|
||||||
|
|
||||||
_minSize = CGSizeMake(MAX(_labelSize.width, minSize.width), _labelSize.height + minSize.height + LABEL_MARGIN + TOP_MARGIN);
|
_minSize = CGSizeMake(MAX(_labelSize.width, minSize.width), _labelSize.height + minSize.height + LABEL_MARGIN + TOP_MARGIN);
|
||||||
_maxSize = CGSizeMake(MAX(_labelSize.width, maxSize.width), 100000000.0);
|
_maxSize = CGSizeMake(MAX(_labelSize.width, minSize.width), 100000000.0);
|
||||||
|
|
||||||
[_toolbar tile];
|
[_toolbar tile];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
*/
|
*/
|
||||||
- (void)setMinSize:(CGSize)aMinSize
|
- (void)setMinSize:(CGSize)aMinSize
|
||||||
{
|
{
|
||||||
if (!aMinSize.height || !aMinSize.width)
|
if(!aMinSize.height || !aMinSize.width)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_minSize = CGSizeMakeCopy(aMinSize);
|
_minSize = CGSizeMakeCopy(aMinSize);
|
||||||
@@ -409,7 +409,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
|||||||
*/
|
*/
|
||||||
- (void)setMaxSize:(CGSize)aMaxSize
|
- (void)setMaxSize:(CGSize)aMaxSize
|
||||||
{
|
{
|
||||||
if (!aMaxSize.height || !aMaxSize.width)
|
if(!aMaxSize.height || !aMaxSize.width)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_maxSize = CGSizeMakeCopy(aMaxSize);
|
_maxSize = CGSizeMakeCopy(aMaxSize);
|
||||||
|
|||||||
+192
-8
@@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
@import <Foundation/CPObject.j>
|
@import <Foundation/CPObject.j>
|
||||||
@import <Foundation/CPIndexPath.j>
|
|
||||||
|
|
||||||
|
|
||||||
@implementation CPTreeNode : CPObject
|
@implementation CPTreeNode : CPObject
|
||||||
@@ -88,16 +87,38 @@
|
|||||||
[_childNodes[count] sortWithSortDescriptors:sortDescriptors recursively:YES];
|
[_childNodes[count] sortWithSortDescriptors:sortDescriptors recursively:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPTreeNode)descendantNodeAtIndexPath:(CPIndexPath)indexPath
|
- (CPEnumerator)preOrder
|
||||||
{
|
{
|
||||||
var index = 0,
|
return [_CPTreeNodeEnumerator enumeratorWithTreeNode:self order:_CPTreeNodeEnumeratorPreOrder];
|
||||||
count = [indexPath length],
|
}
|
||||||
node = self;
|
|
||||||
|
|
||||||
for (; index < count; ++index)
|
- (CPEnumerator)inOrder
|
||||||
node = [node objectInChildNodesAtIndex:[indexPath indexAtPosition:index]];
|
{
|
||||||
|
return [_CPTreeNodeEnumerator enumeratorWithTreeNode:self order:_CPTreeNodeEnumeratorInOrder];
|
||||||
|
}
|
||||||
|
|
||||||
return node;
|
- (CPEnumerator)postOrder
|
||||||
|
{
|
||||||
|
return [_CPTreeNodeEnumerator enumeratorWithTreeNode:self order:_CPTreeNodeEnumeratorPostOrder];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CPEnumerator)levelOrder
|
||||||
|
{
|
||||||
|
return [_CPTreeNodeEnumerator enumeratorWithTreeNode:self order:_CPTreeNodeEnumeratorLevelOrder];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int)countByEnumeratingWithState:(id)aState objects:(id)objects count:(id)aCount
|
||||||
|
{
|
||||||
|
if (aState.state !== 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// FIXME: Change this to inorder when implemented?
|
||||||
|
var count = [[self preOrder] countByEnumeratingWithState:aState objects:objects count:aCount];
|
||||||
|
|
||||||
|
// Dangerous?
|
||||||
|
aState.state = YES;
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -130,3 +151,166 @@ var CPTreeNodeRepresentedObjectKey = @"CPTreeNodeRepresentedObjectKey",
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
var _CPTreeNodeEnumeratorPreOrder = 0,
|
||||||
|
_CPTreeNodeEnumeratorInOrder = 1,
|
||||||
|
_CPTreeNodeEnumeratorPostOrder = 2,
|
||||||
|
_CPTreeNodeEnumeratorLevelOrder = 3;
|
||||||
|
|
||||||
|
@implementation _CPTreeNodeEnumerator : CPEnumerator
|
||||||
|
{
|
||||||
|
int _order;
|
||||||
|
Object _state;
|
||||||
|
CPTreeNode _treeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (_CPTreeNodeEnumerator)enumeratorWithTreeNode:(CPTreeNode)aTreeNode order:(int)anOrder
|
||||||
|
{
|
||||||
|
return [[self alloc] initWithTreeNode:aTreeNode order:anOrder];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithTreeNode:(CPTreeNode)aTreeNode order:(int)anOrder
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
_order = anOrder
|
||||||
|
_state = [self _newState];
|
||||||
|
_treeNode = aTreeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)_newState
|
||||||
|
{
|
||||||
|
return { stack:[_treeNode], visited:[CPSet set] };
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)_preOrderFromState:(Object)state allObjects:(BOOL)shouldReturnAllObjects
|
||||||
|
{
|
||||||
|
var stack = state.stack;
|
||||||
|
|
||||||
|
if (stack.length <= 0)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
if (shouldReturnAllObjects)
|
||||||
|
var allObjects = [];
|
||||||
|
|
||||||
|
while (stack.length)
|
||||||
|
{
|
||||||
|
var currentNode = stack.pop(),
|
||||||
|
childNodes = currentNode._childNodes,
|
||||||
|
count = childNodes.length;
|
||||||
|
|
||||||
|
while (count--)
|
||||||
|
stack.push(childNodes[count]);
|
||||||
|
|
||||||
|
if (shouldReturnAllObjects)
|
||||||
|
allObjects.push(currentNode);
|
||||||
|
else
|
||||||
|
return currentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allObjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)_inOrderFromState:(Object)state allObjects:(BOOL)shouldReturnAllObjects
|
||||||
|
{
|
||||||
|
// Implement.
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)_postOrderFromState:(Object)state allObjects:(BOOL)shouldReturnAllObjects
|
||||||
|
{
|
||||||
|
var stack = state.stack;
|
||||||
|
|
||||||
|
if (stack.length <= 0)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
var visited = state.visited;
|
||||||
|
|
||||||
|
if (shouldReturnAllObjects)
|
||||||
|
var allObjects = [];
|
||||||
|
|
||||||
|
while (stack.length)
|
||||||
|
{
|
||||||
|
var currentNode = [stack lastObject],
|
||||||
|
childNodes = currentNode._childNodes,
|
||||||
|
originalCount = stack.length,
|
||||||
|
count = childNodes.length;
|
||||||
|
|
||||||
|
while (count--)
|
||||||
|
{
|
||||||
|
var treeNode = childNodes[count];
|
||||||
|
|
||||||
|
if ([visited containsObject:treeNode])
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
stack.push(treeNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalCount === stack.length)
|
||||||
|
{
|
||||||
|
[visited addObject:currentNode];
|
||||||
|
|
||||||
|
if (shouldReturnAllObjects)
|
||||||
|
allObjects.push(stack.pop());
|
||||||
|
else
|
||||||
|
return stack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allObjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)_levelOrderFromState:(Object)state allObjects:(BOOL)shouldReturnAllObjects
|
||||||
|
{
|
||||||
|
// Implement.
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)nextObject
|
||||||
|
{
|
||||||
|
if (_order === _CPTreeNodeEnumeratorPreOrder)
|
||||||
|
return [self _preOrderFromState:[self _newState] allObjects:NO];
|
||||||
|
|
||||||
|
else if (_order === _CPTreeNodeEnumeratorInOrder)
|
||||||
|
return [self _inOrderFromState:[self _newState] allObjects:NO];
|
||||||
|
|
||||||
|
else if (_order === _CPTreeNodeEnumeratorPostOrder)
|
||||||
|
return [self _postOrderFromState:[self _newState] allObjects:NO];
|
||||||
|
|
||||||
|
//else if (_order === _CPTreeNodeEnumeratorLevelOrder)
|
||||||
|
return [self _levelOrderFromState:[self _newState] allObjects:NO];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CPArray)allObjects
|
||||||
|
{
|
||||||
|
if (_order === _CPTreeNodeEnumeratorPreOrder)
|
||||||
|
return [self _preOrderFromState:[self _newState] allObjects:YES];
|
||||||
|
|
||||||
|
else if (_order === _CPTreeNodeEnumeratorInOrder)
|
||||||
|
return [self _inOrderFromState:[self _newState] allObjects:YES];
|
||||||
|
|
||||||
|
else if (_order === _CPTreeNodeEnumeratorPostOrder)
|
||||||
|
return [self _postOrderFromState:[self _newState] allObjects:YES];
|
||||||
|
|
||||||
|
//else if (_order === _CPTreeNodeEnumeratorLevelOrder)
|
||||||
|
return [self _levelOrderFromState:[self _newState] allObjects:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int)countByEnumeratingWithState:(id)aState objects:(id)objects count:(id)aCount
|
||||||
|
{
|
||||||
|
if (aState.state !== 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var count = [[self allObjects] countByEnumeratingWithState:aState objects:objects count:aCount];
|
||||||
|
|
||||||
|
// Dangerous?
|
||||||
|
aState.state = YES;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|||||||
@@ -1,267 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPUserDefaultsController.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Portions based on NSUserDefaultsController.m (2009-06-04) in Cocotron (http://www.cocotron.org/)
|
|
||||||
* Copyright (c) 2006-2007 Christopher J. W. Lloyd
|
|
||||||
*
|
|
||||||
* 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 <Foundation/CPString.j>
|
|
||||||
@import <AppKit/CPController.j>
|
|
||||||
|
|
||||||
var SharedUserDefaultsController = nil;
|
|
||||||
|
|
||||||
@implementation CPUserDefaultsController : CPController
|
|
||||||
{
|
|
||||||
CPUserDefaults _defaults @accessors(readonly, property=defaults);
|
|
||||||
CPDictionary _initialValues @accessors(property=initialValues);
|
|
||||||
BOOL _appliesImmediately @accessors(property=appliesImmediately);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id)sharedUserDefaultsController
|
|
||||||
{
|
|
||||||
if (!SharedUserDefaultsController)
|
|
||||||
SharedUserDefaultsController = [[CPUserDefaultsController alloc] initWithDefaults:nil initialValues:nil];
|
|
||||||
|
|
||||||
return SharedUserDefaultsController;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithDefaults:(CPUserDefaults)someDefaults initialValues:(CPDictionary)initialValues
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
if (!someDefaults)
|
|
||||||
someDefaults = [CPUserDefaults standardUserDefaults];
|
|
||||||
|
|
||||||
_defaults = someDefaults;
|
|
||||||
_initialValues = [initialValues copy];
|
|
||||||
_appliesImmediately = YES;
|
|
||||||
_valueProxy = [[_CPUserDefaultsControllerProxy alloc] initWithController:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)values
|
|
||||||
{
|
|
||||||
return _valueProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)hasUnappliedChanges
|
|
||||||
{
|
|
||||||
return [_valueProxy hasUnappliedChanges];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)save:(id)sender
|
|
||||||
{
|
|
||||||
[_valueProxy save];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)revert:(id)sender
|
|
||||||
{
|
|
||||||
[_valueProxy revert];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)revertToInitialValues:(id)sender
|
|
||||||
{
|
|
||||||
[_valueProxy revertToInitialValues];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
var CPUserDefaultsControllerSharedKey = "CPUserDefaultsControllerSharedKey";
|
|
||||||
|
|
||||||
@implementation CPUserDefaultsController (CPCoding)
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if ([aCoder decodeBoolForKey:CPUserDefaultsControllerSharedKey])
|
|
||||||
return [CPUserDefaultsController sharedUserDefaultsController];
|
|
||||||
|
|
||||||
self = [super initWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self)
|
|
||||||
{
|
|
||||||
[CPException raise:CPUnsupportedMethodException reason:@"decoding of non-shared CPUserDefaultsController not implemented"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
if (self === SharedUserDefaultsController)
|
|
||||||
{
|
|
||||||
[aCoder encodeBool:YES forKey:CPUserDefaultsControllerSharedKey];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CPException raise:CPUnsupportedMethodException reason:@"encoding of non-shared CPUserDefaultsController not implemented"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPUserDefaultsControllerProxy : CPObject
|
|
||||||
{
|
|
||||||
CPUserDefaultsController _controller;
|
|
||||||
// TODO Could be optimised with a JS dict.
|
|
||||||
CPMutableDictionary _cachedValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithController:(CPUserDefaultsController)aController
|
|
||||||
{
|
|
||||||
if (self = [super init])
|
|
||||||
{
|
|
||||||
_controller = aController;
|
|
||||||
_cachedValues = [CPMutableDictionary dictionary];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:CPUserDefaultsDidChangeNotification object:[_controller defaults]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
// FIXME No dealloc in Cappuccino.
|
|
||||||
[[CPNotificationCenter defaultCenter] removeObserver:self];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)valueForKey:(CPString)aKey
|
|
||||||
{
|
|
||||||
var value = [_cachedValues objectForKey:aKey];
|
|
||||||
if (value === nil)
|
|
||||||
{
|
|
||||||
value = [[_controller defaults] objectForKey:aKey];
|
|
||||||
if (value === nil)
|
|
||||||
value = [[_controller initialValues] objectForKey:aKey];
|
|
||||||
|
|
||||||
if (value !== nil)
|
|
||||||
[_cachedValues setObject:value forKey:aKey];
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setValue:(id)aValue forKey:(CPString)aKey
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:aKey];
|
|
||||||
[_cachedValues setObject:aValue forKey:aKey];
|
|
||||||
if ([_controller appliesImmediately])
|
|
||||||
[[_controller defaults] setObject:aValue forKey:aKey];
|
|
||||||
[self didChangeValueForKey:aKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)revert
|
|
||||||
{
|
|
||||||
var keys = [_cachedValues allKeys],
|
|
||||||
keysCount = [keys count];
|
|
||||||
|
|
||||||
while(keysCount--)
|
|
||||||
{
|
|
||||||
var key = keys[keysCount];
|
|
||||||
[self willChangeValueForKey:key];
|
|
||||||
[_cachedValues removeObjectForKey:key];
|
|
||||||
[self didChangeValueForKey:key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)save
|
|
||||||
{
|
|
||||||
var keys = [_cachedValues allKeys],
|
|
||||||
keysCount = [keys count];
|
|
||||||
|
|
||||||
while(keysCount--)
|
|
||||||
{
|
|
||||||
var key = keys[keysCount];
|
|
||||||
[[_controller defaults] setObject:[_cachedValues objectForKey:key] forKey:key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)revertToInitialValues
|
|
||||||
{
|
|
||||||
var initial = [_controller initialValues],
|
|
||||||
keys = [_cachedValues allKeys],
|
|
||||||
keysCount = [keys count];
|
|
||||||
|
|
||||||
while(keysCount--)
|
|
||||||
{
|
|
||||||
var key = keys[keysCount];
|
|
||||||
[self willChangeValueForKey:key];
|
|
||||||
|
|
||||||
var initialValue = [initial objectForKey:key];
|
|
||||||
if (initialValue !== nil)
|
|
||||||
[_cachedValues setObject:initialValue forKey:key];
|
|
||||||
else
|
|
||||||
[_cachedValues removeObjectForKey:key];
|
|
||||||
|
|
||||||
[self didChangeValueForKey:key];
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)userDefaultsDidChange:(CPNotification)aNotification
|
|
||||||
{
|
|
||||||
var defaults = [_controller defaults],
|
|
||||||
keys = [_cachedValues allKeys],
|
|
||||||
keysCount = [keys count];
|
|
||||||
|
|
||||||
while(keysCount--)
|
|
||||||
{
|
|
||||||
var key = keys[keysCount],
|
|
||||||
value = [_cachedValues objectForKey:key],
|
|
||||||
newValue = [defaults objectForKey:key];
|
|
||||||
|
|
||||||
if (![value isEqual:newValue])
|
|
||||||
{
|
|
||||||
[self willChangeValueForKey:key];
|
|
||||||
[_cachedValues setObject:newValue forKey:key];
|
|
||||||
[self didChangeValueForKey:key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)hasUnappliedChanges
|
|
||||||
{
|
|
||||||
var defaults = [_controller defaults],
|
|
||||||
keys = [_cachedValues allKeys],
|
|
||||||
keysCount = [keys count];
|
|
||||||
|
|
||||||
while(keysCount--)
|
|
||||||
{
|
|
||||||
var key = keys[keysCount],
|
|
||||||
value = [_cachedValues objectForKey:key],
|
|
||||||
newValue = [defaults objectForKey:key];
|
|
||||||
|
|
||||||
if (![value isEqual:newValue])
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
+13
-113
@@ -186,9 +186,6 @@ var CPViewFlags = { },
|
|||||||
CPView _previousKeyView;
|
CPView _previousKeyView;
|
||||||
|
|
||||||
unsigned _viewClassFlags;
|
unsigned _viewClassFlags;
|
||||||
|
|
||||||
// ToolTips
|
|
||||||
CPString _toolTip @accessors(property=toolTip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -708,7 +705,7 @@ var CPViewFlags = { },
|
|||||||
Sets the frame size of the receiver to the dimensions and origin of the provided rectangle in the coordinate system
|
Sets the frame size of the receiver to the dimensions and origin of the provided rectangle in the coordinate system
|
||||||
of the superview. The method also posts an CPViewFrameDidChangeNotification to the notification
|
of the superview. The method also posts an CPViewFrameDidChangeNotification to the notification
|
||||||
center if the receiver is configured to do so. If the frame is the same as the current frame, the method simply
|
center if the receiver is configured to do so. If the frame is the same as the current frame, the method simply
|
||||||
returns (and no notification is posted).
|
returns (and no notificaion is posted).
|
||||||
@param aFrame the rectangle specifying the new origin and size of the receiver
|
@param aFrame the rectangle specifying the new origin and size of the receiver
|
||||||
*/
|
*/
|
||||||
- (void)setFrame:(CGRect)aFrame
|
- (void)setFrame:(CGRect)aFrame
|
||||||
@@ -1287,14 +1284,6 @@ var CPViewFlags = { },
|
|||||||
return view !== nil;
|
return view !== nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns YES if the view is not hidden, has no hidden ancestor and doesn't belong to a hidden window.
|
|
||||||
*/
|
|
||||||
- (BOOL)_isVisible
|
|
||||||
{
|
|
||||||
return ![self isHiddenOrHasHiddenAncestor] && [[self window] isVisible];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Called when the return value of isHiddenOrHasHiddenAncestor becomes YES,
|
Called when the return value of isHiddenOrHasHiddenAncestor becomes YES,
|
||||||
e.g. when this view becomes hidden due to a setHidden:YES message to
|
e.g. when this view becomes hidden due to a setHidden:YES message to
|
||||||
@@ -1577,16 +1566,6 @@ var CPViewFlags = { },
|
|||||||
return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(aView, self));
|
return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(aView, self));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Converts the point from the base coordinate system to the receiver’s coordinate system.
|
|
||||||
@param aPoint A point specifying a location in the base coordinate system
|
|
||||||
@return The point converted to the receiver’s base coordinate system
|
|
||||||
*/
|
|
||||||
- (CGPoint)convertPointFromBase:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(nil, self));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Converts \c aPoint from the receiver's coordinate space to the coordinate space of \c aView.
|
Converts \c aPoint from the receiver's coordinate space to the coordinate space of \c aView.
|
||||||
@param aPoint the point to convert
|
@param aPoint the point to convert
|
||||||
@@ -1598,16 +1577,6 @@ var CPViewFlags = { },
|
|||||||
return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(self, aView));
|
return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(self, aView));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Converts the point from the receiver’s coordinate system to the base coordinate system.
|
|
||||||
@param aPoint A point specifying a location in the coordinate system of the receiver
|
|
||||||
@return The point converted to the base coordinate system
|
|
||||||
*/
|
|
||||||
- (CGPoint)convertPointToBase:(CGPoint)aPoint
|
|
||||||
{
|
|
||||||
return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(self, nil));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Convert's \c aSize from \c aView's coordinate space to the receiver's coordinate space.
|
Convert's \c aSize from \c aView's coordinate space to the receiver's coordinate space.
|
||||||
@param aSize the size to convert
|
@param aSize the size to convert
|
||||||
@@ -1641,16 +1610,6 @@ var CPViewFlags = { },
|
|||||||
return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(aView, self));
|
return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(aView, self));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Converts the rectangle from the base coordinate system to the receiver’s coordinate system.
|
|
||||||
@param aRect A rectangle specifying a location in the base coordinate system
|
|
||||||
@return The rectangle converted to the receiver’s base coordinate system
|
|
||||||
*/
|
|
||||||
- (CGRect)convertRectFromBase:(CGRect)aRect
|
|
||||||
{
|
|
||||||
return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(nil, self));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Converts \c aRect from the receiver's coordinate space to \c aView's coordinate space.
|
Converts \c aRect from the receiver's coordinate space to \c aView's coordinate space.
|
||||||
@param aRect the rectangle to convert
|
@param aRect the rectangle to convert
|
||||||
@@ -1662,16 +1621,6 @@ var CPViewFlags = { },
|
|||||||
return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(self, aView));
|
return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(self, aView));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Converts the rectangle from the receiver’s coordinate system to the base coordinate system.
|
|
||||||
@param aRect A rectangle specifying a location in the coordinate system of the receiver
|
|
||||||
@return The rectangle converted to the base coordinate system
|
|
||||||
*/
|
|
||||||
- (CGRect)convertRectToBase:(CGRect)aRect
|
|
||||||
{
|
|
||||||
return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(self, nil));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets whether the receiver posts a CPViewFrameDidChangeNotification notification
|
Sets whether the receiver posts a CPViewFrameDidChangeNotification notification
|
||||||
to the default notification center when its frame is changed. The default is \c NO.
|
to the default notification center when its frame is changed. The default is \c NO.
|
||||||
@@ -1746,7 +1695,7 @@ setBoundsOrigin:
|
|||||||
@param aLocation the lower-left corner coordinate of \c anImage
|
@param aLocation the lower-left corner coordinate of \c anImage
|
||||||
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
||||||
@param anEvent the \c -mouseDown: that triggered the drag
|
@param anEvent the \c -mouseDown: that triggered the drag
|
||||||
@param aPasteboard the pasteboard that holds the drag data
|
@param aPastebaord the pasteboard that holds the drag data
|
||||||
@param aSourceObject the drag operation controller
|
@param aSourceObject the drag operation controller
|
||||||
@param slideBack Whether the image should 'slide back' if the drag is rejected
|
@param slideBack Whether the image should 'slide back' if the drag is rejected
|
||||||
*/
|
*/
|
||||||
@@ -1761,7 +1710,7 @@ setBoundsOrigin:
|
|||||||
@param aLocation the top-left corner coordinate of \c aView
|
@param aLocation the top-left corner coordinate of \c aView
|
||||||
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
||||||
@param anEvent the \c -mouseDown: that triggered the drag
|
@param anEvent the \c -mouseDown: that triggered the drag
|
||||||
@param aPasteboard the pasteboard that holds the drag data
|
@param aPastebaord the pasteboard that holds the drag data
|
||||||
@param aSourceObject the drag operation controller
|
@param aSourceObject the drag operation controller
|
||||||
@param slideBack Whether the view should 'slide back' if the drag is rejected
|
@param slideBack Whether the view should 'slide back' if the drag is rejected
|
||||||
*/
|
*/
|
||||||
@@ -2111,7 +2060,7 @@ setBoundsOrigin:
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Scrolls the clip view to a specified point
|
Scrolls the clip view to a specified point
|
||||||
@param the clip view to scroll
|
@param the clip view to scoll
|
||||||
@param the point to scroll to
|
@param the point to scroll to
|
||||||
*/
|
*/
|
||||||
- (void)scrollClipView:(CPClipView)aClipView toPoint:(CGPoint)aPoint
|
- (void)scrollClipView:(CPClipView)aClipView toPoint:(CGPoint)aPoint
|
||||||
@@ -2132,20 +2081,6 @@ setBoundsOrigin:
|
|||||||
|
|
||||||
@implementation CPView (KeyView)
|
@implementation CPView (KeyView)
|
||||||
|
|
||||||
/*!
|
|
||||||
Overridden by subclasses to handle a key equivalent.
|
|
||||||
|
|
||||||
If the receiver’s key equivalent is the same as the characters of the key-down event theEvent,
|
|
||||||
as returned by \ref CPEvent::charactersIgnoringModifiers "[anEvent charactersIgnoringModifiers]",
|
|
||||||
the receiver should take the appropriate action and return \c YES. Otherwise, it should return
|
|
||||||
the result of invoking super’s implementation. The default implementation of this method simply
|
|
||||||
passes the message down the view hierarchy (from superviews to subviews)
|
|
||||||
and returns \c NO if none of the receiver’s subviews responds \c YES.
|
|
||||||
|
|
||||||
@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
|
||||||
{
|
{
|
||||||
var count = [_subviews count];
|
var count = [_subviews count];
|
||||||
@@ -2170,18 +2105,11 @@ setBoundsOrigin:
|
|||||||
|
|
||||||
- (CPView)nextValidKeyView
|
- (CPView)nextValidKeyView
|
||||||
{
|
{
|
||||||
var result = [self nextKeyView],
|
var result = [self nextKeyView];
|
||||||
firstResult = result;
|
|
||||||
|
|
||||||
while (result && ![result canBecomeKeyView])
|
while (result && ![result canBecomeKeyView])
|
||||||
{
|
|
||||||
result = [result nextKeyView];
|
result = [result nextKeyView];
|
||||||
|
|
||||||
// Cycled.
|
|
||||||
if (result === firstResult)
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2192,38 +2120,23 @@ setBoundsOrigin:
|
|||||||
|
|
||||||
- (CPView)previousValidKeyView
|
- (CPView)previousValidKeyView
|
||||||
{
|
{
|
||||||
var result = [self previousKeyView],
|
var result = [self previousKeyView];
|
||||||
firstResult = result;
|
|
||||||
|
|
||||||
while (result && ![result canBecomeKeyView])
|
while (result && ![result canBecomeKeyView])
|
||||||
{
|
|
||||||
result = [result previousKeyView];
|
result = [result previousKeyView];
|
||||||
|
|
||||||
// Cycled.
|
|
||||||
if (result === firstResult)
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_setPreviousKeyView:(CPView)previous
|
- (void)_setPreviousKeyView:(CPView)previous
|
||||||
{
|
{
|
||||||
if ([previous isEqual:self])
|
_previousKeyView = previous;
|
||||||
_previousKeyView = nil;
|
|
||||||
else
|
|
||||||
_previousKeyView = previous;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setNextKeyView:(CPView)next
|
- (void)setNextKeyView:(CPView)next
|
||||||
{
|
{
|
||||||
if ([next isEqual:self])
|
_nextKeyView = next;
|
||||||
_nextKeyView = nil;
|
[_nextKeyView _setPreviousKeyView:self];
|
||||||
else
|
|
||||||
{
|
|
||||||
_nextKeyView = next;
|
|
||||||
[_nextKeyView _setPreviousKeyView:self];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -2300,10 +2213,6 @@ setBoundsOrigin:
|
|||||||
|
|
||||||
- (BOOL)hasThemeState:(CPThemeState)aState
|
- (BOOL)hasThemeState:(CPThemeState)aState
|
||||||
{
|
{
|
||||||
// Because CPThemeStateNormal is defined as 0 we need to check for it explicitly here
|
|
||||||
if (aState === CPThemeStateNormal && _themeState === CPThemeStateNormal)
|
|
||||||
return YES;
|
|
||||||
|
|
||||||
return !!(_themeState & ((typeof aState === "string") ? CPThemeState(aState) : aState));
|
return !!(_themeState & ((typeof aState === "string") ? CPThemeState(aState) : aState));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2538,11 +2447,6 @@ setBoundsOrigin:
|
|||||||
return [_themeAttributes[aName] valueForState:_themeState];
|
return [_themeAttributes[aName] valueForState:_themeState];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)hasThemeAttribute:(CPString)aName
|
|
||||||
{
|
|
||||||
return (_themeAttributes && _themeAttributes[aName] !== undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CPView)createEphemeralSubviewNamed:(CPString)aViewName
|
- (CPView)createEphemeralSubviewNamed:(CPString)aViewName
|
||||||
{
|
{
|
||||||
return nil;
|
return nil;
|
||||||
@@ -2565,7 +2469,7 @@ setBoundsOrigin:
|
|||||||
|
|
||||||
var frame = [self rectForEphemeralSubviewNamed:aViewName];
|
var frame = [self rectForEphemeralSubviewNamed:aViewName];
|
||||||
|
|
||||||
if (frame)
|
if (frame && !_CGRectIsEmpty(frame))
|
||||||
{
|
{
|
||||||
if (!_ephemeralSubviewsForNames[aViewName])
|
if (!_ephemeralSubviewsForNames[aViewName])
|
||||||
{
|
{
|
||||||
@@ -2654,11 +2558,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
|||||||
_registeredDraggedTypes = [CPSet set];
|
_registeredDraggedTypes = [CPSet set];
|
||||||
_registeredDraggedTypesArray = [];
|
_registeredDraggedTypesArray = [];
|
||||||
|
|
||||||
// Other views (CPBox) might set an autoresizes mask on their subviews before it is actually decoded.
|
_autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey] || CPViewNotSizable;
|
||||||
// We make sure we don't override the value by checking if it was already set.
|
|
||||||
if (_autoresizingMask === nil)
|
|
||||||
_autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey] || CPViewNotSizable;
|
|
||||||
|
|
||||||
_autoresizesSubviews = ![aCoder containsValueForKey:CPViewAutoresizesSubviewsKey] || [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey];
|
_autoresizesSubviews = ![aCoder containsValueForKey:CPViewAutoresizesSubviewsKey] || [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey];
|
||||||
|
|
||||||
_hitTests = ![aCoder containsValueForKey:CPViewHitTestsKey] || [aCoder decodeObjectForKey:CPViewHitTestsKey];
|
_hitTests = ![aCoder containsValueForKey:CPViewHitTestsKey] || [aCoder decodeObjectForKey:CPViewHitTestsKey];
|
||||||
@@ -2776,12 +2676,12 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
|||||||
|
|
||||||
var nextKeyView = [self nextKeyView];
|
var nextKeyView = [self nextKeyView];
|
||||||
|
|
||||||
if (nextKeyView !== nil && ![nextKeyView isEqual:self])
|
if (nextKeyView !== nil)
|
||||||
[aCoder encodeConditionalObject:nextKeyView forKey:CPViewNextKeyViewKey];
|
[aCoder encodeConditionalObject:nextKeyView forKey:CPViewNextKeyViewKey];
|
||||||
|
|
||||||
var previousKeyView = [self previousKeyView];
|
var previousKeyView = [self previousKeyView];
|
||||||
|
|
||||||
if (previousKeyView !== nil && ![previousKeyView isEqual:self])
|
if (previousKeyView !== nil)
|
||||||
[aCoder encodeConditionalObject:previousKeyView forKey:CPViewPreviousKeyViewKey];
|
[aCoder encodeConditionalObject:previousKeyView forKey:CPViewPreviousKeyViewKey];
|
||||||
|
|
||||||
[aCoder encodeObject:[self themeClass] forKey:CPViewThemeClassKey];
|
[aCoder encodeObject:[self themeClass] forKey:CPViewThemeClassKey];
|
||||||
|
|||||||
+16
-65
@@ -23,49 +23,19 @@
|
|||||||
@import "CPAnimation.j"
|
@import "CPAnimation.j"
|
||||||
|
|
||||||
|
|
||||||
CPViewAnimationTargetKey = @"CPViewAnimationTargetKey";
|
CPViewAnimationTargetKey = @"CPViewAnimationTarget";
|
||||||
CPViewAnimationStartFrameKey = @"CPViewAnimationStartFrameKey";
|
CPViewAnimationStartFrameKey = @"CPViewAnimationStartFrame";
|
||||||
CPViewAnimationEndFrameKey = @"CPViewAnimationEndFrameKey";
|
CPViewAnimationEndFrameKey = @"CPViewAnimationEndFrame";
|
||||||
CPViewAnimationEffectKey = @"CPViewAnimationEffectKey";
|
CPViewAnimationEffectKey = @"CPViewAnimationEffect";
|
||||||
|
|
||||||
CPViewAnimationFadeInEffect = @"CPViewAnimationFadeInEffect";
|
CPViewAnimationFadeInEffect = @"CPViewAnimationFadeIn";
|
||||||
CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut";
|
||||||
|
|
||||||
/*!
|
|
||||||
@class CPViewAnimation
|
|
||||||
|
|
||||||
CPViewAnimation is a subclass of CPAnimation that makes it easy to do
|
|
||||||
basic animations on views.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@implementation CPViewAnimation : CPAnimation
|
@implementation CPViewAnimation : CPAnimation
|
||||||
{
|
{
|
||||||
CPArray _viewAnimations;
|
CPArray _viewAnimations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Designated initializer.
|
|
||||||
|
|
||||||
This method takes an array of CPDictionaries. Each dictionary should
|
|
||||||
contain values for the following keys:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
CPViewAnimationTargetKey - (Required) The view to animate.
|
|
||||||
CPViewAnimationStartFrameKey - (Optional) The start frame of the target.
|
|
||||||
CPViewAnimationEndFrameKey - (Optional) The end frame of the target.
|
|
||||||
CPViewAnimationEffectKey - (Optional) a fade effect to use for the animation.
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
For example:
|
|
||||||
<pre>
|
|
||||||
var animation = [CPDictionary dictionaryWithObjects:[myViewToAnimate, aStartFrame, anEndFrame, CPViewAnimationFadeInEffect]
|
|
||||||
forKeys:[CPViewAnimationTargetKey, CPViewAnimationStartFrameKey, CPViewAnimationEndFrameKey, CPViewAnimationEffectKey]];
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
If you pass nil instead of an array of dictionaries you should later call setViewAnimations:.
|
|
||||||
|
|
||||||
@param viewAnimations - An array of CPDictionaries for each animation.
|
|
||||||
*/
|
|
||||||
- (id)initWithViewAnimations:(CPArray)viewAnimations
|
- (id)initWithViewAnimations:(CPArray)viewAnimations
|
||||||
{
|
{
|
||||||
if (self = [super initWithDuration:0.5 animationCurve:CPAnimationLinear])
|
if (self = [super initWithDuration:0.5 animationCurve:CPAnimationLinear])
|
||||||
@@ -91,7 +61,7 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
|||||||
if (effect === CPViewAnimationFadeInEffect)
|
if (effect === CPViewAnimationFadeInEffect)
|
||||||
{
|
{
|
||||||
[view setAlphaValue:0.0];
|
[view setAlphaValue:0.0];
|
||||||
[self _targetView:view setHidden:NO];
|
[view setHidden:NO];
|
||||||
}
|
}
|
||||||
else if (effect === CPViewAnimationFadeOutEffect)
|
else if (effect === CPViewAnimationFadeOutEffect)
|
||||||
[view setAlphaValue:1.0];
|
[view setAlphaValue:1.0];
|
||||||
@@ -111,8 +81,7 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
|||||||
view = [self _targetView:dictionary],
|
view = [self _targetView:dictionary],
|
||||||
startFrame = [self _startFrame:dictionary],
|
startFrame = [self _startFrame:dictionary],
|
||||||
endFrame = [self _endFrame:dictionary],
|
endFrame = [self _endFrame:dictionary],
|
||||||
differenceFrame = _CGRectMakeZero(),
|
differenceFrame = _CGRectMakeZero();
|
||||||
value = [super currentValue];
|
|
||||||
|
|
||||||
differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x;
|
differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x;
|
||||||
differenceFrame.origin.y = endFrame.origin.y - startFrame.origin.y;
|
differenceFrame.origin.y = endFrame.origin.y - startFrame.origin.y;
|
||||||
@@ -120,22 +89,22 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
|||||||
differenceFrame.size.height = endFrame.size.height - startFrame.size.height;
|
differenceFrame.size.height = endFrame.size.height - startFrame.size.height;
|
||||||
|
|
||||||
var intermediateFrame = _CGRectMakeZero();
|
var intermediateFrame = _CGRectMakeZero();
|
||||||
intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * value;
|
intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * progress;
|
||||||
intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * value;
|
intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * progress;
|
||||||
intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * value;
|
intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * progress;
|
||||||
intermediateFrame.size.height = startFrame.size.height + differenceFrame.size.height * value;
|
intermediateFrame.size.height = startFrame.size.height + differenceFrame.size.height * progress;
|
||||||
|
|
||||||
[view setFrame:intermediateFrame];
|
[view setFrame:intermediateFrame];
|
||||||
|
|
||||||
// Update the view's alpha value
|
// Update the view's alpha value
|
||||||
var effect = [self _effect:dictionary];
|
var effect = [self _effect:dictionary];
|
||||||
if (effect === CPViewAnimationFadeInEffect)
|
if (effect === CPViewAnimationFadeInEffect)
|
||||||
[view setAlphaValue:1.0 * value];
|
[view setAlphaValue:1.0 * progress];
|
||||||
else if (effect === CPViewAnimationFadeOutEffect)
|
else if (effect === CPViewAnimationFadeOutEffect)
|
||||||
[view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * value];
|
[view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * progress];
|
||||||
|
|
||||||
if (progress === 1.0)
|
if (progress === 1.0)
|
||||||
[self _targetView:view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
|
[view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,25 +125,12 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
|||||||
else if (effect === CPViewAnimationFadeOutEffect)
|
else if (effect === CPViewAnimationFadeOutEffect)
|
||||||
[view setAlphaValue:0.0];
|
[view setAlphaValue:0.0];
|
||||||
|
|
||||||
[self _targetView:view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
|
[view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
[super stopAnimation];
|
[super stopAnimation];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_targetView:(id)theView setHidden:(BOOL)isHidden
|
|
||||||
{
|
|
||||||
if ([theView isKindOfClass:[CPWindow class]])
|
|
||||||
{
|
|
||||||
if (isHidden)
|
|
||||||
[theView orderOut:self];
|
|
||||||
else
|
|
||||||
[theView orderFront:self];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
[theView setHidden:isHidden];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)_targetView:(CPDictionary)dictionary
|
- (id)_targetView:(CPDictionary)dictionary
|
||||||
{
|
{
|
||||||
var targetView = [dictionary valueForKey:CPViewAnimationTargetKey];
|
var targetView = [dictionary valueForKey:CPViewAnimationTargetKey];
|
||||||
@@ -212,11 +168,6 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
|||||||
return _viewAnimations;
|
return _viewAnimations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Takes an array of CPDictionaries as documented in initWithViewAnimations:.
|
|
||||||
|
|
||||||
@param viewAnimations - An array of dictionaries describing the animation.
|
|
||||||
*/
|
|
||||||
- (void)setViewAnimations:(CPArray)viewAnimations
|
- (void)setViewAnimations:(CPArray)viewAnimations
|
||||||
{
|
{
|
||||||
if (viewAnimations != _viewAnimations)
|
if (viewAnimations != _viewAnimations)
|
||||||
|
|||||||
+31
-59
@@ -29,38 +29,26 @@
|
|||||||
|
|
||||||
var CPViewControllerCachedCibs;
|
var CPViewControllerCachedCibs;
|
||||||
|
|
||||||
/*!
|
/*! @class CPViewController
|
||||||
@ingroup appkit
|
The CPViewController class provides the fundamental view-management controller for Cappuccino applications.
|
||||||
@class CPViewController
|
The basic view controller class supports the presentation of an associated view in addition to basic support
|
||||||
|
for managing modal views and, in the future, animations. Subclasses such as CPNavigationController and
|
||||||
|
CPTabBarController provide additional behavior for managing complex hierarchies of view controllers and views.
|
||||||
|
|
||||||
The CPViewController class provides the fundamental view-management
|
You use each instance of CPViewController to manage a single view (and hierarchy). For a simple view controller,
|
||||||
controller for Cappuccino applications. The basic view controller class
|
this entails managing the view hierarchy responsible for presenting your application content.
|
||||||
supports the presentation of an associated view in addition to basic
|
A typical view hierarchy consists of a root viewŃa reference to which is available in the view property of this classŃ
|
||||||
support for managing modal views and, in the future, animations.
|
and one or more subviews presenting the actual content. In the case of navigation and tab bar controllers, the view
|
||||||
Subclasses such as CPNavigationController and CPTabBarController provide
|
controller manages not only the high-level view hierarchy (which provides the navigation controls) but also one
|
||||||
additional behavior for managing complex hierarchies of view controllers
|
or more additional view controllers that handle the presentation of the application content.
|
||||||
and views.
|
|
||||||
|
|
||||||
You use each instance of CPViewController to manage a single view (and
|
Unlike UIViewController in Cocoa Touch, a CPViewController does not represent an entire screen of content. You
|
||||||
hierarchy). For a simple view controller, this entails managing the view
|
will add your root view to an existing view or window's content view. You can manage many view controllers
|
||||||
hierarchy responsible for presenting your application content. A typical
|
on screen at once. CPViewController is also the preferred way of working with Cibs.
|
||||||
view hierarchy consists of a root view, a reference to which is available
|
|
||||||
in the view property of this class, and one or more subviews presenting
|
|
||||||
the actual content. In the case of navigation and tab bar controllers, the
|
|
||||||
view controller manages not only the high-level view hierarchy (which
|
|
||||||
provides the navigation controls) but also one or more additional view
|
|
||||||
controllers that handle the presentation of the application content.
|
|
||||||
|
|
||||||
Unlike UIViewController in Cocoa Touch, a CPViewController does not
|
Subclasses can override -loadView to create their custom view hierarchy, or specify a cib name to be loaded automatically.
|
||||||
represent an entire screen of content. You will add your root view to an
|
It has methods that are called when a view appears or disappears.
|
||||||
existing view or window's content view. You can manage many view
|
This class is also a good place for delegate & datasource methods, and other controller stuff.
|
||||||
controllers on screen at once. CPViewController is also the preferred way
|
|
||||||
of working with Cibs.
|
|
||||||
|
|
||||||
Subclasses can override -loadView to create their custom view hierarchy,
|
|
||||||
or specify a cib name to be loaded automatically. It has methods that are
|
|
||||||
called when a view appears or disappears. This class is also a good place
|
|
||||||
for delegate & datasource methods, and other controller stuff.
|
|
||||||
*/
|
*/
|
||||||
@implementation CPViewController : CPResponder
|
@implementation CPViewController : CPResponder
|
||||||
{
|
{
|
||||||
@@ -100,14 +88,9 @@ var CPViewControllerCachedCibs;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
The designated initializer. If you subclass CPViewController, you must
|
The designated initializer. If you subclass CPViewController, you must call the super implementation of this method, even if you aren't using a Cib.
|
||||||
call the super implementation of this method, even if you aren't using a
|
In the specified Cib, the File's Owner proxy should have its class set to your view controller subclass, with the view outlet connected to the main view.
|
||||||
Cib.
|
If you pass in a nil Cib name, then you must either call -setView: before -view is invoked, or override -loadView to set up your views.
|
||||||
|
|
||||||
In the specified Cib, the File's Owner proxy should have its class set to
|
|
||||||
your view controller subclass, with the view outlet connected to the main
|
|
||||||
view. If you pass in a nil Cib name, then you must either call -setView:
|
|
||||||
before -view is invoked, or override -loadView to set up your views.
|
|
||||||
|
|
||||||
@param cibNameOrNil The path to the cib to load for the root view or nil to programmatically create views.
|
@param cibNameOrNil The path to the cib to load for the root view or nil to programmatically create views.
|
||||||
@param cibBundleOrNil The bundle that the cib is located in or nil for the main bundle.
|
@param cibBundleOrNil The bundle that the cib is located in or nil for the main bundle.
|
||||||
@@ -130,18 +113,14 @@ var CPViewControllerCachedCibs;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Programmatically creates the view that the controller manages. You should
|
Programmatically creates the view that the controller manages.
|
||||||
never call this method directly. The view controller calls this method
|
You should never call this method directly. The view controller calls this method when the view property is requested but is nil.
|
||||||
when the view property is requested but is nil.
|
|
||||||
|
|
||||||
If you create your views manually, you must override this method and use
|
If you create your views manually, you must override this method and use it to create your view and assign it to the view property.
|
||||||
it to create your view and assign it to the view property. The default
|
The default implementation for programmatic views is to create a plain view. You can invoke super to utilize this view.
|
||||||
implementation for programmatic views is to create a plain view. You can
|
|
||||||
invoke super to utilize this view.
|
|
||||||
|
|
||||||
If you use Interface Builder to create your views, you initialize the view
|
If you use Interface Builder to create your views, you initialize the view using the
|
||||||
using the initWithCibName:bundle: method then you must not override this
|
initWithCibName:bundle: method then you must not override this method. The consequences risk shattering the space-time continuum.
|
||||||
method. The consequences risk shattering the space-time continuum.
|
|
||||||
|
|
||||||
Note: The cib loading system is currently synchronous.
|
Note: The cib loading system is currently synchronous.
|
||||||
*/
|
*/
|
||||||
@@ -165,10 +144,8 @@ var CPViewControllerCachedCibs;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the view that the controller manages.
|
Returns the view that the controller manages.
|
||||||
|
If this property is nil, the controller sends loadView to itself to create the view that it manages.
|
||||||
If this property is nil, the controller sends loadView to itself to create
|
Subclasses should override the loadView method to create any custom views. The default value is nil.
|
||||||
the view that it manages. Subclasses should override the loadView method
|
|
||||||
to create any custom views. The default value is nil.
|
|
||||||
*/
|
*/
|
||||||
- (CPView)view
|
- (CPView)view
|
||||||
{
|
{
|
||||||
@@ -206,11 +183,8 @@ var CPViewControllerCachedCibs;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
This method is called after the view controller has loaded its associated views into memory.
|
This method is called after the view controller has loaded its associated views into memory.
|
||||||
|
This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method.
|
||||||
This method is called regardless of whether the views were stored in a nib
|
This method is most commonly used to perform additional initialization steps on views that are loaded from cib files.
|
||||||
file or created programmatically in the loadView method. This method is
|
|
||||||
most commonly used to perform additional initialization steps on views
|
|
||||||
that are loaded from cib files.
|
|
||||||
*/
|
*/
|
||||||
- (void)viewDidLoad
|
- (void)viewDidLoad
|
||||||
{
|
{
|
||||||
@@ -220,9 +194,7 @@ var CPViewControllerCachedCibs;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Manually sets the view that the controller manages.
|
Manually sets the view that the controller manages.
|
||||||
|
Setting to nil will cause -loadView to be called on all subsequent calls of -view.
|
||||||
Setting to nil will cause -loadView to be called on all subsequent calls
|
|
||||||
of -view.
|
|
||||||
|
|
||||||
@param aView The view this controller should represent.
|
@param aView The view this controller should represent.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+113
-487
@@ -36,94 +36,45 @@ CPWebViewProgressEstimateChangedNotification = "CPWebViewProgressEstimateChan
|
|||||||
CPWebViewProgressStartedNotification = "CPWebViewProgressStartedNotification";
|
CPWebViewProgressStartedNotification = "CPWebViewProgressStartedNotification";
|
||||||
CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNotification";
|
CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNotification";
|
||||||
|
|
||||||
/*!
|
|
||||||
Automatically choose between AppKit (Cappuccino style) scrollbars and
|
|
||||||
native scrollbars. In this mode AppKit scrollbars are always used except
|
|
||||||
when the web view is loaded with a URL which does not appear to pass the
|
|
||||||
same origin policy.
|
|
||||||
*/
|
|
||||||
CPWebViewScrollAuto = 0;
|
|
||||||
/*!
|
|
||||||
Always use AppKit scrollbars. If a URL is loaded whcih does not appear to
|
|
||||||
pass the same origin policy, native scrollbars will be used but a warnning
|
|
||||||
will be logged.
|
|
||||||
*/
|
|
||||||
CPWebViewScrollAppKit = 1;
|
CPWebViewScrollAppKit = 1;
|
||||||
/*!
|
|
||||||
Always use native (platform dependent) scrollbars.
|
|
||||||
*/
|
|
||||||
CPWebViewScrollNative = 2;
|
CPWebViewScrollNative = 2;
|
||||||
/*!
|
|
||||||
Never display scrollbars.
|
|
||||||
*/
|
|
||||||
CPWebViewScrollNone = 3;
|
|
||||||
|
|
||||||
/*!
|
// FIXME: somehow make CPWebView work with CPScrollView instead of native scrollbars (is this even possible?)
|
||||||
How frequently the size of the document will be checked at page load time
|
|
||||||
when AppKit scrollbars are used.
|
|
||||||
*/
|
|
||||||
CPWebViewAppKitScrollPollInterval = 1.0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
How many times the size of the size of the document will be checked at
|
|
||||||
page load time when AppKit scrollbars are used.
|
|
||||||
|
|
||||||
AppKit scrollbars must check the size of the document to display the
|
|
||||||
correct size of scrollbars. To improve performance the checks are only
|
|
||||||
performed a limited number of times per reload.
|
|
||||||
*/
|
|
||||||
CPWebViewAppKitScrollMaxPollCount = 3;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
|
|
||||||
@class CPWebView
|
|
||||||
|
|
||||||
CPWebView allows you to display arbitrary HTML or embed a
|
|
||||||
webpage inside your application.
|
|
||||||
|
|
||||||
It's important to note that the same origin policy applies to this view.
|
|
||||||
That is, if the web page being displayed is not located in the same origin
|
|
||||||
(protocol, domain, and port) as the application, you will have limited
|
|
||||||
control over the view and no access to its contents. Furthermore,
|
|
||||||
Cappuccino style scrollbars can't be used to scroll it.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@implementation CPWebView : CPView
|
@implementation CPWebView : CPView
|
||||||
{
|
{
|
||||||
CPScrollView _scrollView;
|
CPScrollView _scrollView;
|
||||||
CPView _frameView;
|
CPView _frameView;
|
||||||
|
|
||||||
IFrame _iframe;
|
IFrame _iframe;
|
||||||
CPString _mainFrameURL;
|
CPString _mainFrameURL;
|
||||||
CPArray _backwardStack;
|
CPArray _backwardStack;
|
||||||
CPArray _forwardStack;
|
CPArray _forwardStack;
|
||||||
|
|
||||||
BOOL _ignoreLoadStart;
|
BOOL _ignoreLoadStart;
|
||||||
BOOL _ignoreLoadEnd;
|
BOOL _ignoreLoadEnd;
|
||||||
BOOL _isLoading;
|
|
||||||
|
|
||||||
id _downloadDelegate;
|
id _downloadDelegate;
|
||||||
id _frameLoadDelegate;
|
id _frameLoadDelegate;
|
||||||
id _policyDelegate;
|
id _policyDelegate;
|
||||||
id _resourceLoadDelegate;
|
id _resourceLoadDelegate;
|
||||||
id _UIDelegate;
|
id _UIDelegate;
|
||||||
|
|
||||||
CPWebScriptObject _wso;
|
CPWebScriptObject _wso;
|
||||||
|
|
||||||
CPString _url;
|
CPString _url;
|
||||||
CPString _html;
|
CPString _html;
|
||||||
|
|
||||||
Function _loadCallback;
|
Function _loadCallback;
|
||||||
|
|
||||||
int _scrollMode;
|
int _scrollMode;
|
||||||
int _effectiveScrollMode;
|
CGSize _scrollSize;
|
||||||
BOOL _contentIsAccessible;
|
|
||||||
CPTimer _contentSizeCheckTimer;
|
|
||||||
int _contentSizePollCount;
|
|
||||||
|
|
||||||
int _loadHTMLStringTimer;
|
int _loadHTMLStringTimer;
|
||||||
|
|
||||||
BOOL _drawsBackground;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CPRect)frameRect frameName:(CPString)frameName groupName:(CPString)groupName
|
- (id)initWithFrame:(CPRect)frameRect frameName:(CPString)frameName groupName:(CPString)groupName
|
||||||
@@ -132,24 +83,17 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
{
|
{
|
||||||
_iframe.name = frameName;
|
_iframe.name = frameName;
|
||||||
}
|
}
|
||||||
|
return self
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFrame:(CPRect)aFrame
|
- (id)initWithFrame:(CPRect)aFrame
|
||||||
{
|
{
|
||||||
if (self = [super initWithFrame:aFrame])
|
if (self = [super initWithFrame:aFrame])
|
||||||
{
|
{
|
||||||
_mainFrameURL = nil;
|
_mainFrameURL = nil;
|
||||||
_backwardStack = [];
|
_backwardStack = [];
|
||||||
_forwardStack = [];
|
_forwardStack = [];
|
||||||
_scrollMode = CPWebViewScrollAuto;
|
_scrollMode = CPWebViewScrollNative;
|
||||||
_contentIsAccessible = YES;
|
|
||||||
_isLoading = NO;
|
|
||||||
|
|
||||||
_drawsBackground = YES;
|
|
||||||
|
|
||||||
[self setBackgroundColor:[CPColor whiteColor]];
|
|
||||||
|
|
||||||
[self _initDOMWithFrame:aFrame];
|
[self _initDOMWithFrame:aFrame];
|
||||||
}
|
}
|
||||||
@@ -163,82 +107,75 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
_ignoreLoadEnd = YES;
|
_ignoreLoadEnd = YES;
|
||||||
|
|
||||||
_iframe = document.createElement("iframe");
|
_iframe = document.createElement("iframe");
|
||||||
_iframe.name = "iframe_" + FLOOR(RAND() * 10000);
|
_iframe.name = "iframe_" + Math.floor(Math.random()*10000);
|
||||||
_iframe.style.width = "100%";
|
_iframe.style.width = "100%";
|
||||||
_iframe.style.height = "100%";
|
_iframe.style.height = "100%";
|
||||||
_iframe.style.borderWidth = "0px";
|
_iframe.style.borderWidth = "0px";
|
||||||
_iframe.frameBorder = "0";
|
_iframe.frameBorder = "0";
|
||||||
|
|
||||||
[self _applyBackgroundColor];
|
[self setDrawsBackground:YES];
|
||||||
|
|
||||||
_loadCallback = function()
|
_loadCallback = function() {
|
||||||
{
|
// HACK: this block handles the case where we don't know about loads initiated by the user clicking a link
|
||||||
// HACK: this block handles the case where we don't know about loads initiated by the user clicking a link
|
if (!_ignoreLoadStart)
|
||||||
if (!_ignoreLoadStart)
|
{
|
||||||
{
|
// post the start load notification
|
||||||
// post the start load notification
|
[self _startedLoading];
|
||||||
[self _startedLoading];
|
|
||||||
|
|
||||||
if (_mainFrameURL)
|
if (_mainFrameURL)
|
||||||
[_backwardStack addObject:_mainFrameURL];
|
[_backwardStack addObject:_mainFrameURL];
|
||||||
|
|
||||||
// FIXME: this doesn't actually get the right URL for different domains. Not possible due to browser security restrictions.
|
// FIXME: this doesn't actually get the right URL for different domains. Not possible due to browser security restrictions.
|
||||||
|
_mainFrameURL = _iframe.src;
|
||||||
_mainFrameURL = _iframe.src;
|
_mainFrameURL = _iframe.src;
|
||||||
|
|
||||||
// clear the forward
|
// clear the forward
|
||||||
[_forwardStack removeAllObjects];
|
[_forwardStack removeAllObjects];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_ignoreLoadStart = NO;
|
_ignoreLoadStart = NO;
|
||||||
|
|
||||||
if (!_ignoreLoadEnd)
|
if (!_ignoreLoadEnd)
|
||||||
{
|
{
|
||||||
[self _finishedLoading];
|
[self _finishedLoading];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_ignoreLoadEnd = NO;
|
_ignoreLoadEnd = NO;
|
||||||
|
|
||||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_iframe.addEventListener)
|
||||||
|
_iframe.addEventListener("load", _loadCallback, false);
|
||||||
|
else if (_iframe.attachEvent)
|
||||||
|
_iframe.attachEvent("onload", _loadCallback);
|
||||||
|
|
||||||
if (_iframe.addEventListener)
|
|
||||||
_iframe.addEventListener("load", _loadCallback, false);
|
|
||||||
else if (_iframe.attachEvent)
|
|
||||||
_iframe.attachEvent("onload", _loadCallback);
|
|
||||||
|
|
||||||
_frameView = [[CPView alloc] initWithFrame:[self bounds]];
|
_frameView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||||
[_frameView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[_frameView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||||
|
|
||||||
_scrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
_scrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
||||||
[_scrollView setAutohidesScrollers:YES];
|
[_scrollView setAutohidesScrollers:YES];
|
||||||
[_scrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[_scrollView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||||
[_scrollView setDocumentView:_frameView];
|
[_scrollView setDocumentView:_frameView];
|
||||||
|
|
||||||
_frameView._DOMElement.appendChild(_iframe);
|
_frameView._DOMElement.appendChild(_iframe);
|
||||||
|
|
||||||
[self _updateEffectiveScrollMode];
|
[self _setScrollMode:_scrollMode];
|
||||||
|
|
||||||
[self addSubview:_scrollView];
|
[self addSubview:_scrollView];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setFrameSize:(CPSize)aSize
|
- (void)setFrameSize:(CPSize)aSize
|
||||||
{
|
{
|
||||||
[super setFrameSize:aSize];
|
[super setFrameSize:aSize];
|
||||||
[self _resizeWebFrame];
|
[self _resizeWebFrame];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidUnhide
|
|
||||||
{
|
|
||||||
// Sizing cannot properly happen while we're hidden because the iframe is inaccessible.
|
|
||||||
// So now that it is accessible again, make sure to catch up.
|
|
||||||
[_frameView setFrameSize:[_scrollView contentSize]];
|
|
||||||
[self _resizeWebFrame];
|
|
||||||
[self _scheduleContentSizeCheck];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_attachScrollEventIfNecessary
|
- (void)_attachScrollEventIfNecessary
|
||||||
{
|
{
|
||||||
if (_effectiveScrollMode !== CPWebViewScrollAppKit)
|
if (_scrollMode !== CPWebViewScrollAppKit)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var win = null;
|
var win = null;
|
||||||
@@ -263,130 +200,71 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
|
|
||||||
- (void)_resizeWebFrame
|
- (void)_resizeWebFrame
|
||||||
{
|
{
|
||||||
// When a webview is not in the DOM we can't inspect its contents for sizing information.
|
if (_scrollMode === CPWebViewScrollAppKit)
|
||||||
// If we try, we might end up setting the fallback frame size which will then become
|
|
||||||
// somewhat sticky.
|
|
||||||
if (![self _isVisible])
|
|
||||||
{
|
{
|
||||||
return;
|
if (_scrollSize)
|
||||||
}
|
|
||||||
|
|
||||||
if (_effectiveScrollMode === CPWebViewScrollAppKit)
|
|
||||||
{
|
|
||||||
var visibleRect = [_frameView visibleRect];
|
|
||||||
[_frameView setFrameSize:CGSizeMake(CGRectGetMaxX(visibleRect), CGRectGetMaxY(visibleRect))];
|
|
||||||
|
|
||||||
// try to get the document size so we can correctly set the frame
|
|
||||||
var win = null;
|
|
||||||
try { win = [self DOMWindow]; } catch (e) {}
|
|
||||||
|
|
||||||
if (win && win.document && win.document.body)
|
|
||||||
{
|
{
|
||||||
var width = win.document.body.scrollWidth,
|
[_frameView setFrameSize:_scrollSize];
|
||||||
height = win.document.body.scrollHeight;
|
|
||||||
|
|
||||||
_iframe.setAttribute("width", width);
|
|
||||||
_iframe.setAttribute("height", height);
|
|
||||||
|
|
||||||
[_frameView setFrameSize:CGSizeMake(width, height)];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If we do have access to the content, it might be that the 'body' element simply hasn't loaded yet.
|
var visibleRect = [_frameView visibleRect];
|
||||||
// The size will be updated by the content size timer in this case.
|
[_frameView setFrameSize:CGSizeMake(CGRectGetMaxX(visibleRect), CGRectGetMaxY(visibleRect))];
|
||||||
if (!win || !win.document)
|
|
||||||
|
// try to get the document size so we can correctly set the frame
|
||||||
|
var win = null;
|
||||||
|
try { win = [self DOMWindow]; } catch (e) {}
|
||||||
|
|
||||||
|
if (win && win.document && win.document.body)
|
||||||
|
{
|
||||||
|
var width = win.document.body.scrollWidth,
|
||||||
|
height = win.document.body.scrollHeight;
|
||||||
|
|
||||||
|
_iframe.setAttribute("width", width);
|
||||||
|
_iframe.setAttribute("height", height);
|
||||||
|
|
||||||
|
[_frameView setFrameSize:CGSizeMake(width, height)];
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
CPLog.warn("using default size 800*1600");
|
CPLog.warn("using default size 800*1600");
|
||||||
|
|
||||||
[_frameView setFrameSize:CGSizeMake(800, 1600)];
|
[_frameView setFrameSize:CGSizeMake(800, 1600)];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[_frameView scrollRectToVisible:visibleRect];
|
[_frameView scrollRectToVisible:visibleRect];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the scroll mode of the receiver.
|
|
||||||
|
|
||||||
Valid values are:
|
|
||||||
CPWebViewScrollAuto
|
|
||||||
CPWebViewScrollAppKit
|
|
||||||
CPWebViewScrollNative
|
|
||||||
CPWebViewScrollNone
|
|
||||||
|
|
||||||
CPWebViewScrollAuto
|
|
||||||
*/
|
|
||||||
- (void)setScrollMode:(int)aScrollMode
|
- (void)setScrollMode:(int)aScrollMode
|
||||||
{
|
{
|
||||||
if (_scrollMode == aScrollMode)
|
if (_scrollMode == aScrollMode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_scrollMode = aScrollMode;
|
[self _setScrollMode:aScrollMode];
|
||||||
|
|
||||||
[self _updateEffectiveScrollMode];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (void)_setScrollMode:(int)aScrollMode
|
||||||
Returns the effective scroll mode of the receiver.
|
|
||||||
|
|
||||||
Valied values are:
|
|
||||||
CPWebViewScrollAuto
|
|
||||||
CPWebViewScrollAppKit
|
|
||||||
CPWebViewScrollNative
|
|
||||||
CPWebViewScrollNone
|
|
||||||
*/
|
|
||||||
- (int)effectiveScrollMode
|
|
||||||
{
|
{
|
||||||
return _effectiveScrollMode;
|
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||||
}
|
_scrollMode = CPWebViewScrollNative;
|
||||||
|
else
|
||||||
- (void)_updateEffectiveScrollMode
|
_scrollMode = aScrollMode;
|
||||||
{
|
|
||||||
var _newScrollMode = CPWebViewScrollAppKit;
|
|
||||||
|
|
||||||
if (_scrollMode == CPWebViewScrollNative
|
|
||||||
|| (_scrollMode == CPWebViewScrollAuto && !_contentIsAccessible)
|
|
||||||
|| CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
|
||||||
{
|
|
||||||
_newScrollMode = CPWebViewScrollNative;
|
|
||||||
}
|
|
||||||
else if (_scrollMode == CPWebViewScrollAppKit && !_contentIsAccessible)
|
|
||||||
{
|
|
||||||
// Same behaviour as the previous case except that a warning is logged when AppKit
|
|
||||||
// scrollers can't be used.
|
|
||||||
CPLog.warn(self + " unable to use CPWebViewScrollAppKit scroll mode due to same origin policy.");
|
|
||||||
_newScrollMode = CPWebViewScrollNative;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_newScrollMode !== _effectiveScrollMode)
|
|
||||||
[self _setEffectiveScrollMode:_newScrollMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_setEffectiveScrollMode:(int)aScrollMode
|
|
||||||
{
|
|
||||||
_effectiveScrollMode = aScrollMode;
|
|
||||||
|
|
||||||
_ignoreLoadStart = YES;
|
_ignoreLoadStart = YES;
|
||||||
_ignoreLoadEnd = YES;
|
_ignoreLoadEnd = YES;
|
||||||
|
|
||||||
var parent = _iframe.parentNode;
|
var parent = _iframe.parentNode;
|
||||||
// FIXME "scrolling" can't be changed without readding the iframe. Unfortunately this causes a reload.
|
|
||||||
parent.removeChild(_iframe);
|
parent.removeChild(_iframe);
|
||||||
|
|
||||||
if (_effectiveScrollMode === CPWebViewScrollAppKit)
|
if (_scrollMode === CPWebViewScrollAppKit)
|
||||||
{
|
{
|
||||||
[_scrollView setHasHorizontalScroller:YES];
|
[_scrollView setHasHorizontalScroller:YES];
|
||||||
[_scrollView setHasVerticalScroller:YES];
|
[_scrollView setHasVerticalScroller:YES];
|
||||||
|
|
||||||
_iframe.setAttribute("scrolling", "no");
|
_iframe.setAttribute("scrolling", "no");
|
||||||
}
|
}
|
||||||
else if (_effectiveScrollMode === CPWebViewScrollNone)
|
|
||||||
{
|
|
||||||
[_scrollView setHasHorizontalScroller:NO];
|
|
||||||
[_scrollView setHasVerticalScroller:NO];
|
|
||||||
|
|
||||||
_iframe.setAttribute("scrolling", "no");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[_scrollView setHasHorizontalScroller:NO];
|
[_scrollView setHasHorizontalScroller:NO];
|
||||||
@@ -398,45 +276,27 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
}
|
}
|
||||||
|
|
||||||
parent.appendChild(_iframe);
|
parent.appendChild(_iframe);
|
||||||
[self _applyBackgroundColor];
|
|
||||||
|
|
||||||
[self _resizeWebFrame];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_maybePollWebFrameSize
|
|
||||||
{
|
|
||||||
if (CPWebViewAppKitScrollMaxPollCount == 0 || _contentSizePollCount++ < CPWebViewAppKitScrollMaxPollCount)
|
|
||||||
[self _resizeWebFrame];
|
|
||||||
else
|
|
||||||
[_contentSizeCheckTimer invalidate];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Loads a string of HTML into the receiver.
|
|
||||||
|
|
||||||
@param CPString - The string to load.
|
|
||||||
*/
|
|
||||||
- (void)loadHTMLString:(CPString)aString
|
- (void)loadHTMLString:(CPString)aString
|
||||||
{
|
{
|
||||||
[self loadHTMLString:aString baseURL:nil];
|
[self loadHTMLString:aString baseURL:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Loads a string of HTML into the receiver.
|
|
||||||
|
|
||||||
@param CPString - The string to load.
|
|
||||||
@param CPURL - The base url of the string. (not implemented)
|
|
||||||
*/
|
|
||||||
- (void)loadHTMLString:(CPString)aString baseURL:(CPURL)URL
|
- (void)loadHTMLString:(CPString)aString baseURL:(CPURL)URL
|
||||||
{
|
{
|
||||||
// FIXME: do something with baseURL?
|
// FIXME: do something with baseURL?
|
||||||
|
|
||||||
|
[self _setScrollMode:CPWebViewScrollAppKit];
|
||||||
|
|
||||||
[_frameView setFrameSize:[_scrollView contentSize]];
|
[_frameView setFrameSize:[_scrollView contentSize]];
|
||||||
|
|
||||||
[self _startedLoading];
|
[self _startedLoading];
|
||||||
|
|
||||||
_ignoreLoadStart = YES;
|
_ignoreLoadStart = YES;
|
||||||
|
_ignoreLoadEnd = NO;
|
||||||
|
|
||||||
_url = nil;
|
_url = null;
|
||||||
_html = aString;
|
_html = aString;
|
||||||
|
|
||||||
[self _load];
|
[self _load];
|
||||||
@@ -444,12 +304,15 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
|
|
||||||
- (void)_loadMainFrameURL
|
- (void)_loadMainFrameURL
|
||||||
{
|
{
|
||||||
|
[self _setScrollMode:CPWebViewScrollNative];
|
||||||
|
|
||||||
[self _startedLoading];
|
[self _startedLoading];
|
||||||
|
|
||||||
_ignoreLoadStart = YES;
|
_ignoreLoadStart = YES;
|
||||||
|
_ignoreLoadEnd = NO;
|
||||||
|
|
||||||
_url = _mainFrameURL;
|
_url = _mainFrameURL;
|
||||||
_html = nil;
|
_html = null;
|
||||||
|
|
||||||
[self _load];
|
[self _load];
|
||||||
}
|
}
|
||||||
@@ -458,26 +321,13 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
{
|
{
|
||||||
if (_url)
|
if (_url)
|
||||||
{
|
{
|
||||||
// Try to figure out if this URL will pass the same origin policy and hence allow us to potentially
|
|
||||||
// use appkit scrollbars.
|
|
||||||
var cpurl = [CPURL URLWithString:_url];
|
|
||||||
_contentIsAccessible = [cpurl _passesSameOriginPolicy];
|
|
||||||
[self _updateEffectiveScrollMode];
|
|
||||||
|
|
||||||
_ignoreLoadEnd = NO;
|
|
||||||
|
|
||||||
_iframe.src = _url;
|
_iframe.src = _url;
|
||||||
}
|
}
|
||||||
else if (_html !== nil)
|
else if (_html)
|
||||||
{
|
{
|
||||||
// clear the iframe
|
// clear the iframe
|
||||||
_iframe.src = "";
|
_iframe.src = "";
|
||||||
|
|
||||||
_contentIsAccessible = YES;
|
|
||||||
[self _updateEffectiveScrollMode];
|
|
||||||
|
|
||||||
_ignoreLoadEnd = NO;
|
|
||||||
|
|
||||||
if (_loadHTMLStringTimer !== nil)
|
if (_loadHTMLStringTimer !== nil)
|
||||||
{
|
{
|
||||||
window.clearTimeout(_loadHTMLStringTimer);
|
window.clearTimeout(_loadHTMLStringTimer);
|
||||||
@@ -489,11 +339,8 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
{
|
{
|
||||||
var win = [self DOMWindow];
|
var win = [self DOMWindow];
|
||||||
|
|
||||||
/*
|
|
||||||
If _html is the empty string, subtitute in an empty HTML structure. Just leaving the contents entirely empty prompts the browser to subtitute in a white page which would interfere with any custom background colours in use by this web view.
|
|
||||||
*/
|
|
||||||
if (win)
|
if (win)
|
||||||
win.document.write(_html || "<html><body></body></html>");
|
win.document.write(_html);
|
||||||
|
|
||||||
window.setTimeout(_loadCallback, 1);
|
window.setTimeout(_loadCallback, 1);
|
||||||
}, 0);
|
}, 0);
|
||||||
@@ -502,8 +349,6 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
|
|
||||||
- (void)_startedLoading
|
- (void)_startedLoading
|
||||||
{
|
{
|
||||||
_isLoading = YES;
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWebViewProgressStartedNotification object:self];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPWebViewProgressStartedNotification object:self];
|
||||||
|
|
||||||
if ([_frameLoadDelegate respondsToSelector:@selector(webView:didStartProvisionalLoadForFrame:)])
|
if ([_frameLoadDelegate respondsToSelector:@selector(webView:didStartProvisionalLoadForFrame:)])
|
||||||
@@ -512,69 +357,20 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
|
|
||||||
- (void)_finishedLoading
|
- (void)_finishedLoading
|
||||||
{
|
{
|
||||||
_isLoading = NO;
|
|
||||||
|
|
||||||
[self _resizeWebFrame];
|
[self _resizeWebFrame];
|
||||||
[self _attachScrollEventIfNecessary];
|
[self _attachScrollEventIfNecessary];
|
||||||
|
|
||||||
[self _scheduleContentSizeCheck];
|
|
||||||
|
|
||||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWebViewProgressFinishedNotification object:self];
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPWebViewProgressFinishedNotification object:self];
|
||||||
|
|
||||||
if ([_frameLoadDelegate respondsToSelector:@selector(webView:didFinishLoadForFrame:)])
|
if ([_frameLoadDelegate respondsToSelector:@selector(webView:didFinishLoadForFrame:)])
|
||||||
[_frameLoadDelegate webView:self didFinishLoadForFrame:nil]; // FIXME: give this a frame somehow?
|
[_frameLoadDelegate webView:self didFinishLoadForFrame:nil]; // FIXME: give this a frame somehow?
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_scheduleContentSizeCheck
|
|
||||||
{
|
|
||||||
[_contentSizeCheckTimer invalidate];
|
|
||||||
if (_effectiveScrollMode == CPWebViewScrollAppKit)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
FIXME Need better method.
|
|
||||||
We don't know when the content of the iframe changes size (e.g. a
|
|
||||||
picture finishes loading, dynamic content is loaded). Often when a
|
|
||||||
page has initially 'loaded', it does not yet have its final size. In
|
|
||||||
lieu of any resize events we will simply check back in a few times
|
|
||||||
some time after loading.
|
|
||||||
|
|
||||||
We run these checks only a limited number of times as to not deplete
|
|
||||||
battery life and slow down the software needlessly. This does mean
|
|
||||||
there are situations where the content changes size and the AppKit
|
|
||||||
scrollbars will be out of sync. Users who have dynamic content
|
|
||||||
in their web view will, for now, have to implement domain specific
|
|
||||||
fixes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
_contentSizePollCount = 0;
|
|
||||||
_contentSizeCheckTimer = [CPTimer scheduledTimerWithTimeInterval:CPWebViewAppKitScrollPollInterval target:self selector:@selector(_maybePollWebFrameSize) userInfo:nil repeats:YES];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns whether the web view is loading. Note: due to browser limitations
|
|
||||||
this value is not particularly reliable.
|
|
||||||
*/
|
|
||||||
- (BOOL)isLoading
|
|
||||||
{
|
|
||||||
return _isLoading;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the URL of the main frame.
|
|
||||||
|
|
||||||
@return CPString - The URL of the main frame.
|
|
||||||
*/
|
|
||||||
- (CPString)mainFrameURL
|
- (CPString)mainFrameURL
|
||||||
{
|
{
|
||||||
return _mainFrameURL;
|
return _mainFrameURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the URL of the main frame.
|
|
||||||
|
|
||||||
@param CPString - the url to set.
|
|
||||||
*/
|
|
||||||
- (void)setMainFrameURL:(CPString)URLString
|
- (void)setMainFrameURL:(CPString)URLString
|
||||||
{
|
{
|
||||||
if (_mainFrameURL)
|
if (_mainFrameURL)
|
||||||
@@ -585,11 +381,6 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
[self _loadMainFrameURL];
|
[self _loadMainFrameURL];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Tells the webview to navigate to the previous page.
|
|
||||||
|
|
||||||
@return BOOL - YES if the receiver was able to go back, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)goBack
|
- (BOOL)goBack
|
||||||
{
|
{
|
||||||
if (_backwardStack.length > 0)
|
if (_backwardStack.length > 0)
|
||||||
@@ -606,11 +397,6 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Tells the receiver to go forward in page history.
|
|
||||||
|
|
||||||
@return - YES if the receiver was able to go forward, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)goForward
|
- (BOOL)goForward
|
||||||
{
|
{
|
||||||
if (_forwardStack.length > 0)
|
if (_forwardStack.length > 0)
|
||||||
@@ -627,23 +413,11 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Checks to see if the webview has a history stack you can navigate back
|
|
||||||
through.
|
|
||||||
|
|
||||||
@return BOOL - YES if the receiver can navigate backward through history, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)canGoBack
|
- (BOOL)canGoBack
|
||||||
{
|
{
|
||||||
return (_backwardStack.length > 0);
|
return (_backwardStack.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Checks to see if the webview has a history stack you can navigate forward
|
|
||||||
through.
|
|
||||||
|
|
||||||
@return BOOL - YES if the receiver can navigate forward through history, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)canGoForward
|
- (BOOL)canGoForward
|
||||||
{
|
{
|
||||||
return (_forwardStack.length > 0);
|
return (_forwardStack.length > 0);
|
||||||
@@ -655,30 +429,16 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
return { back: _backwardStack, forward: _forwardStack };
|
return { back: _backwardStack, forward: _forwardStack };
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Closes the webview by unloading the webpage. The webview will no longer
|
|
||||||
respond to load requests or delegate methods once this is called.
|
|
||||||
*/
|
|
||||||
- (void)close
|
- (void)close
|
||||||
{
|
{
|
||||||
_iframe.parentNode.removeChild(_iframe);
|
_iframe.parentNode.removeChild(_iframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the window object of the webview.
|
|
||||||
|
|
||||||
@return DOMWindow - The window object.
|
|
||||||
*/
|
|
||||||
- (DOMWindow)DOMWindow
|
- (DOMWindow)DOMWindow
|
||||||
{
|
{
|
||||||
return (_iframe.contentDocument && _iframe.contentDocument.defaultView) || _iframe.contentWindow;
|
return (_iframe.contentDocument && _iframe.contentDocument.defaultView) || _iframe.contentWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the root Object of the webview as a CPWebScriptObject.
|
|
||||||
|
|
||||||
@return CPWebScriptObject - the Object of the webview.
|
|
||||||
*/
|
|
||||||
- (CPWebScriptObject)windowScriptObject
|
- (CPWebScriptObject)windowScriptObject
|
||||||
{
|
{
|
||||||
var win = [self DOMWindow];
|
var win = [self DOMWindow];
|
||||||
@@ -692,37 +452,17 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
return _wso;
|
return _wso;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Evaluates a javascript string in the webview and returns the result of
|
|
||||||
that evaluation as a string.
|
|
||||||
|
|
||||||
@param script - A string of javascript.
|
|
||||||
@return CPString - The result of the evaluation.
|
|
||||||
*/
|
|
||||||
- (CPString)stringByEvaluatingJavaScriptFromString:(CPString)script
|
- (CPString)stringByEvaluatingJavaScriptFromString:(CPString)script
|
||||||
{
|
{
|
||||||
var result = [self objectByEvaluatingJavaScriptFromString:script];
|
var result = [self objectByEvaluatingJavaScriptFromString:script];
|
||||||
return result ? String(result) : nil;
|
return result ? String(result) : nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Evaluates a string of javascript in the webview and returns the result.
|
|
||||||
|
|
||||||
@param script - A string of javascript.
|
|
||||||
@return JSObject - A JSObject resulting from the evaluation.
|
|
||||||
*/
|
|
||||||
- (JSObject)objectByEvaluatingJavaScriptFromString:(CPString)script
|
- (JSObject)objectByEvaluatingJavaScriptFromString:(CPString)script
|
||||||
{
|
{
|
||||||
return [[self windowScriptObject] evaluateWebScript:script];
|
return [[self windowScriptObject] evaluateWebScript:script];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Gets the computed style for an element.
|
|
||||||
|
|
||||||
@param DOMElement - An Element.
|
|
||||||
@param pseudoElement - A pseudoElement.
|
|
||||||
@return DOMCSSStyleDeclaration - The computed style for an element.
|
|
||||||
*/
|
|
||||||
- (DOMCSSStyleDeclaration)computedStyleForElement:(DOMElement)element pseudoElement:(CPString)pseudoElement
|
- (DOMCSSStyleDeclaration)computedStyleForElement:(DOMElement)element pseudoElement:(CPString)pseudoElement
|
||||||
{
|
{
|
||||||
var win = [[self windowScriptObject] window];
|
var win = [[self windowScriptObject] window];
|
||||||
@@ -735,114 +475,47 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@return BOOL - YES if the webview draws its own background, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)drawsBackground
|
- (BOOL)drawsBackground
|
||||||
{
|
{
|
||||||
return _drawsBackground;
|
return _iframe.style.backgroundColor != "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (void)setDrawsBackground:(BOOL)drawsBackround
|
||||||
Sets whether the webview draws its own background when the displayed contents do not.
|
|
||||||
|
|
||||||
If you are trying to create a transparent iframe:
|
|
||||||
|
|
||||||
A) call this method with NO
|
|
||||||
A) call setBackground:[CPColor transparent]
|
|
||||||
B) ensure the content does not draw a background, e.g. it has <body style="background-color: transparent;">
|
|
||||||
|
|
||||||
@param BOOL - YES if the webview should draw its background, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setDrawsBackground:(BOOL)drawsBackground
|
|
||||||
{
|
{
|
||||||
if (drawsBackground == _drawsBackground)
|
_iframe.style.backgroundColor = drawsBackround ? "white" : "";
|
||||||
return;
|
|
||||||
_drawsBackground = drawsBackground;
|
|
||||||
|
|
||||||
[self _applyBackgroundColor];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setBackgroundColor:(CPColor)aColor
|
|
||||||
{
|
|
||||||
[super setBackgroundColor:aColor];
|
|
||||||
[self _applyBackgroundColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_applyBackgroundColor
|
|
||||||
{
|
|
||||||
if (_iframe)
|
|
||||||
{
|
|
||||||
var bgColor = [self backgroundColor] || [CPColor whiteColor];
|
|
||||||
_iframe.allowtransparency = !_drawsBackground;
|
|
||||||
_iframe.style.backgroundColor = _drawsBackground ? [bgColor cssString] : "transparent";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IBActions
|
// IBActions
|
||||||
|
|
||||||
/*!
|
- (IBAction)takeStringURLFrom:(id)sender
|
||||||
Used with the target/action mechanism to automatically set the webviews
|
|
||||||
mainFrameURL to the senders stringValue.
|
|
||||||
|
|
||||||
@param sender - the sender of the action. Should respond to -stringValue.
|
|
||||||
*/
|
|
||||||
- (@action)takeStringURLFrom:(id)sender
|
|
||||||
{
|
{
|
||||||
[self setMainFrameURL:[sender stringValue]];
|
[self setMainFrameURL:[sender stringValue]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (IBAction)goBack:(id)sender
|
||||||
Same as -goBack but takes a sender as a param.
|
|
||||||
|
|
||||||
@param sender - the sender of the action.
|
|
||||||
*/
|
|
||||||
- (@action)goBack:(id)sender
|
|
||||||
{
|
{
|
||||||
[self goBack];
|
[self goBack];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (IBAction)goForward:(id)sender
|
||||||
Same as -goForward but takes a sender as a param.
|
|
||||||
|
|
||||||
@param sender - the sender of the action.
|
|
||||||
*/
|
|
||||||
- (@action)goForward:(id)sender
|
|
||||||
{
|
{
|
||||||
[self goForward];
|
[self goForward];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (IBAction)stopLoading:(id)sender
|
||||||
Stops loading the webview. (not yet implemented)
|
|
||||||
|
|
||||||
@param sender - the sender of the action.
|
|
||||||
*/
|
|
||||||
- (@action)stopLoading:(id)sender
|
|
||||||
{
|
{
|
||||||
// FIXME: what to do?
|
// FIXME: what to do?
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (IBAction)reload:(id)sender
|
||||||
Reloads the webview.
|
|
||||||
|
|
||||||
@param sender - the sender of the action.
|
|
||||||
*/
|
|
||||||
- (@action)reload:(id)sender
|
|
||||||
{
|
{
|
||||||
// If we're displaying pure HTML, redisplay it.
|
[self _loadMainFrameURL];
|
||||||
if(!_url && (_html !== nil))
|
|
||||||
[self loadHTMLString:_html];
|
|
||||||
else
|
|
||||||
[self _loadMainFrameURL];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
- (IBAction)print:(id)sender
|
||||||
Tells the webview to print. If the webview is unable to print due to
|
|
||||||
browser restrictions the user is alerted to print from the file menu.
|
|
||||||
|
|
||||||
@param sender - the sender of the receiver.
|
|
||||||
*/
|
|
||||||
- (@action)print:(id)sender
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -902,19 +575,12 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/*!
|
|
||||||
@class CPWebScriptObject
|
|
||||||
|
|
||||||
A CPWebScriptObject is an Objective-J wrapper around a scripting object.
|
|
||||||
*/
|
|
||||||
@implementation CPWebScriptObject : CPObject
|
@implementation CPWebScriptObject : CPObject
|
||||||
{
|
{
|
||||||
Window _window;
|
Window _window;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Initializes the scripting object with the scripting Window object.
|
|
||||||
*/
|
|
||||||
- (id)initWithWindow:(Window)aWindow
|
- (id)initWithWindow:(Window)aWindow
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
@@ -924,12 +590,6 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Call a method with arguments on the receiver.
|
|
||||||
|
|
||||||
@param methodName - The method that should be called.
|
|
||||||
@param args - An array of arguments to pass to the method call.
|
|
||||||
*/
|
|
||||||
- (id)callWebScriptMethod:(CPString)methodName withArguments:(CPArray)args
|
- (id)callWebScriptMethod:(CPString)methodName withArguments:(CPArray)args
|
||||||
{
|
{
|
||||||
// Would using "with" be better here?
|
// Would using "with" be better here?
|
||||||
@@ -943,25 +603,15 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Evaluates a string of javascript and returns the result.
|
|
||||||
|
|
||||||
@param script - The script to run.
|
|
||||||
@return - The result of the evaluation, which may be 'undefined'.
|
|
||||||
*/
|
|
||||||
- (id)evaluateWebScript:(CPString)script
|
- (id)evaluateWebScript:(CPString)script
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return _window.eval(script);
|
return _window.eval(script);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// FIX ME: if we fail inside here, shouldn't we return an exception?
|
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the receivers Window object.
|
|
||||||
*/
|
|
||||||
- (Window)window
|
- (Window)window
|
||||||
{
|
{
|
||||||
return _window;
|
return _window;
|
||||||
@@ -974,7 +624,6 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Initializes the web view from the data in a coder.
|
Initializes the web view from the data in a coder.
|
||||||
|
|
||||||
@param aCoder the coder from which to read the data
|
@param aCoder the coder from which to read the data
|
||||||
@return the initialized web view
|
@return the initialized web view
|
||||||
*/
|
*/
|
||||||
@@ -988,16 +637,13 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
_mainFrameURL = nil;
|
_mainFrameURL = nil;
|
||||||
_backwardStack = [];
|
_backwardStack = [];
|
||||||
_forwardStack = [];
|
_forwardStack = [];
|
||||||
_scrollMode = CPWebViewScrollAuto;
|
_scrollMode = CPWebViewScrollNative;
|
||||||
|
|
||||||
#if PLATFORM(DOM)
|
#if PLATFORM(DOM)
|
||||||
[self _initDOMWithFrame:[self frame]];
|
[self _initDOMWithFrame:[self frame]];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (![self backgroundColor])
|
[self setBackgroundColor:[CPColor whiteColor]];
|
||||||
[self setBackgroundColor:[CPColor whiteColor]];
|
|
||||||
|
|
||||||
[self _updateEffectiveScrollMode];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -1005,7 +651,6 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Writes out the web view's instance information to a coder.
|
Writes out the web view's instance information to a coder.
|
||||||
|
|
||||||
@param aCoder the coder to which to write the data
|
@param aCoder the coder to which to write the data
|
||||||
*/
|
*/
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||||
@@ -1017,22 +662,3 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPURL(SOP)
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Private API.
|
|
||||||
|
|
||||||
Returns YES if the receiver URL would pass the same origin policy if loaded by the current runtime environment.
|
|
||||||
|
|
||||||
Since Cappuccino runs in the browser, it is often restricted from accessing URLs from outside of its own origin (protocol, domain, port). This method tries to determine if the URL represented by the receiver is of the same origin, or if other circumstances will allow the content at the URL to be interacted with.
|
|
||||||
*/
|
|
||||||
- (BOOL)_passesSameOriginPolicy
|
|
||||||
{
|
|
||||||
var documentURL = [CPURL URLWithString:window.location.href];
|
|
||||||
if ([documentURL isFileURL] && CPFeatureIsCompatible(CPSOPDisabledFromFileURLs))
|
|
||||||
return YES;
|
|
||||||
return ([documentURL scheme] == [self scheme] && [documentURL host] == [self host] && [documentURL port] == [self port]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|||||||
+104
-275
@@ -193,24 +193,9 @@ var SHADOW_MARGIN_LEFT = 20.0,
|
|||||||
_CPWindowShadowColor = nil;
|
_CPWindowShadowColor = nil;
|
||||||
|
|
||||||
var CPWindowSaveImage = nil,
|
var CPWindowSaveImage = nil,
|
||||||
CPWindowSavingImage = nil,
|
CPWindowSavingImage = nil;
|
||||||
|
|
||||||
CPWindowResizeTime = 0.2;
|
var CPWindowResizeTime = 0.2;
|
||||||
|
|
||||||
/*
|
|
||||||
Keys for which action messages will be sent by default when unhandled, e.g. complete:.
|
|
||||||
*/
|
|
||||||
var CPWindowActionMessageKeys = [
|
|
||||||
CPLeftArrowFunctionKey,
|
|
||||||
CPRightArrowFunctionKey,
|
|
||||||
CPUpArrowFunctionKey,
|
|
||||||
CPDownArrowFunctionKey,
|
|
||||||
CPPageUpFunctionKey,
|
|
||||||
CPPageDownFunctionKey,
|
|
||||||
CPHomeFunctionKey,
|
|
||||||
CPEndFunctionKey,
|
|
||||||
CPEscapeFunctionKey
|
|
||||||
];
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@@ -271,7 +256,6 @@ var CPWindowActionMessageKeys = [
|
|||||||
BOOL _isAnimating;
|
BOOL _isAnimating;
|
||||||
BOOL _hasShadow;
|
BOOL _hasShadow;
|
||||||
BOOL _isMovableByWindowBackground;
|
BOOL _isMovableByWindowBackground;
|
||||||
BOOL _isMovable;
|
|
||||||
unsigned _shadowStyle;
|
unsigned _shadowStyle;
|
||||||
BOOL _showsResizeIndicator;
|
BOOL _showsResizeIndicator;
|
||||||
|
|
||||||
@@ -404,8 +388,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
_registeredDraggedTypes = [CPSet set];
|
_registeredDraggedTypes = [CPSet set];
|
||||||
_registeredDraggedTypesArray = [];
|
_registeredDraggedTypesArray = [];
|
||||||
_isSheet = NO;
|
_isSheet = NO;
|
||||||
_acceptsMouseMovedEvents = YES;
|
|
||||||
_isMovable = YES;
|
|
||||||
|
|
||||||
// Set up our window number.
|
// Set up our window number.
|
||||||
_windowNumber = [CPApp._windows count];
|
_windowNumber = [CPApp._windows count];
|
||||||
@@ -428,7 +410,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
|
|
||||||
// Create a generic content view.
|
// Create a generic content view.
|
||||||
[self setContentView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
|
[self setContentView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
|
||||||
[self setInitialFirstResponder:[self contentView]];
|
|
||||||
|
|
||||||
_firstResponder = self;
|
_firstResponder = self;
|
||||||
|
|
||||||
@@ -469,27 +450,12 @@ CPTexturedBackgroundWindowMask
|
|||||||
return _platformWindow;
|
return _platformWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the platform window of the reciver.
|
|
||||||
This method will first close the reciever,
|
|
||||||
change the platform window, then reopen the window (if it was originally open).
|
|
||||||
*/
|
|
||||||
- (void)setPlatformWindow:(CPPlatformWindow)aPlatformWindow
|
- (void)setPlatformWindow:(CPPlatformWindow)aPlatformWindow
|
||||||
{
|
{
|
||||||
var wasVisible = [self isVisible];
|
// FIXME: already visible.
|
||||||
|
|
||||||
// we have to close it first, otherwise we get a DOM exception.
|
|
||||||
if (wasVisible)
|
|
||||||
[self close];
|
|
||||||
|
|
||||||
_platformWindow = aPlatformWindow;
|
_platformWindow = aPlatformWindow;
|
||||||
[_platformWindow _setTitle:_title window:self];
|
|
||||||
|
|
||||||
if (wasVisible)
|
|
||||||
[self orderFront:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ignore
|
@ignore
|
||||||
*/
|
*/
|
||||||
@@ -557,12 +523,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the receiver as a full platform window. If you pass YES the CPWindow instance will fill the entire browser content area,
|
|
||||||
otherwise the CPWindow will be a window inside of your browser window which the user can drag around, and resize (if you allow).
|
|
||||||
|
|
||||||
@param BOOL - YES if the window should fill the browser window, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setFullPlatformWindow:(BOOL)shouldBeFullPlatformWindow
|
- (void)setFullPlatformWindow:(BOOL)shouldBeFullPlatformWindow
|
||||||
{
|
{
|
||||||
if (![_platformWindow supportsFullPlatformWindows])
|
if (![_platformWindow supportsFullPlatformWindows])
|
||||||
@@ -603,9 +563,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@return BOOL - YES if the CPWindow fills the browser window, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (BOOL)isFullPlatformWindow
|
- (BOOL)isFullPlatformWindow
|
||||||
{
|
{
|
||||||
return _isFullPlatformWindow;
|
return _isFullPlatformWindow;
|
||||||
@@ -621,18 +578,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the frame rectangle used by a window.
|
Returns the frame rectangle used by a window.
|
||||||
Style masks include:
|
|
||||||
<pre>
|
|
||||||
CPBorderlessWindowMask
|
|
||||||
CPTitledWindowMask
|
|
||||||
CPClosableWindowMask
|
|
||||||
CPMiniaturizableWindowMask (NOTE: only available in NativeHost)
|
|
||||||
CPResizableWindowMask
|
|
||||||
CPTexturedBackgroundWindowMask
|
|
||||||
CPBorderlessBridgeWindowMask
|
|
||||||
CPHUDBackgroundWindowMask
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
@param aContentRect the content rectangle of the window
|
@param aContentRect the content rectangle of the window
|
||||||
@param aStyleMask the style mask of the window
|
@param aStyleMask the style mask of the window
|
||||||
@return the matching window's frame rectangle
|
@return the matching window's frame rectangle
|
||||||
@@ -674,7 +619,7 @@ CPTexturedBackgroundWindowMask
|
|||||||
the resize operation, and redraw itself if necessary.
|
the resize operation, and redraw itself if necessary.
|
||||||
@param aFrame the new size and location for the window
|
@param aFrame the new size and location for the window
|
||||||
@param shouldDisplay whether the window should redraw its views
|
@param shouldDisplay whether the window should redraw its views
|
||||||
@param shouldAnimate whether the window resize should be animated.
|
@param shouldAnimate whether the window resize should be animated
|
||||||
*/
|
*/
|
||||||
- (void)_setClippedFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
- (void)_setClippedFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
||||||
{
|
{
|
||||||
@@ -683,13 +628,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
[self setFrame:aFrame display:shouldDisplay animate:shouldAnimate];
|
[self setFrame:aFrame display:shouldDisplay animate:shouldAnimate];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the frame of the window.
|
|
||||||
|
|
||||||
@param aFrame - A CGRect of the new frame for the receiver.
|
|
||||||
@param shouldDisplay - YES if the window should call setNeedsDisplay otherwise NO.
|
|
||||||
@param shouldAnimate - YES if the window should animate to it's new size and position, otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
||||||
{
|
{
|
||||||
aFrame = _CGRectMakeCopy(aFrame);
|
aFrame = _CGRectMakeCopy(aFrame);
|
||||||
@@ -767,11 +705,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the window's frame rect.
|
|
||||||
@param aFrame - The new CGRect of the window.
|
|
||||||
@param shouldDisplay - YES if the window should call setNeedsDisplay: otherwise NO.
|
|
||||||
*/
|
|
||||||
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay
|
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay
|
||||||
{
|
{
|
||||||
[self _setClippedFrame:aFrame display:shouldDisplay animate:NO];
|
[self _setClippedFrame:aFrame display:shouldDisplay animate:NO];
|
||||||
@@ -779,7 +712,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the window's frame rectangle
|
Sets the window's frame rectangle
|
||||||
@param aFrame - The CGRect of the windows new frame
|
|
||||||
*/
|
*/
|
||||||
- (void)setFrame:(CGRect)aFrame
|
- (void)setFrame:(CGRect)aFrame
|
||||||
{
|
{
|
||||||
@@ -953,12 +885,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
|
|
||||||
var bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame));
|
var bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame));
|
||||||
|
|
||||||
// During init the initial first responder is set to the contentView
|
|
||||||
// if it hasn't changed in the mean time we need to update that reference
|
|
||||||
// to the new contentView
|
|
||||||
if ([self initialFirstResponder] === _contentView)
|
|
||||||
[self setInitialFirstResponder:aView];
|
|
||||||
|
|
||||||
_contentView = aView;
|
_contentView = aView;
|
||||||
[_contentView setFrame:[self contentRectForFrameRect:bounds]];
|
[_contentView setFrame:[self contentRectForFrameRect:bounds]];
|
||||||
|
|
||||||
@@ -1011,7 +937,7 @@ CPTexturedBackgroundWindowMask
|
|||||||
/*!
|
/*!
|
||||||
Sets the window's minimum size. If the provided
|
Sets the window's minimum size. If the provided
|
||||||
size is the same as the current minimum size, the method simply returns.
|
size is the same as the current minimum size, the method simply returns.
|
||||||
@param aSize the new minimum size for the window
|
@aSize the new minimum size for the window
|
||||||
*/
|
*/
|
||||||
- (void)setMinSize:(CGSize)aSize
|
- (void)setMinSize:(CGSize)aSize
|
||||||
{
|
{
|
||||||
@@ -1169,17 +1095,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
[self _updateShadow];
|
[self _updateShadow];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the shadow style of the receiver.
|
|
||||||
Values are:
|
|
||||||
<pre>
|
|
||||||
CPWindowShadowStyleStandard
|
|
||||||
CPWindowShadowStyleMenu
|
|
||||||
CPWindowShadowStylePanel
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
@param aStyle - The new shadow style of the receiver.
|
|
||||||
*/
|
|
||||||
- (void)setShadowStyle:(unsigned)aStyle
|
- (void)setShadowStyle:(unsigned)aStyle
|
||||||
{
|
{
|
||||||
_shadowStyle = aStyle;
|
_shadowStyle = aStyle;
|
||||||
@@ -1283,17 +1198,17 @@ CPTexturedBackgroundWindowMask
|
|||||||
|
|
||||||
- (BOOL)acceptsFirstResponder
|
- (BOOL)acceptsFirstResponder
|
||||||
{
|
{
|
||||||
return NO;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CPView)initialFirstResponder
|
- (id)initialFirstResponder
|
||||||
{
|
{
|
||||||
return _initialFirstResponder;
|
return _initialFirstResponder;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setInitialFirstResponder:(CPView)aView
|
- (void)setInitialFirstResponder:(id)aResponder
|
||||||
{
|
{
|
||||||
_initialFirstResponder = aView;
|
_initialFirstResponder = aResponder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -1308,10 +1223,10 @@ CPTexturedBackgroundWindowMask
|
|||||||
if (_firstResponder === aResponder)
|
if (_firstResponder === aResponder)
|
||||||
return YES;
|
return YES;
|
||||||
|
|
||||||
if (![_firstResponder resignFirstResponder])
|
if(![_firstResponder resignFirstResponder])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (!aResponder || ![aResponder acceptsFirstResponder] || ![aResponder becomeFirstResponder])
|
if(!aResponder || ![aResponder acceptsFirstResponder] || ![aResponder becomeFirstResponder])
|
||||||
{
|
{
|
||||||
_firstResponder = self;
|
_firstResponder = self;
|
||||||
|
|
||||||
@@ -1369,7 +1284,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
_title = aTitle;
|
_title = aTitle;
|
||||||
|
|
||||||
[_windowView setTitle:aTitle];
|
[_windowView setTitle:aTitle];
|
||||||
[_platformWindow _setTitle:_title window:self];
|
|
||||||
|
|
||||||
[self _synchronizeMenuBarTitleWithWindowTitle];
|
[self _synchronizeMenuBarTitleWithWindowTitle];
|
||||||
}
|
}
|
||||||
@@ -1440,23 +1354,6 @@ CPTexturedBackgroundWindowMask
|
|||||||
return _isMovableByWindowBackground;
|
return _isMovableByWindowBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets whether the window can be moved.
|
|
||||||
@param shouldBeMovable \c YES makes the window movable.
|
|
||||||
*/
|
|
||||||
- (void)setMovable:(BOOL)shouldBeMovable
|
|
||||||
{
|
|
||||||
_isMovable = shouldBeMovable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c YES if the window can be moved.
|
|
||||||
*/
|
|
||||||
- (void)isMovable
|
|
||||||
{
|
|
||||||
return _isMovable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the window location to be the center of the screen
|
Sets the window location to be the center of the screen
|
||||||
*/
|
*/
|
||||||
@@ -1718,7 +1615,7 @@ CPTexturedBackgroundWindowMask
|
|||||||
@param aLocation the lower-left corner coordinate of \c anImage
|
@param aLocation the lower-left corner coordinate of \c anImage
|
||||||
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
||||||
@param anEvent the \c -mouseDown: that triggered the drag
|
@param anEvent the \c -mouseDown: that triggered the drag
|
||||||
@param aPasteboard the pasteboard that holds the drag data
|
@param aPastebaord the pasteboard that holds the drag data
|
||||||
@param aSourceObject the drag operation controller
|
@param aSourceObject the drag operation controller
|
||||||
@param slideBack Whether the image should 'slide back' if the drag is rejected
|
@param slideBack Whether the image should 'slide back' if the drag is rejected
|
||||||
*/
|
*/
|
||||||
@@ -1755,7 +1652,7 @@ CPTexturedBackgroundWindowMask
|
|||||||
@param aLocation the lower-left corner coordinate of \c aView
|
@param aLocation the lower-left corner coordinate of \c aView
|
||||||
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
@param mouseOffset the distance from the \c -mouseDown: location and the current location
|
||||||
@param anEvent the \c -mouseDown: that triggered the drag
|
@param anEvent the \c -mouseDown: that triggered the drag
|
||||||
@param aPasteboard the pasteboard that holds the drag data
|
@param aPastebaord the pasteboard that holds the drag data
|
||||||
@param aSourceObject the drag operation controller
|
@param aSourceObject the drag operation controller
|
||||||
@param slideBack Whether the view should 'slide back' if the drag is rejected
|
@param slideBack Whether the view should 'slide back' if the drag is rejected
|
||||||
*/
|
*/
|
||||||
@@ -1906,7 +1803,7 @@ CPTexturedBackgroundWindowMask
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Restores a minimized window to it's original size.
|
Restores a mimized window to it's original size.
|
||||||
*/
|
*/
|
||||||
- (void)deminiaturize:(id)sender
|
- (void)deminiaturize:(id)sender
|
||||||
{
|
{
|
||||||
@@ -2258,7 +2155,7 @@ CPTexturedBackgroundWindowMask
|
|||||||
aSheet._isSheet = YES;
|
aSheet._isSheet = YES;
|
||||||
aSheet._parentView = self;
|
aSheet._parentView = self;
|
||||||
|
|
||||||
var originx = frame.origin.x + FLOOR((frame.size.width - sheetFrame.size.width) / 2),
|
var originx = frame.origin.x + FLOOR((frame.size.width - sheetFrame.size.width)/2),
|
||||||
originy = frame.origin.y + [[self contentView] frame].origin.y,
|
originy = frame.origin.y + [[self contentView] frame].origin.y,
|
||||||
startFrame = CGRectMake(originx, originy, sheetFrame.size.width, 0),
|
startFrame = CGRectMake(originx, originy, sheetFrame.size.width, 0),
|
||||||
endFrame = CGRectMake(originx, originy, sheetFrame.size.width, sheetFrame.size.height);
|
endFrame = CGRectMake(originx, originy, sheetFrame.size.width, sheetFrame.size.height);
|
||||||
@@ -2419,24 +2316,23 @@ CPTexturedBackgroundWindowMask
|
|||||||
// an event going of the responder chain is passed to the input system as a last resort.
|
// an event going of the responder chain is passed to the input system as a last resort.
|
||||||
// However, the only methods I could get Cocoa to call automatically are
|
// However, the only methods I could get Cocoa to call automatically are
|
||||||
// moveUp: moveDown: moveLeft: moveRight: pageUp: pageDown: and complete:
|
// moveUp: moveDown: moveLeft: moveRight: pageUp: pageDown: and complete:
|
||||||
// Unhandled events just travel further up the responder chain _past_ the window.
|
[self _processKeyboardUIKey:anEvent];
|
||||||
if (![self _processKeyboardUIKey:anEvent])
|
|
||||||
[super keyDown:anEvent];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ignore
|
@ignore
|
||||||
Interprets the key event for action messages and sends the action message down the responder chain
|
Interprets the key event for action messages and sends the action message down the responder chain
|
||||||
Cocoa only sends moveDown:, moveUp:, moveLeft:, moveRight:, pageUp:, pageDown: and complete: messages.
|
Cocoa only sends moveDown:, moveUp:, moveLeft:, moveRight:, pageUp:, pageDown: and complete: messages.
|
||||||
We deviate from this by sending (the default) scrollPageUp:, scrollPageDown:, scrollToBeginningOfDocument: and scrollToEndOfDocument: for pageUp, pageDown, home and end keys.
|
We deviate from this by sending (the default) scrollPageUp: scrollPageDown: for pageUp and pageDown keys.
|
||||||
@param anEvent the event to handle.
|
@param anEvent the event to handle.
|
||||||
@return YES if the key event was handled, NO if no responder handled the key event
|
@return YES if the key event was handled, NO if no responder handled the key event
|
||||||
*/
|
*/
|
||||||
- (BOOL)_processKeyboardUIKey:(CPEvent)anEvent
|
- (BOOL)_processKeyboardUIKey:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
var character = [anEvent charactersIgnoringModifiers];
|
var character = [anEvent charactersIgnoringModifiers],
|
||||||
|
uiKeys = [CPLeftArrowFunctionKey, CPRightArrowFunctionKey, CPUpArrowFunctionKey, CPDownArrowFunctionKey, CPPageUpFunctionKey, CPPageDownFunctionKey, CPEscapeFunctionKey];
|
||||||
|
|
||||||
if (![CPWindowActionMessageKeys containsObject:character])
|
if (![uiKeys containsObject:character])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
var selectors = [CPKeyBinding selectorsForKey:character modifierFlags:0];
|
var selectors = [CPKeyBinding selectorsForKey:character modifierFlags:0];
|
||||||
@@ -2456,8 +2352,49 @@ CPTexturedBackgroundWindowMask
|
|||||||
// The difference is that doCommandBySelector: will also send the action to the window and application delegates.
|
// The difference is that doCommandBySelector: will also send the action to the window and application delegates.
|
||||||
[[self firstResponder] doCommandBySelector:@selector(complete:)];
|
[[self firstResponder] doCommandBySelector:@selector(complete:)];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NO;
|
/*
|
||||||
|
@ignore
|
||||||
|
Interprets the key event for action messages and sends the action message down the responder chain
|
||||||
|
Cocoa only sends moveDown:, moveUp:, moveLeft:, moveRight:, pageUp:, pageDown: and complete: messages.
|
||||||
|
We deviate from this by sending (the default) scrollPageUp: scrollPageDown: for pageUp and pageDown keys.
|
||||||
|
@param anEvent the event to handle.
|
||||||
|
@return YES if the key event was handled, NO if no responder handled the key event
|
||||||
|
*/
|
||||||
|
- (BOOL)_processKeyboardUIKey:(CPEvent)anEvent
|
||||||
|
{
|
||||||
|
var character = [anEvent charactersIgnoringModifiers],
|
||||||
|
uiKeys = [
|
||||||
|
CPLeftArrowFunctionKey,
|
||||||
|
CPRightArrowFunctionKey,
|
||||||
|
CPUpArrowFunctionKey,
|
||||||
|
CPDownArrowFunctionKey,
|
||||||
|
CPPageUpFunctionKey,
|
||||||
|
CPPageDownFunctionKey,
|
||||||
|
CPEscapeFunctionKey
|
||||||
|
];
|
||||||
|
|
||||||
|
if (![uiKeys containsObject:character])
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
var selectors = [CPKeyBinding selectorsForKey:character modifierFlags:0];
|
||||||
|
|
||||||
|
if ([selectors count] <= 0)
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
if (character !== CPEscapeFunctionKey)
|
||||||
|
{
|
||||||
|
var selector = [selectors objectAtIndex:0];
|
||||||
|
return [[self firstResponder] tryToPerform:selector with:self];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Cocoa sends complete: for the escape key (in stead of the default cancelOperation:)
|
||||||
|
// This is also the only action that is not sent directly to the first responder, but through doCommandBySelector.
|
||||||
|
// The difference is that doCommandBySelector: will also send the action to the window and application delegates.
|
||||||
|
[[self firstResponder] doCommandBySelector:@selector(complete:)];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_dirtyKeyViewLoop
|
- (void)_dirtyKeyViewLoop
|
||||||
@@ -2468,31 +2405,45 @@ CPTexturedBackgroundWindowMask
|
|||||||
|
|
||||||
- (BOOL)_hasKeyViewLoop
|
- (BOOL)_hasKeyViewLoop
|
||||||
{
|
{
|
||||||
var views = allViews(self),
|
var subviews = [];
|
||||||
index = [views count];
|
|
||||||
|
|
||||||
while (index--)
|
[self _appendSubviewsOf:_contentView toArray:subviews];
|
||||||
if ([views[index] nextKeyView])
|
|
||||||
|
for (var i = 0, count = [subviews count]; i<count; i++)
|
||||||
|
{
|
||||||
|
if (subviews[i]._nextKeyView)
|
||||||
return YES;
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)recalculateKeyViewLoop
|
- (void)recalculateKeyViewLoop
|
||||||
{
|
{
|
||||||
var views = allViews(self);
|
var subviews = [];
|
||||||
|
|
||||||
[views sortUsingFunction:keyViewComparator context:nil];
|
[self _appendSubviewsOf:_contentView toArray:subviews];
|
||||||
|
|
||||||
var index = 0,
|
var keyViewOrder = [subviews sortedArrayUsingFunction:keyViewComparator context:_contentView],
|
||||||
count = [views count];
|
count = [keyViewOrder count];
|
||||||
|
|
||||||
for (; index < count; ++index)
|
for (var i=0; i<count; i++)
|
||||||
[views[index] setNextKeyView:views[(index + 1) % count]];
|
[keyViewOrder[i] setNextKeyView:keyViewOrder[(i+1)%count]];
|
||||||
|
|
||||||
_keyViewLoopIsDirty = NO;
|
_keyViewLoopIsDirty = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)_appendSubviewsOf:(CPView)aView toArray:(CPArray)anArray
|
||||||
|
{
|
||||||
|
var subviews = [aView subviews],
|
||||||
|
count = [subviews count];
|
||||||
|
|
||||||
|
while (count--)
|
||||||
|
[self _appendSubviewsOf:subviews[count] toArray:anArray];
|
||||||
|
|
||||||
|
[anArray addObject:aView];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setAutorecalculatesKeyViewLoop:(BOOL)shouldRecalculate
|
- (void)setAutorecalculatesKeyViewLoop:(BOOL)shouldRecalculate
|
||||||
{
|
{
|
||||||
if (_autorecalculatesKeyViewLoop === shouldRecalculate)
|
if (_autorecalculatesKeyViewLoop === shouldRecalculate)
|
||||||
@@ -2513,97 +2464,42 @@ CPTexturedBackgroundWindowMask
|
|||||||
|
|
||||||
- (void)selectNextKeyView:(id)sender
|
- (void)selectNextKeyView:(id)sender
|
||||||
{
|
{
|
||||||
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
|
|
||||||
[self recalculateKeyViewLoop];
|
|
||||||
|
|
||||||
var nextValidKeyView = nil;
|
|
||||||
|
|
||||||
if ([_firstResponder isKindOfClass:[CPView class]])
|
if ([_firstResponder isKindOfClass:[CPView class]])
|
||||||
nextValidKeyView = [_firstResponder nextValidKeyView];
|
[self selectKeyViewFollowingView:_firstResponder];
|
||||||
|
|
||||||
if (!nextValidKeyView)
|
|
||||||
{
|
|
||||||
var initialFirstResponder = [self initialFirstResponder];
|
|
||||||
|
|
||||||
if ([initialFirstResponder acceptsFirstResponder])
|
|
||||||
nextValidKeyView = initialFirstResponder;
|
|
||||||
else
|
|
||||||
nextValidKeyView = [initialFirstResponder nextValidKeyView];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self makeFirstResponder:nextValidKeyView];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)selectPreviousKeyView:(id)sender
|
- (void)selectPreviousKeyView:(id)sender
|
||||||
{
|
{
|
||||||
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
|
|
||||||
[self recalculateKeyViewLoop];
|
|
||||||
|
|
||||||
var previousValidKeyView = nil;
|
|
||||||
|
|
||||||
if ([_firstResponder isKindOfClass:[CPView class]])
|
if ([_firstResponder isKindOfClass:[CPView class]])
|
||||||
previousValidKeyView = [_firstResponder previousValidKeyView];
|
[self selectKeyViewPrecedingView:_firstResponder];
|
||||||
|
|
||||||
if (!previousValidKeyView)
|
|
||||||
{
|
|
||||||
var initialFirstResponder = [self initialFirstResponder];
|
|
||||||
|
|
||||||
if ([initialFirstResponder acceptsFirstResponder])
|
|
||||||
previousValidKeyView = initialFirstResponder;
|
|
||||||
else
|
|
||||||
previousValidKeyView = [initialFirstResponder previousValidKeyView];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self makeFirstResponder:previousValidKeyView];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)selectKeyViewFollowingView:(CPView)aView
|
- (void)selectKeyViewFollowingView:(CPView)aView
|
||||||
{
|
{
|
||||||
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
|
if (_keyViewLoopIsDirty)
|
||||||
[self recalculateKeyViewLoop];
|
[self recalculateKeyViewLoop];
|
||||||
|
|
||||||
var nextValidKeyView = [aView nextValidKeyView];
|
[self makeFirstResponder:[aView nextValidKeyView]];
|
||||||
|
|
||||||
if ([nextValidKeyView isKindOfClass:[CPView class]])
|
|
||||||
[self makeFirstResponder:nextValidKeyView];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)selectKeyViewPrecedingView:(CPView)aView
|
- (void)selectKeyViewPrecedingView:(CPView)aView
|
||||||
{
|
{
|
||||||
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
|
if (_keyViewLoopIsDirty)
|
||||||
[self recalculateKeyViewLoop];
|
[self recalculateKeyViewLoop];
|
||||||
|
|
||||||
var previousValidKeyView = [aView previousValidKeyView];
|
[self makeFirstResponder:[aView previousValidKeyView]];
|
||||||
|
|
||||||
if ([previousValidKeyView isKindOfClass:[CPView class]])
|
|
||||||
[self makeFirstResponder:previousValidKeyView];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the default button for the window.
|
|
||||||
Note: this method is deprecated use setDefaultButton: instead.
|
|
||||||
@param aButton - The button that should become default.
|
|
||||||
*/
|
|
||||||
- (void)setDefaultButtonCell:(CPButton)aButton
|
- (void)setDefaultButtonCell:(CPButton)aButton
|
||||||
{
|
{
|
||||||
[self setDefaultButton:aButton];
|
[self setDefaultButton:aButton];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the default button of the receiver.
|
|
||||||
NOTE: This method is deprecated. Use defaultButton instead.
|
|
||||||
*/
|
|
||||||
- (CPButton)defaultButtonCell
|
- (CPButton)defaultButtonCell
|
||||||
{
|
{
|
||||||
return [self defaultButton];
|
return [self defaultButton];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the default button for the window.
|
|
||||||
This is equivalent to setting the the key equivalent of the button to "return".
|
|
||||||
Additionally this will turn your button blue (with the Aristo theme).
|
|
||||||
@param aButton - The button that should become default.
|
|
||||||
*/
|
|
||||||
- (void)setDefaultButton:(CPButton)aButton
|
- (void)setDefaultButton:(CPButton)aButton
|
||||||
{
|
{
|
||||||
if (_defaultButton === aButton)
|
if (_defaultButton === aButton)
|
||||||
@@ -2618,43 +2514,26 @@ CPTexturedBackgroundWindowMask
|
|||||||
[_defaultButton setKeyEquivalent:CPCarriageReturnCharacter];
|
[_defaultButton setKeyEquivalent:CPCarriageReturnCharacter];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the default button of the receiver.
|
|
||||||
*/
|
|
||||||
- (CPButton)defaultButton
|
- (CPButton)defaultButton
|
||||||
{
|
{
|
||||||
return _defaultButton;
|
return _defaultButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the default button key equivalent to "return".
|
|
||||||
*/
|
|
||||||
- (void)enableKeyEquivalentForDefaultButton
|
- (void)enableKeyEquivalentForDefaultButton
|
||||||
{
|
{
|
||||||
_defaultButtonEnabled = YES;
|
_defaultButtonEnabled = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the default button key equivalent to "return".
|
|
||||||
NOTE: this method is deprecated. Use enableKeyEquivalentForDefaultButton instead.
|
|
||||||
*/
|
|
||||||
- (void)enableKeyEquivalentForDefaultButtonCell
|
- (void)enableKeyEquivalentForDefaultButtonCell
|
||||||
{
|
{
|
||||||
[self enableKeyEquivalentForDefaultButton];
|
[self enableKeyEquivalentForDefaultButton];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Removes the key equivalent for the default button.
|
|
||||||
*/
|
|
||||||
- (void)disableKeyEquivalentForDefaultButton
|
- (void)disableKeyEquivalentForDefaultButton
|
||||||
{
|
{
|
||||||
_defaultButtonEnabled = NO;
|
_defaultButtonEnabled = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Removes the key equivalent for the default button.
|
|
||||||
Note: this method is deprecated. Use disableKeyEquivalentForDefaultButton instead.
|
|
||||||
*/
|
|
||||||
- (void)disableKeyEquivalentForDefaultButtonCell
|
- (void)disableKeyEquivalentForDefaultButtonCell
|
||||||
{
|
{
|
||||||
[self disableKeyEquivalentForDefaultButton];
|
[self disableKeyEquivalentForDefaultButton];
|
||||||
@@ -2662,48 +2541,19 @@ CPTexturedBackgroundWindowMask
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
var allViews = function(aWindow)
|
var keyViewComparator = function(a, b, context)
|
||||||
{
|
{
|
||||||
var views = [CPArray arrayWithObject:[aWindow contentView]];
|
var viewBounds = [a convertRect:[a bounds] toView:nil],
|
||||||
|
otherBounds = [b convertRect:[b bounds] toView:nil];
|
||||||
|
|
||||||
[views addObjectsFromArray:[[aWindow contentView] subviews]];
|
if (CGRectGetMinY(viewBounds) < CGRectGetMinY(otherBounds))
|
||||||
|
return -1;
|
||||||
var index = 0;
|
else if (CGRectGetMinY(viewBounds) == CGRectGetMinY(otherBounds) && CGRectGetMinX(viewBounds) < CGRectGetMinX(otherBounds))
|
||||||
for (; index < views.length; ++index)
|
return -1;
|
||||||
views = views.concat([views[index] subviews]);
|
else if (CGRectGetMinX(viewBounds) == CGRectGetMinX(otherBounds) && CGRectGetMinX(viewBounds) == CGRectGetMinX(otherBounds))
|
||||||
|
return 0;
|
||||||
return views;
|
else
|
||||||
}
|
return 1;
|
||||||
|
|
||||||
var keyViewComparator = function(lhs, rhs, context)
|
|
||||||
{
|
|
||||||
var lhsBounds = [lhs convertRect:[lhs bounds] toView:nil],
|
|
||||||
rhsBounds = [rhs convertRect:[rhs bounds] toView:nil],
|
|
||||||
lhsY = _CGRectGetMinY(lhsBounds),
|
|
||||||
rhsY = _CGRectGetMinY(rhsBounds),
|
|
||||||
lhsX = _CGRectGetMinX(lhsBounds),
|
|
||||||
rhsX = _CGRectGetMinX(rhsBounds),
|
|
||||||
intersectsVertically = MIN(_CGRectGetMaxY(lhsBounds), _CGRectGetMaxY(rhsBounds)) - MAX(lhsY, rhsY);
|
|
||||||
|
|
||||||
// If two views are "on the same line" (intersect vertically), then rely on the x comparison.
|
|
||||||
if (intersectsVertically > 0)
|
|
||||||
{
|
|
||||||
if (lhsX < rhsX)
|
|
||||||
return CPOrderedAscending;
|
|
||||||
|
|
||||||
if (lhsX === rhsX)
|
|
||||||
return CPOrderedSame;
|
|
||||||
|
|
||||||
return CPOrderedDescending;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhsY < rhsY)
|
|
||||||
return CPOrderedAscending;
|
|
||||||
|
|
||||||
if (lhsY === rhsY)
|
|
||||||
return CPOrderedSame;
|
|
||||||
|
|
||||||
return CPOrderedDescending;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@implementation CPWindow (MenuBar)
|
@implementation CPWindow (MenuBar)
|
||||||
@@ -2768,25 +2618,16 @@ var keyViewComparator = function(lhs, rhs, context)
|
|||||||
return _autoresizingMask;
|
return _autoresizingMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Converts aPoint from the window coordinate system to the global coordinate system.
|
|
||||||
*/
|
|
||||||
- (CGPoint)convertBaseToGlobal:(CGPoint)aPoint
|
- (CGPoint)convertBaseToGlobal:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
return [CPPlatform isBrowser] ? [self convertBaseToPlatformWindow:aPoint] : [self convertBaseToScreen:aPoint];
|
return [CPPlatform isBrowser] ? [self convertBaseToPlatformWindow:aPoint] : [self convertBaseToScreen:aPoint];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Converts aPoint from the global coordinate system to the window coordinate system.
|
|
||||||
*/
|
|
||||||
- (CGPoint)convertGlobalToBase:(CGPoint)aPoint
|
- (CGPoint)convertGlobalToBase:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
return [CPPlatform isBrowser] ? [self convertPlatformWindowToBase:aPoint] : [self convertScreenToBase:aPoint];
|
return [CPPlatform isBrowser] ? [self convertPlatformWindowToBase:aPoint] : [self convertScreenToBase:aPoint];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Converts aPoint from the window coordinate system to the coordinate system of the parent platform window.
|
|
||||||
*/
|
|
||||||
- (CGPoint)convertBaseToPlatformWindow:(CGPoint)aPoint
|
- (CGPoint)convertBaseToPlatformWindow:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
if ([self _sharesChromeWithPlatformWindow])
|
if ([self _sharesChromeWithPlatformWindow])
|
||||||
@@ -2797,9 +2638,6 @@ var keyViewComparator = function(lhs, rhs, context)
|
|||||||
return _CGPointMake(aPoint.x + origin.x, aPoint.y + origin.y);
|
return _CGPointMake(aPoint.x + origin.x, aPoint.y + origin.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Converts aPoint from the parent platform window coordinate system to the windows coordinate system.
|
|
||||||
*/
|
|
||||||
- (CGPoint)convertPlatformWindowToBase:(CGPoint)aPoint
|
- (CGPoint)convertPlatformWindowToBase:(CGPoint)aPoint
|
||||||
{
|
{
|
||||||
if ([self _sharesChromeWithPlatformWindow])
|
if ([self _sharesChromeWithPlatformWindow])
|
||||||
@@ -2889,19 +2727,12 @@ var keyViewComparator = function(lhs, rhs, context)
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CPWindow (Deprecated)
|
@implementation CPWindow (Deprecated)
|
||||||
/*!
|
|
||||||
Sets the CPWindow to fill the whole browser window.
|
|
||||||
NOTE: this method has been deprecated in favor of setFullPlatformWindow:
|
|
||||||
*/
|
|
||||||
- (void)setFullBridge:(BOOL)shouldBeFullBridge
|
- (void)setFullBridge:(BOOL)shouldBeFullBridge
|
||||||
{
|
{
|
||||||
[self setFullPlatformWindow:shouldBeFullBridge];
|
[self setFullPlatformWindow:shouldBeFullBridge];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns YES if the window fills the full browser window, otherwise NO.
|
|
||||||
NOTE: this method has been deprecated in favor of isFullPlatformWindow.
|
|
||||||
*/
|
|
||||||
- (BOOL)isFullBridge
|
- (BOOL)isFullBridge
|
||||||
{
|
{
|
||||||
return [self isFullPlatformWindow];
|
return [self isFullPlatformWindow];
|
||||||
@@ -2994,10 +2825,8 @@ CPCustomWindowShadowStyle = 3;
|
|||||||
@import "_CPWindowView.j"
|
@import "_CPWindowView.j"
|
||||||
@import "_CPStandardWindowView.j"
|
@import "_CPStandardWindowView.j"
|
||||||
@import "_CPDocModalWindowView.j"
|
@import "_CPDocModalWindowView.j"
|
||||||
@import "_CPToolTipWindowView.j"
|
|
||||||
@import "_CPHUDWindowView.j"
|
@import "_CPHUDWindowView.j"
|
||||||
@import "_CPBorderlessWindowView.j"
|
@import "_CPBorderlessWindowView.j"
|
||||||
@import "_CPBorderlessBridgeWindowView.j"
|
@import "_CPBorderlessBridgeWindowView.j"
|
||||||
@import "_CPAttachedWindowView.j"
|
|
||||||
@import "CPDragServer.j"
|
@import "CPDragServer.j"
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|||||||
@@ -1,300 +0,0 @@
|
|||||||
/*
|
|
||||||
* _CPAttachedWindowView.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Antoine Mercadal
|
|
||||||
* Copyright 2011 <primalmotion@archipelproject.org>
|
|
||||||
*
|
|
||||||
* 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 "_CPWindowView.j"
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@ignore
|
|
||||||
|
|
||||||
A custom CPWindowView that manage border and cursor
|
|
||||||
*/
|
|
||||||
@implementation _CPAttachedWindowView : _CPWindowView
|
|
||||||
{
|
|
||||||
BOOL _mouseDownPressed @accessors(getter=isMouseDownPressed, setter=setMouseDownPressed:);
|
|
||||||
float _arrowOffsetX @accessors(property=arrowOffsetX);
|
|
||||||
float _arrowOffsetY @accessors(property=arrowOffsetY);
|
|
||||||
int _appearance @accessors(property=appearance);
|
|
||||||
unsigned _preferredEdge @accessors(property=preferredEdge);
|
|
||||||
|
|
||||||
CPSize _cursorSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Compute the contentView frame from a given window frame
|
|
||||||
|
|
||||||
@param aFrameRect the window frame
|
|
||||||
*/
|
|
||||||
- (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
|
||||||
{
|
|
||||||
var contentRect = CGRectMakeCopy(aFrameRect);
|
|
||||||
|
|
||||||
// @todo change border art and remove this pixel perfect adaptation
|
|
||||||
// return CGRectInset(contentRect, 20, 20);
|
|
||||||
|
|
||||||
contentRect.origin.x += 18;
|
|
||||||
contentRect.origin.y += 17;
|
|
||||||
contentRect.size.width -= 35;
|
|
||||||
contentRect.size.height -= 37;
|
|
||||||
|
|
||||||
return contentRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Compute the window frame from a given contentView frame
|
|
||||||
|
|
||||||
@param aContentRect the contentView frame
|
|
||||||
*/
|
|
||||||
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
|
||||||
{
|
|
||||||
var frameRect = CGRectMakeCopy(aContentRect);
|
|
||||||
|
|
||||||
// @todo change border art and remove this pixel perfect adaptation
|
|
||||||
//return CGRectOffset(frameRect, 20, 20);
|
|
||||||
|
|
||||||
frameRect.origin.x -= 18;
|
|
||||||
frameRect.origin.y -= 17;
|
|
||||||
frameRect.size.width += 35;
|
|
||||||
frameRect.size.height += 37;
|
|
||||||
return frameRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Initialize the _CPWindowView
|
|
||||||
*/
|
|
||||||
- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame styleMask:aStyleMask])
|
|
||||||
{
|
|
||||||
var bundle = [CPBundle bundleForClass:[self class]];
|
|
||||||
_arrowOffsetX = 0.0;
|
|
||||||
_arrowOffsetY = 0.0;
|
|
||||||
|
|
||||||
// @TODO: make this themable
|
|
||||||
_useGlowingEffect = YES;
|
|
||||||
_appearance = CPPopoverAppearanceMinimal;
|
|
||||||
_cursorSize = CPSizeMake(15, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Hide the cursor
|
|
||||||
*/
|
|
||||||
- (void)hideCursor
|
|
||||||
{
|
|
||||||
_cursorSize = CPSizeMakeZero();
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Show the cursor
|
|
||||||
*/
|
|
||||||
- (void)showCursor
|
|
||||||
{
|
|
||||||
_cursorSize = CPSizeMake(15, 10);
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
_mouseDownPressed = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Draw the view
|
|
||||||
*/
|
|
||||||
- (void)drawRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
[super drawRect:aRect];
|
|
||||||
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
radius = 5,
|
|
||||||
arrowWidth = _cursorSize.width,
|
|
||||||
arrowHeight = _cursorSize.height,
|
|
||||||
strokeWidth = 1,
|
|
||||||
strokeColor,
|
|
||||||
shadowColor = [[CPColor blackColor] colorWithAlphaComponent:.2],
|
|
||||||
shadowSize = CGSizeMake(0, 7),
|
|
||||||
shadowBlur = 15,
|
|
||||||
gradient;
|
|
||||||
|
|
||||||
if (_appearance == CPPopoverAppearanceMinimal)
|
|
||||||
{
|
|
||||||
gradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [(254.0 / 255), (254.0 / 255), (254.0 / 255), 0.93,
|
|
||||||
(231.0 / 255), (231.0 / 255), (231.0 / 255), 0.93], [0,1], 2);
|
|
||||||
strokeColor = [CPColor colorWithHexString:@"B8B8B8"];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [(38.0 / 255), (38.0 / 255), (38.0 / 255), 0.93,
|
|
||||||
(18.0 / 255), (18.0 / 255), (18.0 / 255), 0.93], [0,1], 2);
|
|
||||||
strokeColor = [CPColor colorWithHexString:@"222222"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix rect to take care of stroke and shadow
|
|
||||||
aRect.origin.x += strokeWidth + shadowBlur;
|
|
||||||
aRect.origin.y += strokeWidth + (shadowBlur + shadowSize.height / 2);
|
|
||||||
aRect.size.width -= (strokeWidth * 2) + (shadowBlur * 2);
|
|
||||||
aRect.size.height -= (strokeWidth * 2) + (shadowBlur * 2 + shadowSize.height);
|
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, strokeColor);
|
|
||||||
CGContextSetLineWidth(context, strokeWidth);
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
CGContextSetShadowWithColor(context, shadowSize, shadowBlur, shadowColor);
|
|
||||||
CGContextDrawLinearGradient(context, gradient, CGPointMake(CPRectGetMidX(aRect), 0.0), CGPointMake(CPRectGetMidX(aRect), aRect.size.height), 0);
|
|
||||||
|
|
||||||
|
|
||||||
var xMin = _CGRectGetMinX(aRect),
|
|
||||||
xMax = _CGRectGetMaxX(aRect),
|
|
||||||
yMin = _CGRectGetMinY(aRect),
|
|
||||||
yMax = _CGRectGetMaxY(aRect);
|
|
||||||
|
|
||||||
// draw!
|
|
||||||
switch (_preferredEdge)
|
|
||||||
{
|
|
||||||
case CPMinXEdge:
|
|
||||||
// origin ne
|
|
||||||
CGContextMoveToPoint(context, xMin + radius, yMin);
|
|
||||||
|
|
||||||
// ne
|
|
||||||
CGContextAddLineToPoint(context, xMax - radius, yMin);
|
|
||||||
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
|
|
||||||
|
|
||||||
// arrow CPMinXEdge
|
|
||||||
CGContextAddLineToPoint(context, xMax, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY - (arrowHeight - 2));
|
|
||||||
CGContextAddLineToPoint(context, aRect.size.width + arrowHeight + aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY);
|
|
||||||
CGContextAddLineToPoint(context, aRect.size.width + aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2 + (arrowWidth / 2)) + aRect.origin.y + _arrowOffsetY);
|
|
||||||
|
|
||||||
// se
|
|
||||||
CGContextAddLineToPoint(context, xMax, yMax - radius);
|
|
||||||
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
|
|
||||||
|
|
||||||
// sw
|
|
||||||
CGContextAddLineToPoint(context, xMin + radius, yMax);
|
|
||||||
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
|
|
||||||
|
|
||||||
// nw
|
|
||||||
CGContextAddLineToPoint(context, xMin, yMin + radius);
|
|
||||||
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPMaxXEdge:
|
|
||||||
// origin ne
|
|
||||||
CGContextMoveToPoint(context, xMin + radius, yMin);
|
|
||||||
|
|
||||||
// ne
|
|
||||||
CGContextAddLineToPoint(context, xMax - radius, yMin);
|
|
||||||
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
|
|
||||||
|
|
||||||
// se
|
|
||||||
CGContextAddLineToPoint(context, xMax, yMax - radius);
|
|
||||||
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
|
|
||||||
|
|
||||||
// sw
|
|
||||||
CGContextAddLineToPoint(context, xMin + radius, yMax);
|
|
||||||
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
|
|
||||||
|
|
||||||
// arrow CPMaxXEdge
|
|
||||||
CGContextAddLineToPoint(context, xMin, (aRect.size.height / 2 + (arrowWidth / 2) + aRect.origin.y + _arrowOffsetY));
|
|
||||||
CGContextAddLineToPoint(context, aRect.origin.x - arrowHeight + _arrowOffsetX, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY);
|
|
||||||
CGContextAddLineToPoint(context, aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2 - (arrowWidth / 2) + aRect.origin.y + _arrowOffsetY));
|
|
||||||
|
|
||||||
// nw
|
|
||||||
CGContextAddLineToPoint(context, xMin, yMin + radius);
|
|
||||||
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPMaxYEdge:
|
|
||||||
// origin nw
|
|
||||||
CGContextMoveToPoint(context, xMin, yMin + yMin);
|
|
||||||
|
|
||||||
// nw
|
|
||||||
CGContextAddLineToPoint(context, xMin, yMin + radius);
|
|
||||||
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
|
|
||||||
|
|
||||||
// arrow CPMaxYEdge
|
|
||||||
CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX - (arrowWidth / 2), yMin);
|
|
||||||
CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX, aRect.origin.y - arrowHeight + _arrowOffsetY);
|
|
||||||
CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX, aRect.origin.y + _arrowOffsetY);
|
|
||||||
|
|
||||||
// ne
|
|
||||||
CGContextAddLineToPoint(context, xMax - radius, yMin);
|
|
||||||
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
|
|
||||||
|
|
||||||
// se
|
|
||||||
CGContextAddLineToPoint(context, xMax, yMax - radius);
|
|
||||||
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
|
|
||||||
|
|
||||||
// sw
|
|
||||||
CGContextAddLineToPoint(context, xMin + radius, yMax);
|
|
||||||
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CPMinYEdge:
|
|
||||||
// origin nw
|
|
||||||
CGContextMoveToPoint(context, xMin, yMin + yMin);
|
|
||||||
|
|
||||||
// nw
|
|
||||||
CGContextAddLineToPoint(context, xMin, yMin + radius);
|
|
||||||
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
|
|
||||||
|
|
||||||
// ne
|
|
||||||
CGContextAddLineToPoint(context, xMax - radius, yMin);
|
|
||||||
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
|
|
||||||
|
|
||||||
// se
|
|
||||||
CGContextAddLineToPoint(context, xMax, yMax - radius);
|
|
||||||
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
|
|
||||||
|
|
||||||
// arrow CPMinYEdge
|
|
||||||
CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX , yMax);
|
|
||||||
CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX, aRect.size.height + aRect.origin.y + arrowHeight + _arrowOffsetY);
|
|
||||||
CGContextAddLineToPoint(context, (aRect.size.width / 2) - (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX, aRect.size.height + aRect.origin.y + _arrowOffsetY);
|
|
||||||
|
|
||||||
// sw
|
|
||||||
CGContextAddLineToPoint(context, xMin + radius, yMax);
|
|
||||||
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// no computed edge means standard rounded rect
|
|
||||||
CGContextAddPath(context, CGPathWithRoundedRectangleInRect(aRect, radius, radius, YES, YES, YES, YES));
|
|
||||||
}
|
|
||||||
|
|
||||||
CGContextClosePath(context);
|
|
||||||
|
|
||||||
//Draw it
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
CGContextFillPath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
_mouseDownPressed = YES;
|
|
||||||
[super mouseDown:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseUp:(CPEvent)anEvent
|
|
||||||
{
|
|
||||||
_mouseDownPressed = NO;
|
|
||||||
[super mouseUp:anEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -6,7 +6,6 @@ var _CPStandardWindowViewBodyBackgroundColor = nil;
|
|||||||
@implementation _CPDocModalWindowView : _CPWindowView
|
@implementation _CPDocModalWindowView : _CPWindowView
|
||||||
{
|
{
|
||||||
CPView _bodyView;
|
CPView _bodyView;
|
||||||
CPView _shadowView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CPColor)bodyBackgroundColor
|
+ (CPColor)bodyBackgroundColor
|
||||||
@@ -26,19 +25,13 @@ var _CPStandardWindowViewBodyBackgroundColor = nil;
|
|||||||
var theClass = [self class],
|
var theClass = [self class],
|
||||||
bounds = [self bounds];
|
bounds = [self bounds];
|
||||||
|
|
||||||
_bodyView = [[CPView alloc] initWithFrame:_CGRectMake(0.0, 0.0, _CGRectGetWidth(bounds), _CGRectGetHeight(bounds))];
|
_bodyView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), CGRectGetHeight(bounds))];
|
||||||
|
|
||||||
[_bodyView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
[_bodyView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||||
[_bodyView setBackgroundColor:[theClass bodyBackgroundColor]];
|
[_bodyView setBackgroundColor:[theClass bodyBackgroundColor]];
|
||||||
[_bodyView setHitTests:NO];
|
[_bodyView setHitTests:NO];
|
||||||
|
|
||||||
[self addSubview:_bodyView];
|
[self addSubview:_bodyView];
|
||||||
|
|
||||||
var bundle = [CPBundle bundleForClass:[CPWindow class]];
|
|
||||||
_shadowView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, _CGRectGetWidth(bounds), 8)];
|
|
||||||
[_shadowView setAutoresizingMask:CPViewWidthSizable];
|
|
||||||
[_shadowView setBackgroundColor:[CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowAttachedSheetShadow.png"] size:CGSizeMake(9,8)]]];
|
|
||||||
[self addSubview:_shadowView];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPTextField.j"
|
|
||||||
|
|
||||||
var _CPHUDWindowViewBackgroundColor = nil,
|
var _CPHUDWindowViewBackgroundColor = nil,
|
||||||
|
|
||||||
CPHUDCloseButtonImage = nil;
|
CPHUDCloseButtonImage = nil;
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "CPTextField.j"
|
|
||||||
@import "_CPWindowView.j"
|
@import "_CPWindowView.j"
|
||||||
|
|
||||||
|
|
||||||
@@ -106,7 +105,7 @@ var _CPStandardWindowViewBodyBackgroundColor = nil,
|
|||||||
_CPStandardWindowViewMinimizeButtonImage = nil,
|
_CPStandardWindowViewMinimizeButtonImage = nil,
|
||||||
_CPStandardWindowViewMinimizeButtonHighlightedImage = nil;
|
_CPStandardWindowViewMinimizeButtonHighlightedImage = nil;
|
||||||
|
|
||||||
var STANDARD_GRADIENT_HEIGHT = 41.0,
|
var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||||
STANDARD_TITLEBAR_HEIGHT = 25.0;
|
STANDARD_TITLEBAR_HEIGHT = 25.0;
|
||||||
|
|
||||||
@implementation _CPStandardWindowView : _CPWindowView
|
@implementation _CPStandardWindowView : _CPWindowView
|
||||||
@@ -342,7 +341,7 @@ var STANDARD_GRADIENT_HEIGHT = 41.0,
|
|||||||
if (_minimizeButton)
|
if (_minimizeButton)
|
||||||
leftOffset += 19.0;
|
leftOffset += 19.0;
|
||||||
|
|
||||||
[_titleField setFrame:CGRectMake(leftOffset, 5.0, width - leftOffset * 2.0, CGRectGetHeight([_titleField frame]))];
|
[_titleField setFrame:CGRectMake(leftOffset, 5.0, width - leftOffset*2.0, CGRectGetHeight([_titleField frame]))];
|
||||||
|
|
||||||
[[theWindow contentView] setFrameOrigin:CGPointMake(0.0, CGRectGetMaxY([_dividerView frame]))];
|
[[theWindow contentView] setFrameOrigin:CGPointMake(0.0, CGRectGetMaxY([_dividerView frame]))];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
* _CPToolTipWindowView.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Antoine Mercadal
|
|
||||||
* Copyright 2011 <primalmotion@archipelproject.org>
|
|
||||||
*
|
|
||||||
* 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 "_CPWindowView.j"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPToolTipWindowView : _CPWindowView
|
|
||||||
{
|
|
||||||
BOOL _mouseDownPressed @accessors(getter=isMouseDownPressed, setter=setMouseDownPressed:);
|
|
||||||
unsigned _gravity @accessors(property=gravity);
|
|
||||||
|
|
||||||
CPColor _backgroundColor;
|
|
||||||
CPColor _strokeColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Class methods
|
|
||||||
|
|
||||||
/*! compute the contentView frame from a given window frame
|
|
||||||
@param aFrameRect the window frame
|
|
||||||
*/
|
|
||||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
|
||||||
{
|
|
||||||
var contentRect = CGRectMakeCopy(aFrameRect);
|
|
||||||
|
|
||||||
contentRect.origin.x += 3;
|
|
||||||
contentRect.origin.y += 3;
|
|
||||||
contentRect.size.width -= 6;
|
|
||||||
contentRect.size.height -= 6;
|
|
||||||
|
|
||||||
return contentRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! compute the window frame from a given contentView frame
|
|
||||||
@param aContentRect the contentView frame
|
|
||||||
*/
|
|
||||||
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
|
||||||
{
|
|
||||||
var aFrameRect = CGRectMakeCopy(aContentRect);
|
|
||||||
|
|
||||||
aFrameRect.origin.x -= 3;
|
|
||||||
aFrameRect.origin.y -= 3;
|
|
||||||
aFrameRect.size.width += 6;
|
|
||||||
aFrameRect.size.height += 6;
|
|
||||||
|
|
||||||
return aFrameRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Initialization
|
|
||||||
|
|
||||||
- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask
|
|
||||||
{
|
|
||||||
if (self = [super initWithFrame:aFrame styleMask:aStyleMask])
|
|
||||||
{
|
|
||||||
_strokeColor = [CPColor colorWithHexString:@"E3E3E3"];
|
|
||||||
_backgroundColor = [CPColor colorWithHexString:@"FFFFCA"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark drawing
|
|
||||||
|
|
||||||
- (void)drawRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
[super drawRect:aRect];
|
|
||||||
|
|
||||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
||||||
radius = 2,
|
|
||||||
strokeWidth = 1;
|
|
||||||
|
|
||||||
CGContextSetStrokeColor(context, _strokeColor);
|
|
||||||
CGContextSetFillColor(context, _backgroundColor);
|
|
||||||
CGContextSetLineWidth(context, strokeWidth);
|
|
||||||
CGContextBeginPath(context);
|
|
||||||
|
|
||||||
aRect.origin.x += strokeWidth;
|
|
||||||
aRect.origin.y += strokeWidth;
|
|
||||||
aRect.size.width -= strokeWidth * 2;
|
|
||||||
aRect.size.height -= strokeWidth * 2;
|
|
||||||
|
|
||||||
CGContextAddPath(context, CGPathWithRoundedRectangleInRect(aRect, radius, radius, YES, YES, YES, YES));
|
|
||||||
CGContextClosePath(context);
|
|
||||||
|
|
||||||
//Draw it
|
|
||||||
CGContextStrokePath(context);
|
|
||||||
CGContextFillPath(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -37,6 +37,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
CGSize _toolbarOffset;
|
CGSize _toolbarOffset;
|
||||||
// BOOL _isAnimatingToolbar;
|
// BOOL _isAnimatingToolbar;
|
||||||
|
|
||||||
|
|
||||||
CGRect _resizeFrame;
|
CGRect _resizeFrame;
|
||||||
CGPoint _mouseDraggedPoint;
|
CGPoint _mouseDraggedPoint;
|
||||||
|
|
||||||
@@ -111,7 +112,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
return [self trackResizeWithEvent:anEvent];
|
return [self trackResizeWithEvent:anEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([theWindow isMovable] && [theWindow isMovableByWindowBackground])
|
if ([theWindow isMovableByWindowBackground])
|
||||||
[self trackMoveWithEvent:anEvent];
|
[self trackMoveWithEvent:anEvent];
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -137,12 +138,12 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
|
|
||||||
else if (type === CPLeftMouseDragged)
|
else if (type === CPLeftMouseDragged)
|
||||||
{
|
{
|
||||||
var newSize = CGSizeMake(CGRectGetWidth(_resizeFrame) + location.x - CGRectGetMinX(_resizeFrame), CGRectGetHeight(_resizeFrame) + location.y - CGRectGetMinY(_resizeFrame));
|
var newSize = CGSizeMake(CGRectGetWidth(_resizeFrame) + location.x - CGRectGetMinX(_resizeFrame), CGRectGetHeight(_resizeFrame) + location.y - CGRectGetMinY(_resizeFrame));
|
||||||
|
|
||||||
if (theWindow._isSheet && theWindow._parentView && (theWindow._frame.size.width !== newSize.width))
|
if (theWindow._isSheet && theWindow._parentView && (theWindow._frame.size.width !== newSize.width))
|
||||||
[theWindow._parentView _setAttachedSheetFrameOrigin];
|
[theWindow._parentView _setAttachedSheetFrameOrigin];
|
||||||
|
|
||||||
[theWindow setFrameSize:newSize];
|
[theWindow setFrameSize:newSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
[CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
[CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||||
@@ -161,7 +162,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
|
|
||||||
var minPointY = 0;
|
var minPointY = 0;
|
||||||
|
|
||||||
if ([CPMenu menuBarVisible])
|
if([CPMenu menuBarVisible])
|
||||||
minPointY = [[CPApp mainMenu] menuBarHeight];
|
minPointY = [[CPApp mainMenu] menuBarHeight];
|
||||||
|
|
||||||
var restrictedPoint = CGPointMake(0, 0);
|
var restrictedPoint = CGPointMake(0, 0);
|
||||||
@@ -174,9 +175,6 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
|||||||
|
|
||||||
- (void)trackMoveWithEvent:(CPEvent)anEvent
|
- (void)trackMoveWithEvent:(CPEvent)anEvent
|
||||||
{
|
{
|
||||||
if (![[self window] isMovable])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var type = [anEvent type];
|
var type = [anEvent type];
|
||||||
|
|
||||||
if (type === CPLeftMouseUp)
|
if (type === CPLeftMouseUp)
|
||||||
|
|||||||
@@ -32,15 +32,10 @@
|
|||||||
@ingroup appkit
|
@ingroup appkit
|
||||||
@class CPWindowController
|
@class CPWindowController
|
||||||
|
|
||||||
An instance of a CPWindowController manages a CPWindow. Windows are typically loaded via a cib,
|
An instance of a CPWindowController manages a CPWindow. It has methods
|
||||||
but they can also manage windows created in code. A CPWindowController can manage a window by
|
that get called when the window is loading, and after the window has loaded. In the
|
||||||
itself or work with AppKits's document-based architecture.
|
Model-View-Controller method of program design, the CPWindowController would be
|
||||||
|
considered the 'Controller' and the CPWindow the 'Model.'
|
||||||
In a Document based app, a CPWindowController instance is created and managed by a CPDocument subclass.
|
|
||||||
|
|
||||||
If the CPWindowController is managing a CPWindow created in a cib the \c owner of the CPWindow is this controller.
|
|
||||||
|
|
||||||
@note When creating the window programatically (instead of a cib) you should override the \c loadWindow method.\c loadWindow is called the first time the window object is needed. @endnote
|
|
||||||
*/
|
*/
|
||||||
@implementation CPWindowController : CPResponder
|
@implementation CPWindowController : CPResponder
|
||||||
{
|
{
|
||||||
@@ -67,7 +62,7 @@
|
|||||||
/*!
|
/*!
|
||||||
Initializes the controller with a window.
|
Initializes the controller with a window.
|
||||||
@param aWindow the window to control
|
@param aWindow the window to control
|
||||||
@return the initialized window controller
|
@return the initialzed window controller
|
||||||
*/
|
*/
|
||||||
- (id)initWithWindow:(CPWindow)aWindow
|
- (id)initWithWindow:(CPWindow)aWindow
|
||||||
{
|
{
|
||||||
@@ -87,7 +82,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Initializes the controller with a Cappuccino Interface Builder name.
|
Initializes the controller with a Capppuccino Interface Builder name.
|
||||||
@param aWindowCibName the cib name of the window to control
|
@param aWindowCibName the cib name of the window to control
|
||||||
@return the initialized window controller
|
@return the initialized window controller
|
||||||
*/
|
*/
|
||||||
@@ -129,8 +124,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Loads the window. This method should never be called directly. Instead call \c window which will in turn call windowWillLoad and windowDidLoad.
|
Loads the window
|
||||||
This method should be overwritten if you are creating the view programatically.
|
|
||||||
*/
|
*/
|
||||||
- (void)loadWindow
|
- (void)loadWindow
|
||||||
{
|
{
|
||||||
@@ -164,8 +158,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the CPWindow the reciever controls.
|
Returns the window this object controls.
|
||||||
This will cause \c loadWindow to be called if no window object exists yet.
|
|
||||||
*/
|
*/
|
||||||
- (CPWindow)window
|
- (CPWindow)window
|
||||||
{
|
{
|
||||||
@@ -283,10 +276,6 @@
|
|||||||
[self setViewController:viewController];
|
[self setViewController:viewController];
|
||||||
|
|
||||||
[self synchronizeWindowTitleWithDocumentName];
|
[self synchronizeWindowTitleWithDocumentName];
|
||||||
|
|
||||||
// Change of document means toolbar items may no longer make sense.
|
|
||||||
// FIXME: DOCUMENT ARCHITECTURE Should we setToolbar: as well?
|
|
||||||
[[[self window] toolbar] validateVisibleItems];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSupportsMultipleDocuments:(BOOL)shouldSupportMultipleDocuments
|
- (void)setSupportsMultipleDocuments:(BOOL)shouldSupportMultipleDocuments
|
||||||
@@ -384,7 +373,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the CPDocument in the controlled window.
|
Returns the document in the controlled window.
|
||||||
*/
|
*/
|
||||||
- (CPDocument)document
|
- (CPDocument)document
|
||||||
{
|
{
|
||||||
|
|||||||
+3
-3
@@ -34,9 +34,9 @@
|
|||||||
@import "_CPCibWindowTemplate.j"
|
@import "_CPCibWindowTemplate.j"
|
||||||
|
|
||||||
|
|
||||||
CPCibOwner = @"CPCibOwner";
|
CPCibOwner = @"CPCibOwner",
|
||||||
CPCibTopLevelObjects = @"CPCibTopLevelObjects";
|
CPCibTopLevelObjects = @"CPCibTopLevelObjects",
|
||||||
CPCibReplacementClasses = @"CPCibReplacementClasses";
|
CPCibReplacementClasses = @"CPCibReplacementClasses",
|
||||||
CPCibExternalObjects = @"CPCibExternalObjects";
|
CPCibExternalObjects = @"CPCibExternalObjects";
|
||||||
|
|
||||||
var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* CPCibBindingConnector.j
|
|
||||||
* AppKit
|
|
||||||
*
|
|
||||||
* Created by Ross Boucher.
|
|
||||||
* Copyright 2010, 280 North, Inc.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "CPCibConnector.j"
|
|
||||||
|
|
||||||
var CPCibBindingConnectorBindingKey = @"CPCibBindingConnectorBindingKey",
|
|
||||||
CPCibBindingConnectorKeyPathKey = @"CPCibBindingConnectorKeyPathKey",
|
|
||||||
CPCibBindingConnectorOptionsKey = @"CPCibBindingConnectorOptionsKey";
|
|
||||||
|
|
||||||
@implementation CPCibBindingConnector : CPCibConnector
|
|
||||||
{
|
|
||||||
id _binding;
|
|
||||||
id _keyPath;
|
|
||||||
id _options;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
if (self = [super initWithCoder:aCoder])
|
|
||||||
{
|
|
||||||
_binding = [aCoder decodeObjectForKey:CPCibBindingConnectorBindingKey];
|
|
||||||
_keyPath = [aCoder decodeObjectForKey:CPCibBindingConnectorKeyPathKey];
|
|
||||||
_options = [aCoder decodeObjectForKey:CPCibBindingConnectorOptionsKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
||||||
{
|
|
||||||
[super encodeWithCoder:aCoder];
|
|
||||||
|
|
||||||
[aCoder encodeObject:_binding forKey:CPCibBindingConnectorBindingKey];
|
|
||||||
[aCoder encodeObject:_keyPath forKey:CPCibBindingConnectorKeyPathKey];
|
|
||||||
[aCoder encodeObject:_options forKey:CPCibBindingConnectorOptionsKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)establishConnection
|
|
||||||
{
|
|
||||||
[_source bind:_binding toObject:_destination withKeyPath:_keyPath options:_options];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -47,8 +47,12 @@
|
|||||||
|
|
||||||
// If the destination doesn't respond to this selector, warn but don't die.
|
// If the destination doesn't respond to this selector, warn but don't die.
|
||||||
if (_destination && ![_destination respondsToSelector:selector])
|
if (_destination && ![_destination respondsToSelector:selector])
|
||||||
|
{
|
||||||
CPLog.warn(@"Could not connect the action " + selector + @" to target of class " + [_destination className]);
|
CPLog.warn(@"Could not connect the action " + selector + @" to target of class " + [_destination className]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Not being able to set the action is a fatal error.
|
// Not being able to set the action is a fatal error.
|
||||||
if ([_source respondsToSelector:@selector(setAction:)])
|
if ([_source respondsToSelector:@selector(setAction:)])
|
||||||
objj_msgSend(_source, @selector(setAction:), selector);
|
objj_msgSend(_source, @selector(setAction:), selector);
|
||||||
|
|||||||
@@ -83,14 +83,7 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
|
|||||||
if ([aCoder respondsToSelector:@selector(bundle)] &&
|
if ([aCoder respondsToSelector:@selector(bundle)] &&
|
||||||
(![aCoder respondsToSelector:@selector(awakenCustomResources)] || [aCoder awakenCustomResources]))
|
(![aCoder respondsToSelector:@selector(awakenCustomResources)] || [aCoder awakenCustomResources]))
|
||||||
if (_className === @"CPImage")
|
if (_className === @"CPImage")
|
||||||
{
|
|
||||||
if (_resourceName == "CPAddTemplate")
|
|
||||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"plus_button.png"] size:CGSizeMake(11, 12)];
|
|
||||||
else if (_resourceName == "CPRemoveTemplate")
|
|
||||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"minus_button.png"] size:CGSizeMake(11, 4)];
|
|
||||||
|
|
||||||
return [[CPImage alloc] initWithContentsOfFile:[[aCoder bundle] pathForResource:_resourceName] size:_properties.valueForKey(@"size")];
|
return [[CPImage alloc] initWithContentsOfFile:[[aCoder bundle] pathForResource:_resourceName] size:_properties.valueForKey(@"size")];
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
@import "CPCibConnector.j"
|
@import "CPCibConnector.j"
|
||||||
@import "CPCibControlConnector.j"
|
@import "CPCibControlConnector.j"
|
||||||
@import "CPCibOutletConnector.j"
|
@import "CPCibOutletConnector.j"
|
||||||
@import "CPCibBindingConnector.j"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation _CPCibObjectData : CPObject
|
@implementation _CPCibObjectData : CPObject
|
||||||
|
|||||||
@@ -5,19 +5,19 @@
|
|||||||
@import "CPWindow.j"
|
@import "CPWindow.j"
|
||||||
|
|
||||||
|
|
||||||
var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeKey",
|
var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeKey",
|
||||||
_CPCibWindowTemplateMaxSizeKey = @"_CPCibWindowTemplateMaxSizeKey",
|
_CPCibWindowTemplateMaxSizeKey = @"_CPCibWindowTemplateMaxSizeKey",
|
||||||
|
|
||||||
_CPCibWindowTemplateViewClassKey = @"_CPCibWindowTemplateViewClassKey",
|
_CPCibWindowTemplateViewClassKey = @"_CPCibWindowTemplateViewClassKey",
|
||||||
_CPCibWindowTemplateWindowClassKey = @"_CPCibWindowTemplateWindowClassKey",
|
_CPCibWindowTemplateWindowClassKey = @"_CPCibWindowTemplateWindowClassKey",
|
||||||
|
|
||||||
_CPCibWindowTemplateWindowRectKey = @"_CPCibWindowTemplateWindowRectKey",
|
_CPCibWindowTemplateWindowRectKey = @"_CPCibWindowTemplateWindowRectKey",
|
||||||
_CPCibWindowTemplateWindowStyleMaskKey = @"_CPCibWindowTempatStyleMaskKey",
|
_CPCibWindowTemplateWindowStyleMaskKey = @"_CPCibWindowTempatStyleMaskKey",
|
||||||
_CPCibWindowTemplateWindowTitleKey = @"_CPCibWindowTemplateWindowTitleKey",
|
_CPCibWindowTemplateWindowTitleKey = @"_CPCibWindowTemplateWindowTitleKey",
|
||||||
_CPCibWindowTemplateWindowViewKey = @"_CPCibWindowTemplateWindowViewKey",
|
_CPCibWindowTemplateWindowViewKey = @"_CPCibWindowTemplateWindowViewKey",
|
||||||
|
|
||||||
_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop = @"_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop",
|
_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop = @"_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop";
|
||||||
_CPCibWindowTemplateWindowIsFullPlatformWindowKey = @"_CPCibWindowTemplateWindowIsFullPlatformWindowKey";
|
_CPCibWindowTemplateWindowIsFullPlatformWindowKey = @"_CPCibWindowTemplateWindowIsFullPlatformWindowKey";
|
||||||
|
|
||||||
@implementation _CPCibWindowTemplate : CPObject
|
@implementation _CPCibWindowTemplate : CPObject
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -449,28 +449,23 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
|||||||
CGContextClearRect(_context, _CGRectMake(0.0, 0.0, _CGRectGetWidth(_backingStoreFrame), _CGRectGetHeight(_backingStoreFrame)));
|
CGContextClearRect(_context, _CGRectMake(0.0, 0.0, _CGRectGetWidth(_backingStoreFrame), _CGRectGetHeight(_backingStoreFrame)));
|
||||||
|
|
||||||
// Recomposite
|
// Recomposite
|
||||||
var transform;
|
var transform = _transformFromLayer;
|
||||||
|
|
||||||
if (_superlayer)
|
if (_superlayer)
|
||||||
{
|
{
|
||||||
var superlayerTransform = _CALayerGetTransform(_superlayer, nil),
|
var superlayerTransform = _CALayerGetTransform(_superlayer, nil),
|
||||||
superlayerOrigin = CGPointApplyAffineTransform(_superlayer._bounds.origin, superlayerTransform);
|
superlayerOrigin = CGPointApplyAffineTransform(_superlayer._bounds.origin, superlayerTransform);
|
||||||
|
|
||||||
transform = CGAffineTransformConcat(_transformFromLayer, superlayerTransform);
|
transform = CGAffineTransformConcat(transform, superlayerTransform);
|
||||||
|
|
||||||
transform.tx -= superlayerOrigin.x;
|
transform.tx -= superlayerOrigin.x;
|
||||||
transform.ty -= superlayerOrigin.y;
|
transform.ty -= superlayerOrigin.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
|
||||||
// Copy so we don't affect the original.
|
|
||||||
transform = CGAffineTransformCreateCopy(_transformFromLayer);
|
|
||||||
|
|
||||||
transform.tx -= _CGRectGetMinX(_backingStoreFrame);
|
transform.tx -= _CGRectGetMinX(_backingStoreFrame);
|
||||||
transform.ty -= _CGRectGetMinY(_backingStoreFrame);
|
transform.ty -= _CGRectGetMinY(_backingStoreFrame);
|
||||||
|
|
||||||
CGContextSaveGState(_context);
|
CGContextSaveGState(_context);
|
||||||
|
|
||||||
CGContextConcatCTM(_context, transform);//_transformFromView);
|
CGContextConcatCTM(_context, transform);//_transformFromView);
|
||||||
if (USE_BUFFER)
|
if (USE_BUFFER)
|
||||||
{
|
{
|
||||||
@@ -796,7 +791,7 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
|||||||
|
|
||||||
layer._runLoopUpdateMask = 0;
|
layer._runLoopUpdateMask = 0;
|
||||||
}
|
}
|
||||||
window.loop = false;
|
window.loop= false;
|
||||||
CALayerRegisteredRunLoopUpdates = nil;
|
CALayerRegisteredRunLoopUpdates = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -851,7 +846,7 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Marks the specified rectangle as needing to be redrawn.
|
Marks the specified rectange as needing to be redrawn.
|
||||||
@param aRect the area that needs to be redrawn.
|
@param aRect the area that needs to be redrawn.
|
||||||
*/
|
*/
|
||||||
- (void)setNeedsDisplayInRect:(CGRect)aRect
|
- (void)setNeedsDisplayInRect:(CGRect)aRect
|
||||||
@@ -895,8 +890,8 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Converts the rectangle from the receiver's coordinate system to the specified layer's coordinate system.
|
Converts the rectangle from the receier's coordinate system to the specified layer's coordinate system.
|
||||||
@param aRect the rectangle to convert
|
@param aRect the rectange to convert
|
||||||
@param aLayer the layer coordinate system to convert to
|
@param aLayer the layer coordinate system to convert to
|
||||||
@return the converted rectangle
|
@return the converted rectangle
|
||||||
*/
|
*/
|
||||||
@@ -1142,7 +1137,7 @@ function _CALayerRecalculateGeometry(aLayer, aGeometryChange)
|
|||||||
|
|
||||||
if (aGeometryChange & CALayerGeometryBoundsMask && aLayer._needsDisplayOnBoundsChange)
|
if (aGeometryChange & CALayerGeometryBoundsMask && aLayer._needsDisplayOnBoundsChange)
|
||||||
[aLayer setNeedsDisplay];
|
[aLayer setNeedsDisplay];
|
||||||
// We need to recompose if we have a custom backing store frame, OR
|
// We need to recomposite if we have a custom backing store frame, OR
|
||||||
// If the change is not solely composed of position and anchor points changes.
|
// If the change is not solely composed of position and anchor points changes.
|
||||||
// Anchor point and position changes simply move the object, requiring
|
// Anchor point and position changes simply move the object, requiring
|
||||||
// no re-rendering.
|
// no re-rendering.
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function CGColorGetConstantColor(aColorName)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
This function is for source compatibility.
|
This function is for source compatability.
|
||||||
*/
|
*/
|
||||||
function CGColorRetain(aColor)
|
function CGColorRetain(aColor)
|
||||||
{
|
{
|
||||||
@@ -55,7 +55,7 @@ function CGColorRetain(aColor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
This function is for source compatibility.
|
This function is for source compatability.
|
||||||
*/
|
*/
|
||||||
function CGColorRelease()
|
function CGColorRelease()
|
||||||
{
|
{
|
||||||
@@ -108,7 +108,7 @@ function CGColorCreateCopy(aColor)
|
|||||||
*/
|
*/
|
||||||
function CGColorCreateGenericGray(gray, alpha)
|
function CGColorCreateGenericGray(gray, alpha)
|
||||||
{
|
{
|
||||||
return CGColorCreate(CGColorSpaceCreateDeviceRGB(), [gray, gray, gray, alpha]);
|
return CGColorCreate(0, [gray, alpha]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -122,7 +122,7 @@ function CGColorCreateGenericGray(gray, alpha)
|
|||||||
*/
|
*/
|
||||||
function CGColorCreateGenericRGB(red, green, blue, alpha)
|
function CGColorCreateGenericRGB(red, green, blue, alpha)
|
||||||
{
|
{
|
||||||
return CGColorCreate(CGColorSpaceCreateDeviceRGB(), [red, green, blue, alpha]);
|
return CGColorCreate(0, [red, green, blue, alpha]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -137,8 +137,7 @@ function CGColorCreateGenericRGB(red, green, blue, alpha)
|
|||||||
*/
|
*/
|
||||||
function CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha)
|
function CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha)
|
||||||
{
|
{
|
||||||
return CGColorCreate(CGColorSpaceCreateDeviceCMYK(),
|
return CGColorCreate(0, [cyan, magenta, yellow, black, alpha]);
|
||||||
[cyan, magenta, yellow, black, alpha]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -150,22 +149,19 @@ function CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha)
|
|||||||
*/
|
*/
|
||||||
function CGColorCreateCopyWithAlpha(aColor, anAlpha)
|
function CGColorCreateCopyWithAlpha(aColor, anAlpha)
|
||||||
{
|
{
|
||||||
if (!aColor)
|
var components = aColor.components;
|
||||||
return aColor; // Avoid error null pointer in next line
|
|
||||||
|
|
||||||
var components = aColor.components.slice();
|
if (!aColor || anAlpha == components[components.length - 1])
|
||||||
|
return aColor;
|
||||||
if (anAlpha == components[components.length - 1])
|
|
||||||
return aColor;
|
|
||||||
|
|
||||||
// set new alpha value now so that a potentially a new cache entry is made and
|
|
||||||
// not that an existing cache entry is mutated.
|
|
||||||
components[components.length - 1] = anAlpha;
|
|
||||||
|
|
||||||
if (aColor.pattern)
|
if (aColor.pattern)
|
||||||
return CGColorCreateWithPattern(aColor.colorspace, aColor.pattern, components);
|
var copy = CGColorCreateWithPattern(aColor.colorspace, aColor.pattern, components);
|
||||||
else
|
else
|
||||||
return CGColorCreate(aColor.colorspace, components);
|
var copy = CGColorCreate(aColor.colorspace, components);
|
||||||
|
|
||||||
|
copy.components[components.length - 1] = anAlpha;
|
||||||
|
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -279,11 +275,11 @@ function CGColorGetPattern(aColor)
|
|||||||
/* var components = aColor.components;
|
/* var components = aColor.components;
|
||||||
|
|
||||||
case : _CGCSSForColor[CFGetHash(aColor)] = "rgba(" + ROUND(components[0] * 255.0) + ',' + ROUND(components[0] * 255.0) + ',' ROUND(components[0] * 255.0) + ',' + ROUND(components[0] * 255.0);
|
case : _CGCSSForColor[CFGetHash(aColor)] = "rgba(" + ROUND(components[0] * 255.0) + ',' + ROUND(components[0] * 255.0) + ',' ROUND(components[0] * 255.0) + ',' + ROUND(components[0] * 255.0);
|
||||||
_cssString = (hasAlpha ? "rgba(" : "rgb(") +
|
_cssString = (hasAlpha ? "rgba(" : "rgb(") +
|
||||||
parseInt(_components[0] * 255.0) + ", " +
|
parseInt(_components[0] * 255.0) + ", " +
|
||||||
parseInt(_components[1] * 255.0) + ", " +
|
parseInt(_components[1] * 255.0) + ", " +
|
||||||
parseInt(_components[2] * 255.0) +
|
parseInt(_components[2] * 255.0) +
|
||||||
(hasAlpha ? (", " + _components[3]) : "") + ")";
|
(hasAlpha ? (", " + _components[3]) : "") + ")";
|
||||||
|
|
||||||
function CFStringFromColor()
|
function CFStringFromColor()
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user