Compare commits

..
3453 changed files with 56783 additions and 298868 deletions
-9
View File
@@ -5,16 +5,7 @@ Demos
./Aristo ./Aristo
WebSite WebSite
.push-package .push-package
*.xcodeproj*
*.xcodeproj/*.pbxuser *.xcodeproj/*.pbxuser
*.xcodeproj/*.perspectivev3 *.xcodeproj/*.perspectivev3
xcuserdata/ xcuserdata/
!*.xcodeproj/project.pbxproj !*.xcodeproj/project.pbxproj
*.xCodeSupport/
*.XcodeSupport/
*XcodeSupport/
Tests/Manual/**/*.xcodeproj
*.sublime-project
*.sublime-workspace
*.tm_properties
*.idea
-6
View File
@@ -1,6 +0,0 @@
language: node_js
node_js:
- 0.8
install: ./bootstrap.sh --noprompt --directory ./narwhal
script: jake test
env: PATH="$TRAVIS_BUILD_DIR/narwhal/bin:$PATH" CAPP_BUILD="$TRAVIS_BUILD_DIR/Build" NARWHAL_ENGINE=rhino
+4 -19
View File
@@ -20,17 +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 "_CPObject+Theme.j"
@import "_CPToolTip.j"
@import "CALayer.j" @import "CALayer.j"
@import "CGGeometry.j"
@import "CPAccordionView.j"
@import "CPAlert.j" @import "CPAlert.j"
@import "CPAnimation.j" @import "CPAnimation.j"
@import "CPAnimationContext.j"
@import "CPAppearance.j"
@import "CPApplication.j" @import "CPApplication.j"
@import "CPArrayController.j" @import "CPArrayController.j"
@import "CPAttributedString+Additions.j"
@import "CPBezierPath.j" @import "CPBezierPath.j"
@import "CPBox.j" @import "CPBox.j"
@import "CPBrowser.j" @import "CPBrowser.j"
@@ -41,24 +36,18 @@
@import "CPCibBindingConnector.j" @import "CPCibBindingConnector.j"
@import "CPCibConnector.j" @import "CPCibConnector.j"
@import "CPCibControlConnector.j" @import "CPCibControlConnector.j"
@import "CPCibHelpConnector.j"
@import "CPCibLoading.j" @import "CPCibLoading.j"
@import "CPCibOutletConnector.j" @import "CPCibOutletConnector.j"
@import "CPCibRuntimeAttributesConnector.j"
@import "CPClipView.j" @import "CPClipView.j"
@import "CPCollectionView.j" @import "CPCollectionView.j"
@import "CPCollectionViewItem.j" @import "CPCollectionViewItem.j"
@import "CPColor.j" @import "CPColor.j"
@import "CPColorPanel.j" @import "CPColorPanel.j"
@import "CPColorSpace.j"
@import "CPColorWell.j" @import "CPColorWell.j"
@import "CPComboBox.j"
@import "CPCompatibility.j" @import "CPCompatibility.j"
@import "CPControl.j" @import "CPControl.j"
@import "CPController.j"
@import "CPCookie.j" @import "CPCookie.j"
@import "CPCursor.j" @import "CPCursor.j"
@import "CPDatePicker.j"
@import "CPDocument.j" @import "CPDocument.j"
@import "CPDocumentController.j" @import "CPDocumentController.j"
@import "CPEvent.j" @import "CPEvent.j"
@@ -66,7 +55,7 @@
@import "CPFlashView.j" @import "CPFlashView.j"
@import "CPFont.j" @import "CPFont.j"
@import "CPFontManager.j" @import "CPFontManager.j"
@import "CPGradient.j" @import "CPGeometry.j"
@import "CPGraphics.j" @import "CPGraphics.j"
@import "CPImage.j" @import "CPImage.j"
@import "CPImageView.j" @import "CPImageView.j"
@@ -78,7 +67,6 @@
@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 "CPPredicateEditor.j"
@import "CPPredicateEditorRowTemplate.j" @import "CPPredicateEditorRowTemplate.j"
@@ -96,24 +84,21 @@
@import "CPSound.j" @import "CPSound.j"
@import "CPSplitView.j" @import "CPSplitView.j"
@import "CPStepper.j" @import "CPStepper.j"
@import "CPString+Additions.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 "CPTextView.j"
@import "CPTokenField.j" @import "CPTokenField.j"
@import "CPToolbar.j" @import "CPToolbar.j"
@import "CPToolbarItem.j" @import "CPToolbarItem.j"
@import "CPTrackingArea.j" @import "_CPToolTip.j"
@import "CPTreeNode.j" @import "CPTreeNode.j"
@import "CPUserDefaultsController.j" @import "CPUserDefaultsController.j"
@import "CPView.j" @import "CPView.j"
@import "CPViewAnimator.j"
@import "CPViewAnimation.j" @import "CPViewAnimation.j"
@import "CPViewController.j" @import "CPViewController.j"
@import "CPVisualEffectView.j"
@import "CPWebView.j" @import "CPWebView.j"
@import "CPWindow.j" @import "CPWindow.j"
@import "CPWindowController.j" @import "CPWindowController.j"
@import "CPWorkspace.j"
+15 -15
View File
@@ -106,7 +106,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
_itemViews = []; _itemViews = [];
_expandedItemIndexes = [CPIndexSet indexSet]; _expandedItemIndexes = [CPIndexSet indexSet];
[self setItemHeaderPrototype:[[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 24.0)]]; [self setItemHeaderPrototype:[[CPButton alloc] initWithFrame:_CGRectMake(0.0, 0.0, 100.0, 24.0)]];
} }
return self; return self;
@@ -227,7 +227,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
{ {
var indexSet = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, _items.length)]; var indexSet = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, _items.length)];
[indexSet removeIndexes:_expandedItemIndexes]; [indexSet removeIndexes:_expandedIndexes];
return indexSet; return indexSet;
} }
@@ -258,18 +258,18 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
- (void)setFrameSize:(CGSize)aSize - (void)setFrameSize:(CGSize)aSize
{ {
var width = CGRectGetWidth([self frame]); var width = _CGRectGetWidth([self frame]);
[super setFrameSize:aSize]; [super setFrameSize:aSize];
if (width !== CGRectGetWidth([self frame])) if (width !== _CGRectGetWidth([self frame]))
[self _invalidateItemsStartingAtIndex:0]; [self _invalidateItemsStartingAtIndex:0];
} }
- (void)layoutSubviews - (void)layoutSubviews
{ {
if (_items.length <= 0) if (_items.length <= 0)
return [self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), 0.0)]; return [self setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), 0.0)];
if (_dirtyItemIndex === CPNotFound) if (_dirtyItemIndex === CPNotFound)
return; return;
@@ -278,7 +278,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
var index = _dirtyItemIndex, var index = _dirtyItemIndex,
count = _itemViews.length, count = _itemViews.length,
width = CGRectGetWidth([self bounds]), width = _CGRectGetWidth([self bounds]),
y = index > 0 ? CGRectGetMaxY([_itemViews[index - 1] frame]) : 0.0; y = index > 0 ? CGRectGetMaxY([_itemViews[index - 1] frame]) : 0.0;
// Do this now (instead of after looping), so that if we are made dirty again in the middle we don't blow this value away. // Do this now (instead of after looping), so that if we are made dirty again in the middle we don't blow this value away.
@@ -293,7 +293,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
y = CGRectGetMaxY([itemView frame]); y = CGRectGetMaxY([itemView frame]);
} }
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), y)]; [self setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), y)];
} }
@end @end
@@ -310,7 +310,7 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
- (id)initWithAccordionView:(CPAccordionView)anAccordionView - (id)initWithAccordionView:(CPAccordionView)anAccordionView
{ {
self = [super initWithFrame:CGRectMakeZero()]; self = [super initWithFrame:_CGRectMakeZero()];
if (self) if (self)
{ {
@@ -376,21 +376,21 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
- (void)setFrameY:(float)aY width:(float)aWidth - (void)setFrameY:(float)aY width:(float)aWidth
{ {
var headerHeight = CGRectGetHeight([_headerView frame]); var headerHeight = _CGRectGetHeight([_headerView frame]);
// Size to fit or something? // Size to fit or something?
[_headerView setFrameSize:CGSizeMake(aWidth, headerHeight)]; [_headerView setFrameSize:_CGSizeMake(aWidth, headerHeight)];
[_contentView setFrameOrigin:CGPointMake(0.0, headerHeight)]; [_contentView setFrameOrigin:_CGPointMake(0.0, headerHeight)];
if ([self isCollapsed]) if ([self isCollapsed])
[self setFrame:CGRectMake(0.0, aY, aWidth, headerHeight)]; [self setFrame:_CGRectMake(0.0, aY, aWidth, headerHeight)];
else else
{ {
var contentHeight = CGRectGetHeight([_contentView frame]); var contentHeight = _CGRectGetHeight([_contentView frame]);
[_contentView setFrameSize:CGSizeMake(aWidth, contentHeight)]; [_contentView setFrameSize:_CGSizeMake(aWidth, contentHeight)];
[self setFrame:CGRectMake(0.0, aY, aWidth, contentHeight + headerHeight)]; [self setFrame:_CGRectMake(0.0, aY, aWidth, contentHeight + headerHeight)];
} }
} }
+144 -302
View File
@@ -28,23 +28,15 @@
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPString.j> @import <Foundation/CPString.j>
@import "CPApplication.j"
@import "CPButton.j" @import "CPButton.j"
@import "CPColor.j" @import "CPColor.j"
@import "CPFont.j" @import "CPFont.j"
@import "CPImage.j" @import "CPImage.j"
@import "CPImageView.j" @import "CPImageView.j"
@import "CPPanel.j" @import "CPPanel.j"
@import "CPText.j"
@import "CPTextField.j" @import "CPTextField.j"
@class CPCheckBox
@global CPApp
var CPAlertDelegate_alertShowHelp_ = 1 << 0,
CPAlertDelegate_alertDidEnd_returnCode_ = 1 << 1;
@typedef CPAlertStyle
/* /*
@global @global
@group CPAlertStyle @group CPAlertStyle
@@ -61,64 +53,6 @@ CPInformationalAlertStyle = 1;
*/ */
CPCriticalAlertStyle = 2; CPCriticalAlertStyle = 2;
var bottomHeight = 71;
@protocol CPAlertDelegate <CPObject>
@optional
- (BOOL)alertShowHelp:(CPAlert)alert;
- (void)alertDidEnd:(CPAlert)theAlert returnCode:(int)returnCode;
@end
@implementation _CPAlertThemeView : CPView
+ (CPString)defaultThemeClass
{
return @"alert";
}
+ (CPDictionary)themeAttributes
{
return @{
@"size": CGSizeMake(400.0, 110.0),
@"content-inset": CGInsetMake(15, 15, 15, 50),
@"informative-offset": 6,
@"button-offset": 10,
@"message-text-alignment": CPJustifiedTextAlignment,
@"message-text-color": [CPColor blackColor],
@"message-text-font": [CPFont boldSystemFontOfSize:13.0],
@"message-text-shadow-color": [CPNull null],
@"message-text-shadow-offset": CGSizeMakeZero(),
@"informative-text-alignment": CPJustifiedTextAlignment,
@"informative-text-color": [CPColor blackColor],
@"informative-text-font": [CPFont systemFontOfSize:12.0],
@"informative-text-shadow-color": [CPNull null],
@"informative-text-shadow-offset": CGSizeMakeZero(),
@"image-offset": CGPointMake(15, 12),
@"information-image": [CPNull null],
@"warning-image": [CPNull null],
@"error-image": [CPNull null],
@"help-image": [CPNull null],
@"help-image-left-offset": 15,
@"help-image-pressed": [CPNull null],
@"suppression-button-y-offset": 0.0,
@"suppression-button-x-offset": 0.0,
@"default-elements-margin": 3.0,
@"suppression-button-text-color": [CPColor blackColor],
@"suppression-button-text-font": [CPFont systemFontOfSize:12.0],
@"suppression-button-text-shadow-color": [CPNull null],
@"suppression-button-text-shadow-offset": 0.0,
@"modal-window-button-margin-y": 0.0,
@"modal-window-button-margin-x": 0.0,
@"standard-window-button-margin-y": 0.0,
@"standard-window-button-margin-x": 0.0,
};
}
@end
/*! /*!
@ingroup appkit @ingroup appkit
@@ -141,35 +75,32 @@ var bottomHeight = 71;
representing the first button added to the alert which appears on the representing the first button added to the alert which appears on the
right, 1 representing the next button to the left and so on) right, 1 representing the next button to the left and so on)
*/ */
@implementation CPAlert : CPObject @implementation CPAlert : CPView
{ {
BOOL _showHelp @accessors(property=showsHelp); BOOL _showHelp @accessors(property=showsHelp);
BOOL _showSuppressionButton @accessors(property=showsSuppressionButton); BOOL _showSuppressionButton @accessors(property=showsSuppressionButton);
CPAlertStyle _alertStyle @accessors(property=alertStyle); CPAlertStyle _alertStyle @accessors(property=alertStyle);
CPString _title @accessors(property=title); CPString _title @accessors(property=title);
CPView _accessoryView @accessors(property=accessoryView); CPView _accessoryView @accessors(property=accessoryView);
CPImage _icon @accessors(property=icon); CPImage _icon @accessors(property=icon);
CPArray _buttons @accessors(property=buttons, readonly); CPArray _buttons @accessors(property=buttons,readonly);
CPCheckBox _suppressionButton @accessors(property=suppressionButton, readonly); CPCheckBox _suppressionButton @accessors(property=suppressionButton,readonly);
id <CPAlertDelegate> _delegate @accessors(property=delegate); id _delegate @accessors(property=delegate);
id _modalDelegate; id _modalDelegate;
SEL _didEndSelector @accessors(property=didEndSelector); SEL _didEndSelector;
Function _didEndBlock;
unsigned _implementedDelegateMethods;
_CPAlertThemeView _themeView @accessors(property=themeView, readonly); CPWindow _window @accessors(property=window,readonly);
CPWindow _window @accessors(property=window, readonly); int _defaultWindowStyle;
int _defaultWindowStyle;
CPImageView _alertImageView; CPImageView _alertImageView;
CPTextField _informativeLabel; CPTextField _informativeLabel;
CPTextField _messageLabel; CPTextField _messageLabel;
CPButton _alertHelpButton; CPButton _alertHelpButton;
BOOL _needsLayout; BOOL _needsLayout;
} }
#pragma mark Creating Alerts #pragma mark Creating Alerts
@@ -186,21 +117,21 @@ var bottomHeight = 71;
*/ */
+ (CPAlert)alertWithMessageText:(CPString)aMessage defaultButton:(CPString)defaultButtonTitle alternateButton:(CPString)alternateButtonTitle otherButton:(CPString)otherButtonTitle informativeTextWithFormat:(CPString)informativeText + (CPAlert)alertWithMessageText:(CPString)aMessage defaultButton:(CPString)defaultButtonTitle alternateButton:(CPString)alternateButtonTitle otherButton:(CPString)otherButtonTitle informativeTextWithFormat:(CPString)informativeText
{ {
var newAlert = [[self alloc] init]; var alert = [[CPAlert alloc] init];
[newAlert setMessageText:aMessage]; [alert setMessageText:aMessage];
[newAlert addButtonWithTitle:defaultButtonTitle]; [alert addButtonWithTitle:defaultButtonTitle];
if (alternateButtonTitle) if (alternateButtonTitle)
[newAlert addButtonWithTitle:alternateButtonTitle]; [alert addButtonWithTitle:alternateButtonTitle];
if (otherButtonTitle) if (otherButtonTitle)
[newAlert addButtonWithTitle:otherButtonTitle]; [alert addButtonWithTitle:otherButtonTitle];
if (informativeText) if (informativeText)
[newAlert setInformativeText:informativeText]; [alert setInformativeText:informativeText];
return newAlert; return alert;
} }
/*! /*!
@@ -211,12 +142,12 @@ var bottomHeight = 71;
*/ */
+ (CPAlert)alertWithError:(CPString)anErrorMessage + (CPAlert)alertWithError:(CPString)anErrorMessage
{ {
var newAlert = [[self alloc] init]; var alert = [[CPAlert alloc] init];
[newAlert setMessageText:anErrorMessage]; [alert setMessageText:anErrorMessage];
[newAlert setAlertStyle:CPCriticalAlertStyle]; [alert setAlertStyle:CPCriticalAlertStyle];
return newAlert; return alert;
} }
/*! /*!
@@ -232,8 +163,7 @@ var bottomHeight = 71;
_alertStyle = CPWarningAlertStyle; _alertStyle = CPWarningAlertStyle;
_showHelp = NO; _showHelp = NO;
_needsLayout = YES; _needsLayout = YES;
_defaultWindowStyle = _CPModalWindowMask; _defaultWindowStyle = CPTitledWindowMask;
_themeView = [_CPAlertThemeView new];
_messageLabel = [CPTextField labelWithTitle:@"Alert"]; _messageLabel = [CPTextField labelWithTitle:@"Alert"];
_alertImageView = [[CPImageView alloc] init]; _alertImageView = [[CPImageView alloc] init];
@@ -248,37 +178,8 @@ var bottomHeight = 71;
return self; return self;
} }
#pragma mark -
#pragma mark Delegate
/*!
Set the delegate of the receiver
@param aDelegate the delegate object for the alert.
*/
- (void)setDelegate:(id <CPAlertDelegate>)aDelegate
{
if (_delegate === aDelegate)
return;
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(alertShowHelp:)])
_implementedDelegateMethods |= CPAlertDelegate_alertShowHelp_;
if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
_implementedDelegateMethods |= CPAlertDelegate_alertDidEnd_returnCode_;
}
#pragma mark Accessors #pragma mark Accessors
- (CPTheme)theme
{
return [_themeView theme];
}
/*! /*!
set the theme to use set the theme to use
@@ -296,29 +197,19 @@ var bottomHeight = 71;
_window = nil; // will be regenerated at next layout _window = nil; // will be regenerated at next layout
_needsLayout = YES; _needsLayout = YES;
[_themeView setTheme:aTheme]; [super setTheme:aTheme];
} }
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName /*! @deprecated
{ */
[_themeView setValue:aValue forThemeAttribute:aName]; - (void)setWindowStyle:(int)aStyle
}
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState
{
[_themeView setValue:aValue forThemeAttribute:aName inState:aState];
}
/*! @deprecated */
- (void)setWindowStyle:(int)style
{ {
CPLog.warn("DEPRECATED: setWindowStyle: is deprecated. use setTheme: instead"); CPLog.warn("DEPRECATED: setWindowStyle: is deprecated. use setTheme: instead");
[self setTheme:(aStyle === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
[self setTheme:(style === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
} }
/*! @deprecated */ /*! @deprecated
*/
- (int)windowStyle - (int)windowStyle
{ {
CPLog.warn("DEPRECATED: windowStyle: is deprecated. use theme instead"); CPLog.warn("DEPRECATED: windowStyle: is deprecated. use theme instead");
@@ -327,19 +218,18 @@ var bottomHeight = 71;
/*! /*!
Set the text of the alert's message. set the text of the alert's message
@param aText CPString containing the text @param aText CPString containing the text
*/ */
- (void)setMessageText:(CPString)text - (void)setMessageText:(CPString)aText
{ {
[_messageLabel setStringValue:text]; [_messageLabel setStringValue:aText];
_needsLayout = YES; _needsLayout = YES;
} }
/*! /*!
Return the content of the message text. return the content of the message text
@return CPString containing the message text @return CPString containing the message text
*/ */
- (CPString)messageText - (CPString)messageText
@@ -348,13 +238,13 @@ var bottomHeight = 71;
} }
/*! /*!
Set the text of the alert's informative text. set the text of the alert's informative text
@param aText CPString containing the informative text @param aText CPString containing the informative text
*/ */
- (void)setInformativeText:(CPString)text - (void)setInformativeText:(CPString)aText
{ {
[_informativeLabel setStringValue:text]; [_informativeLabel setStringValue:aText];
_needsLayout = YES; _needsLayout = YES;
} }
@@ -371,7 +261,6 @@ var bottomHeight = 71;
/*! /*!
Sets the title of the alert window. Sets the title of the alert window.
This API is not present in Cocoa. This API is not present in Cocoa.
@param aTitle CPString containing the window title @param aTitle CPString containing the window title
*/ */
- (void)setTitle:(CPString)aTitle - (void)setTitle:(CPString)aTitle
@@ -381,7 +270,7 @@ var bottomHeight = 71;
} }
/*! /*!
Set the accessory view. set the accessory view
@param aView the accessory view @param aView the accessory view
*/ */
@@ -392,7 +281,7 @@ var bottomHeight = 71;
} }
/*! /*!
Set if the alert shows the suppression button. set if alert shows the suppression button
@param shouldShowSuppressionButton YES or NO @param shouldShowSuppressionButton YES or NO
*/ */
@@ -445,16 +334,16 @@ var bottomHeight = 71;
*/ */
- (void)_layoutMessageView - (void)_layoutMessageView
{ {
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"], var inset = [self currentValueForThemeAttribute:@"content-inset"],
sizeWithFontCorrection = 6.0, sizeWithFontCorrection = 6.0,
messageLabelWidth, messageLabelWidth,
messageLabelTextSize; messageLabelTextSize;
[_messageLabel setTextColor:[_themeView currentValueForThemeAttribute:@"message-text-color"]]; [_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]];
[_messageLabel setFont:[_themeView currentValueForThemeAttribute:@"message-text-font"]]; [_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]];
[_messageLabel setTextShadowColor:[_themeView currentValueForThemeAttribute:@"message-text-shadow-color"]]; [_messageLabel setTextShadowColor:[self currentValueForThemeAttribute:@"message-text-shadow-color"]];
[_messageLabel setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"message-text-shadow-offset"]]; [_messageLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"message-text-shadow-offset"]];
[_messageLabel setAlignment:[_themeView currentValueForThemeAttribute:@"message-text-alignment"]]; [_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]];
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping]; [_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
messageLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right; messageLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right;
@@ -468,22 +357,22 @@ var bottomHeight = 71;
*/ */
- (void)_layoutInformativeView - (void)_layoutInformativeView
{ {
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"], var inset = [self currentValueForThemeAttribute:@"content-inset"],
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"], defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
sizeWithFontCorrection = 6.0, sizeWithFontCorrection = 6.0,
informativeLabelWidth, informativeLabelWidth,
informativeLabelOriginY, informativeLabelOriginY,
informativeLabelTextSize; informativeLabelTextSize;
[_informativeLabel setTextColor:[_themeView currentValueForThemeAttribute:@"informative-text-color"]]; [_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]];
[_informativeLabel setFont:[_themeView currentValueForThemeAttribute:@"informative-text-font"]]; [_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]];
[_informativeLabel setTextShadowColor:[_themeView currentValueForThemeAttribute:@"informative-text-shadow-color"]]; [_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]];
[_informativeLabel setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"informative-text-shadow-offset"]]; [_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
[_informativeLabel setAlignment:[_themeView currentValueForThemeAttribute:@"informative-text-alignment"]]; [_informativeLabel setAlignment:[self currentValueForThemeAttribute:@"informative-text-alignment"]];
[_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping]; [_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
informativeLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right; informativeLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right,
informativeLabelOriginY = [_messageLabel frameOrigin].y + [_messageLabel frameSize].height + defaultElementsMargin; informativeLabelOriginY = [_messageLabel frameOrigin].y + [_messageLabel frameSize].height + defaultElementsMargin,
informativeLabelTextSize = [[_informativeLabel stringValue] sizeWithFont:[_informativeLabel font] inWidth:informativeLabelWidth]; informativeLabelTextSize = [[_informativeLabel stringValue] sizeWithFont:[_informativeLabel font] inWidth:informativeLabelWidth];
[_informativeLabel setFrame:CGRectMake(inset.left, informativeLabelOriginY, informativeLabelTextSize.width, informativeLabelTextSize.height + sizeWithFontCorrection)]; [_informativeLabel setFrame:CGRectMake(inset.left, informativeLabelOriginY, informativeLabelTextSize.width, informativeLabelTextSize.height + sizeWithFontCorrection)];
@@ -497,8 +386,8 @@ var bottomHeight = 71;
if (!_accessoryView) if (!_accessoryView)
return; return;
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"], var inset = [self currentValueForThemeAttribute:@"content-inset"],
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"], defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
accessoryViewWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right, accessoryViewWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right,
accessoryViewOriginY = CGRectGetMaxY([_informativeLabel frame]) + defaultElementsMargin; accessoryViewOriginY = CGRectGetMaxY([_informativeLabel frame]) + defaultElementsMargin;
@@ -514,16 +403,16 @@ var bottomHeight = 71;
if (!_showSuppressionButton) if (!_showSuppressionButton)
return; return;
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"], var inset = [self currentValueForThemeAttribute:@"content-inset"],
suppressionViewXOffset = [_themeView currentValueForThemeAttribute:@"suppression-button-x-offset"], suppressionViewXOffset = [self currentValueForThemeAttribute:@"suppression-button-x-offset"],
suppressionViewYOffset = [_themeView currentValueForThemeAttribute:@"suppression-button-y-offset"], suppressionViewYOffset = [self currentValueForThemeAttribute:@"suppression-button-y-offset"],
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"], defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
suppressionButtonViewOriginY = CGRectGetMaxY([(_accessoryView || _informativeLabel) frame]) + defaultElementsMargin + suppressionViewYOffset; suppressionButtonViewOriginY = CGRectGetMaxY([(_accessoryView || _informativeLabel) frame]) + defaultElementsMargin + suppressionViewYOffset;
[_suppressionButton setTextColor:[_themeView currentValueForThemeAttribute:@"suppression-button-text-color"]]; [_suppressionButton setTextColor:[self currentValueForThemeAttribute:@"suppression-button-text-color"]];
[_suppressionButton setFont:[_themeView currentValueForThemeAttribute:@"suppression-button-text-font"]]; [_suppressionButton setFont:[self currentValueForThemeAttribute:@"suppression-button-text-font"]];
[_suppressionButton setTextShadowColor:[_themeView currentValueForThemeAttribute:@"suppression-button-text-shadow-color"]]; [_suppressionButton setTextShadowColor:[self currentValueForThemeAttribute:@"suppression-button-text-shadow-color"]];
[_suppressionButton setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"suppression-button-text-shadow-offset"]]; [_suppressionButton setTextShadowOffset:[self currentValueForThemeAttribute:@"suppression-button-text-shadow-offset"]];
[_suppressionButton sizeToFit]; [_suppressionButton sizeToFit];
[_suppressionButton setFrameOrigin:CGPointMake(inset.left + suppressionViewXOffset, suppressionButtonViewOriginY)]; [_suppressionButton setFrameOrigin:CGPointMake(inset.left + suppressionViewXOffset, suppressionButtonViewOriginY)];
@@ -535,17 +424,14 @@ var bottomHeight = 71;
*/ */
- (CGSize)_layoutButtonsFromView:(CPView)lastView - (CGSize)_layoutButtonsFromView:(CPView)lastView
{ {
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"], var inset = [self currentValueForThemeAttribute:@"content-inset"],
minimumSize = [_themeView currentValueForThemeAttribute:@"size"], minimumSize = [self currentValueForThemeAttribute:@"size"],
buttonOffset = [_themeView currentValueForThemeAttribute:@"button-offset"], buttonOffset = [self currentValueForThemeAttribute:@"button-offset"],
helpLeftOffset = [_themeView currentValueForThemeAttribute:@"help-image-left-offset"], helpLeftOffset = [self currentValueForThemeAttribute:@"help-image-left-offset"],
aRepresentativeButton = [_buttons objectAtIndex:0], aRepresentativeButton = [_buttons objectAtIndex:0],
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"], defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
panelSize = [[_window contentView] frame].size, panelSize = [[_window contentView] frame].size,
buttonsOriginY, buttonsOriginY,
buttonMarginY,
buttonMarginX,
theme = [self theme],
offsetX; offsetX;
[aRepresentativeButton setTheme:[self theme]]; [aRepresentativeButton setTheme:[self theme]];
@@ -558,19 +444,6 @@ var bottomHeight = 71;
buttonsOriginY = panelSize.height - [aRepresentativeButton frameSize].height + buttonOffset; buttonsOriginY = panelSize.height - [aRepresentativeButton frameSize].height + buttonOffset;
offsetX = panelSize.width - inset.right; offsetX = panelSize.width - inset.right;
switch ([_window styleMask])
{
case _CPModalWindowMask:
buttonMarginY = [_themeView currentValueForThemeAttribute:@"modal-window-button-margin-y"];
buttonMarginX = [_themeView currentValueForThemeAttribute:@"modal-window-button-margin-x"];
break;
default:
buttonMarginY = [_themeView currentValueForThemeAttribute:@"standard-window-button-margin-y"];
buttonMarginX = [_themeView currentValueForThemeAttribute:@"standard-window-button-margin-x"];
break;
}
for (var i = [_buttons count] - 1; i >= 0 ; i--) for (var i = [_buttons count] - 1; i >= 0 ; i--)
{ {
var button = _buttons[i]; var button = _buttons[i];
@@ -582,14 +455,14 @@ var bottomHeight = 71;
height = CGRectGetHeight(buttonFrame); height = CGRectGetHeight(buttonFrame);
offsetX -= width; offsetX -= width;
[button setFrame:CGRectMake(offsetX + buttonMarginX, buttonsOriginY + buttonMarginY, width, height)]; [button setFrame:CGRectMake(offsetX, buttonsOriginY, width, height)];
offsetX -= 10; offsetX -= 10;
} }
if (_showHelp) if (_showHelp)
{ {
var helpImage = [_themeView currentValueForThemeAttribute:@"help-image"], var helpImage = [self currentValueForThemeAttribute:@"help-image"],
helpImagePressed = [_themeView currentValueForThemeAttribute:@"help-image-pressed"], helpImagePressed = [self currentValueForThemeAttribute:@"help-image-pressed"],
helpImageSize = helpImage ? [helpImage size] : CGSizeMakeZero(), helpImageSize = helpImage ? [helpImage size] : CGSizeMakeZero(),
helpFrame = CGRectMake(helpLeftOffset, buttonsOriginY, helpImageSize.width, helpImageSize.height); helpFrame = CGRectMake(helpLeftOffset, buttonsOriginY, helpImageSize.width, helpImageSize.height);
@@ -614,7 +487,7 @@ var bottomHeight = 71;
if (!_window) if (!_window)
[self _createWindowWithStyle:nil]; [self _createWindowWithStyle:nil];
var iconOffset = [_themeView currentValueForThemeAttribute:@"image-offset"], var iconOffset = [self currentValueForThemeAttribute:@"image-offset"],
theImage = _icon, theImage = _icon,
finalSize; finalSize;
@@ -622,13 +495,13 @@ var bottomHeight = 71;
switch (_alertStyle) switch (_alertStyle)
{ {
case CPWarningAlertStyle: case CPWarningAlertStyle:
theImage = [_themeView currentValueForThemeAttribute:@"warning-image"]; theImage = [self currentValueForThemeAttribute:@"warning-image"];
break; break;
case CPInformationalAlertStyle: case CPInformationalAlertStyle:
theImage = [_themeView currentValueForThemeAttribute:@"information-image"]; theImage = [self currentValueForThemeAttribute:@"information-image"];
break; break;
case CPCriticalAlertStyle: case CPCriticalAlertStyle:
theImage = [_themeView currentValueForThemeAttribute:@"error-image"]; theImage = [self currentValueForThemeAttribute:@"error-image"];
break; break;
} }
@@ -646,7 +519,7 @@ var bottomHeight = 71;
if (_showSuppressionButton) if (_showSuppressionButton)
lastView = _suppressionButton; lastView = _suppressionButton;
else if (_accessoryView) else if (_accessoryView)
lastView = _accessoryView; lastView = _accessoryView
finalSize = [self _layoutButtonsFromView:lastView]; finalSize = [self _layoutButtonsFromView:lastView];
if ([_window styleMask] & CPDocModalWindowMask) if ([_window styleMask] & CPDocModalWindowMask)
@@ -655,12 +528,6 @@ var bottomHeight = 71;
[_window setFrameSize:finalSize]; [_window setFrameSize:finalSize];
[_window center]; [_window center];
if ([_window styleMask] & _CPModalWindowMask || [_window styleMask] & CPHUDBackgroundWindowMask)
{
[_window setMovable:YES];
[_window setMovableByWindowBackground:YES];
}
_needsLayout = NO; _needsLayout = NO;
} }
@@ -683,17 +550,6 @@ var bottomHeight = 71;
[CPApp runModalForWindow:_window]; [CPApp runModalForWindow:_window];
} }
/*!
The same as \c runModal, but executes the code in \c block when the
alert is dismissed.
*/
- (void)runModalWithDidEndBlock:(Function /*(CPAlert alert, int returnCode)*/)block
{
_didEndBlock = block;
[self runModal];
}
/*! /*!
Runs the receiver modally as an alert sheet attached to a specified window. Runs the receiver modally as an alert sheet attached to a specified window.
@@ -728,20 +584,6 @@ var bottomHeight = 71;
[self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil]; [self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
} }
/*!
Runs the receiver modally as an alert sheet attached to a specified window.
Executes the code in \c block when the alert is dismissed.
@param window The parent window for the sheet.
@param block Code block to execute on dismissal
*/
- (void)beginSheetModalForWindow:(CPWindow)aWindow didEndBlock:(Function /*(CPAlert alert, int returnCode)*/)block
{
_didEndBlock = block;
[self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
}
#pragma mark Private #pragma mark Private
/*! /*!
@@ -750,11 +592,9 @@ var bottomHeight = 71;
- (void)_createWindowWithStyle:(int)forceStyle - (void)_createWindowWithStyle:(int)forceStyle
{ {
var frame = CGRectMakeZero(); var frame = CGRectMakeZero();
frame.size = [_themeView currentValueForThemeAttribute:@"size"]; frame.size = [self currentValueForThemeAttribute:@"size"];
_window = [[CPPanel alloc] initWithContentRect:frame styleMask:forceStyle || _defaultWindowStyle]; _window = [[CPWindow alloc] initWithContentRect:frame styleMask:forceStyle || _defaultWindowStyle];
[_window setLevel:CPStatusWindowLevel];
[_window setPlatformWindow:[[CPApp keyWindow] platformWindow]];
if (_title) if (_title)
[_window setTitle:_title]; [_window setTitle:_title];
@@ -781,7 +621,8 @@ var bottomHeight = 71;
*/ */
- (@action)_showHelp:(id)aSender - (@action)_showHelp:(id)aSender
{ {
[self _sendDelegateAlertShowHelp]; if ([_delegate respondsToSelector:@selector(alertShowHelp:)])
[_delegate alertShowHelp:self];
} }
/* /*
@@ -790,10 +631,7 @@ var bottomHeight = 71;
- (@action)_takeReturnCodeFrom:(id)aSender - (@action)_takeReturnCodeFrom:(id)aSender
{ {
if ([_window isSheet]) if ([_window isSheet])
{
[_window orderOut:nil];
[CPApp endSheet:_window returnCode:[aSender tag]]; [CPApp endSheet:_window returnCode:[aSender tag]];
}
else else
{ {
[CPApp abortModal]; [CPApp abortModal];
@@ -808,57 +646,61 @@ var bottomHeight = 71;
*/ */
- (void)_alertDidEnd:(CPWindow)aWindow returnCode:(int)returnCode contextInfo:(id)contextInfo - (void)_alertDidEnd:(CPWindow)aWindow returnCode:(int)returnCode contextInfo:(id)contextInfo
{ {
if (_didEndBlock) if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
{ [_delegate alertDidEnd:self returnCode:returnCode];
if (typeof(_didEndBlock) === "function")
_didEndBlock(self, returnCode);
else
CPLog.warn("%s: didEnd block is not a function", [self description]);
// didEnd blocks are transient if (_didEndSelector)
_didEndBlock = nil; objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
}
else if (_modalDelegate) _modalDelegate = nil;
{ _didEndSelector = nil;
if (_didEndSelector) }
_modalDelegate.isa.objj_msgSend3(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
} #pragma mark Theme Attributes
else if (_delegate)
{ + (CPString)defaultThemeClass
if (_didEndSelector) {
_delegate.isa.objj_msgSend2(_delegate, _didEndSelector, self, returnCode); return @"alert";
else }
[self _sendDelegateAlertDidEndReturnCode:returnCode];
} + (id)themeAttributes
} {
return [CPDictionary dictionaryWithObjects:[CGSizeMake(400.0, 110.0), CGInsetMake(15, 15, 15, 50), 6, 10,
@end CPJustifiedTextAlignment, [CPColor blackColor], [CPFont boldSystemFontOfSize:13.0], [CPNull null], CGSizeMakeZero(),
CPJustifiedTextAlignment, [CPColor blackColor], [CPFont systemFontOfSize:12.0], [CPNull null], CGSizeMakeZero(),
CGPointMake(15, 12),
@implementation CPAlert (CPAlertDelegate) [CPNull null],
[CPNull null],
/*! [CPNull null],
@ignore [CPNull null],
Call the delegate alertDidEnd:returnCode [CPNull null],
*/ [CPNull null],
- (void)_sendDelegateAlertDidEndReturnCode:(int)returnCode 0.0,
{ 0.0,
if (!(_implementedDelegateMethods & CPAlertDelegate_alertDidEnd_returnCode_)) 3.0,
return; [CPColor blackColor],
[CPFont systemFontOfSize:12.0],
[_delegate alertDidEnd:self returnCode:returnCode]; [CPNull null],
} 0.0
]
/*! forKeys:[@"size", @"content-inset", @"informative-offset", @"button-offset",
@ignore @"message-text-alignment", @"message-text-color", @"message-text-font", @"message-text-shadow-color", @"message-text-shadow-offset",
Call the delegate alertShowHelp: @"informative-text-alignment", @"informative-text-color", @"informative-text-font", @"informative-text-shadow-color", @"informative-text-shadow-offset",
*/ @"image-offset",
- (BOOL)_sendDelegateAlertShowHelp @"information-image",
{ @"warning-image",
if (!(_implementedDelegateMethods & CPAlertDelegate_alertShowHelp_)) @"error-image",
return YES; @"help-image",
@"help-image-left-offset",
return [_delegate alertShowHelp:self]; @"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"
]];
} }
@end @end
+45 -135
View File
@@ -26,26 +26,25 @@
@import "CAMediaTimingFunction.j" @import "CAMediaTimingFunction.j"
@protocol CPAnimationDelegate <CPObject> /*
@global
@optional @group CPAnimationCurve
- (BOOL)animationShouldStart:(CPAnimation)animation; */
- (float)animation:(CPAnimation)animation valueForProgress:(float)progress;
- (void)animationDidEnd:(CPAnimation)animation;
- (void)animationDidStop:(CPAnimation)animation;
@end
var CPAnimationDelegate_animationShouldStart_ = 1 << 1,
CPAnimationDelegate_animation_valueForProgress_ = 1 << 2,
CPAnimationDelegate_animationDidEnd_ = 1 << 3,
CPAnimationDelegate_animationDidStop_ = 1 << 4;
@typedef CPAnimationCurve
CPAnimationEaseInOut = 0; CPAnimationEaseInOut = 0;
/*
@global
@group CPAnimationCurve
*/
CPAnimationEaseIn = 1; CPAnimationEaseIn = 1;
/*
@global
@group CPAnimationCurve
*/
CPAnimationEaseOut = 2; CPAnimationEaseOut = 2;
/*
@global
@group CPAnimationCurve
*/
CPAnimationLinear = 3; CPAnimationLinear = 3;
ACTUAL_FRAME_RATE = 0; ACTUAL_FRAME_RATE = 0;
@@ -81,18 +80,17 @@ ACTUAL_FRAME_RATE = 0;
*/ */
@implementation CPAnimation : CPObject @implementation CPAnimation : CPObject
{ {
CPTimeInterval _lastTime; CPTimeInterval _lastTime;
CPTimeInterval _duration; CPTimeInterval _duration;
CPAnimationCurve _animationCurve; CPAnimationCurve _animationCurve;
CAMediaTimingFunction _timingFunction; CAMediaTimingFunction _timingFunction;
float _frameRate; float _frameRate;
float _progress; float _progress;
id <CPAnimationDelegate> _delegate; id _delegate;
CPTimer _timer; CPTimer _timer;
unsigned _implementedDelegateMethods;
} }
/*! /*!
@@ -124,30 +122,23 @@ ACTUAL_FRAME_RATE = 0;
*/ */
- (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve - (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve
{ {
var timingFunctionName;
switch (anAnimationCurve) switch (anAnimationCurve)
{ {
case CPAnimationEaseInOut: case CPAnimationEaseInOut: timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut;
timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut; break;
break;
case CPAnimationEaseIn: case CPAnimationEaseIn: timingFunctionName = kCAMediaTimingFunctionEaseIn;
timingFunctionName = kCAMediaTimingFunctionEaseIn; break;
break;
case CPAnimationEaseOut: case CPAnimationEaseOut: timingFunctionName = kCAMediaTimingFunctionEaseOut;
timingFunctionName = kCAMediaTimingFunctionEaseOut; break;
break;
case CPAnimationLinear: case CPAnimationLinear: timingFunctionName = kCAMediaTimingFunctionLinear;
timingFunctionName = kCAMediaTimingFunctionLinear; break;
break;
default: default: [CPException raise:CPInvalidArgumentException
[CPException raise:CPInvalidArgumentException reason:"Invalid value provided for animation curve"];
reason:@"Invalid value provided for animation curve"]; break;
break;
} }
_animationCurve = anAnimationCurve; _animationCurve = anAnimationCurve;
@@ -216,25 +207,9 @@ ACTUAL_FRAME_RATE = 0;
Sets the animation's delegate. Sets the animation's delegate.
@param aDelegate the new delegate @param aDelegate the new delegate
*/ */
- (void)setDelegate:(id <CPAnimationDelegate>)aDelegate - (void)setDelegate:(id)aDelegate
{ {
if (_delegate === aDelegate)
return;
_delegate = aDelegate; _delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(animationShouldStart:)])
_implementedDelegateMethods |= CPAnimationDelegate_animationShouldStart_;
if ([_delegate respondsToSelector:@selector(animationDidEnd:)])
_implementedDelegateMethods |= CPAnimationDelegate_animationDidEnd_;
if ([_delegate respondsToSelector:@selector(animationDidStop:)])
_implementedDelegateMethods |= CPAnimationDelegate_animationDidStop_;
if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
_implementedDelegateMethods |= CPAnimationDelegate_animation_valueForProgress_;
} }
/*! /*!
@@ -245,7 +220,7 @@ ACTUAL_FRAME_RATE = 0;
- (void)startAnimation - (void)startAnimation
{ {
// If we're already animating, or our delegate stops us, animate. // If we're already animating, or our delegate stops us, animate.
if (_timer || ![self _sendDelegateAnimationShouldStart]) if (_timer || _delegate && [_delegate respondsToSelector:@selector(animationShouldStart:)] && ![_delegate animationShouldStart:self])
return; return;
if (_progress === 1.0) if (_progress === 1.0)
@@ -254,9 +229,7 @@ ACTUAL_FRAME_RATE = 0;
ACTUAL_FRAME_RATE = 0; ACTUAL_FRAME_RATE = 0;
_lastTime = new Date(); _lastTime = new Date();
var timerInterval = _frameRate <= 0.0 ? 0.0001 : 1.0 / _frameRate; _timer = [CPTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(animationTimerDidFire:) userInfo:nil repeats:YES];
_timer = [CPTimer scheduledTimerWithTimeInterval:timerInterval target:self selector:@selector(animationTimerDidFire:) userInfo:nil repeats:YES];
} }
/* /*
@@ -278,7 +251,8 @@ ACTUAL_FRAME_RATE = 0;
[_timer invalidate]; [_timer invalidate];
_timer = nil; _timer = nil;
[self _sendDelegateAnimationDidEnd]; if ([_delegate respondsToSelector:@selector(animationDidEnd:)])
[_delegate animationDidEnd:self];
} }
} }
@@ -293,7 +267,8 @@ ACTUAL_FRAME_RATE = 0;
[_timer invalidate]; [_timer invalidate];
_timer = nil; _timer = nil;
[self _sendDelegateAnimationDidStop]; if ([_delegate respondsToSelector:@selector(animationDidStop:)])
[_delegate animationDidStop:self];
} }
/*! /*!
@@ -329,11 +304,8 @@ ACTUAL_FRAME_RATE = 0;
{ {
var t = [self currentProgress]; var t = [self currentProgress];
if ([self _delegateRespondsToAnimationValueForProgress]) if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
return [self _sendDelegateAnimationValueForProgress:t]; return [_delegate animation:self valueForProgress:t];
if (_animationCurve == CPAnimationLinear)
return t;
var c1 = [], var c1 = [],
c2 = []; c2 = [];
@@ -346,72 +318,10 @@ ACTUAL_FRAME_RATE = 0;
@end @end
@implementation CPAnimation (CPAnimationDelegate)
/*!
@ignore
Check if the delegate responds to animation:valueForProgress:
*/
- (BOOL)_delegateRespondsToAnimationValueForProgress
{
return _implementedDelegateMethods & CPAnimationDelegate_animation_valueForProgress_;
}
/*!
@ignore
Call delegate animationShouldStart:
*/
- (BOOL)_sendDelegateAnimationShouldStart
{
if (!(_implementedDelegateMethods & CPAnimationDelegate_animationShouldStart_))
return YES;
return [_delegate animationShouldStart:self];
}
/*!
@ignore
Call delegate animation:valueForProgress:
*/
- (float)_sendDelegateAnimationValueForProgress:(float)aProgress
{
if (!(_implementedDelegateMethods & CPAnimationDelegate_animation_valueForProgress_))
return aProgress;
return [_delegate animation:self valueForProgress:aProgress];
}
/*!
@ignore
Call delegate animationDidEnd:
*/
- (void)_sendDelegateAnimationDidEnd
{
if (!(_implementedDelegateMethods & CPAnimationDelegate_animationDidEnd_))
return;
[_delegate animationDidEnd:self];
}
/*!
@ignore
Call delegate animationDidStop:
*/
- (void)_sendDelegateAnimationDidStop
{
if (!(_implementedDelegateMethods & CPAnimationDelegate_animationDidStop_))
return;
[_delegate animationDidStop:self];
}
@end
// currently used function to determine time // currently used function to determine time
// 1:1 conversion to js from webkit source files // 1:1 conversion to js from webkit source files
// UnitBezier.h, WebCore_animation_AnimationBase.cpp // UnitBezier.h, WebCore_animation_AnimationBase.cpp
var CubicBezierAtTime = function(t, p1x, p1y, p2x, p2y, duration) var CubicBezierAtTime = function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration)
{ {
var ax = 0, var ax = 0,
bx = 0, bx = 0,
-162
View File
@@ -1,162 +0,0 @@
/*
* CPAppearance.j
* AppKit
*
* Created by Antoine Mercadal.
* Copyright 2015, Cappuccino Project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPTheme.j"
CPAppearanceNameAqua = @"CPAppearanceNameAqua";
CPAppearanceNameLightContent = @"CPAppearanceNameLightContent";
CPAppearanceNameVibrantDark = @"CPAppearanceNameVibrantDark";
CPAppearanceNameVibrantLight = @"CPAppearanceNameVibrantLight";
var _CPAppearanceCurrent = nil,
_CPAppearancesRegistry = @{};
@protocol CPAppearanceCustomization <CPObject>
@required
- (CPAppearance)appearance;
- (void)setAppearance:(CPAppearance)appearance;
- (CPAppearance)effectiveAppearance;
- (void)setEffectiveAppearance:(CPAppearance)appearance;
@end
CPThemeStateAppearanceAqua = CPThemeState("appearance-aqua");
CPThemeStateAppearanceLightContent = CPThemeState("appearance-light-content");
CPThemeStateAppearanceVibrantLight = CPThemeState("appearance-vibrant-light");
CPThemeStateAppearanceVibrantDark = CPThemeState("appearance-vibrant-dark");
/*!
@ingroup appkit
A CPAppareance represents the appearance of an to a subset of UI elements.
This is a very lightweight implementation of the NSAppearance system, but
We are using it for compliance, and especially for the CPVisualEffectView
*/
@implementation CPAppearance : CPObject
{
BOOL _allowsVibrancy @accessors(property=allowsVibrancy);
CPString _name;
}
#pragma mark -
#pragma mark Class Methods
/*! Returns the current default CPAppearance
*/
+ (CPAppearance)currentAppearance
{
if (!_CPAppearanceCurrent)
_CPAppearanceCurrent = [CPAppearance appearanceNamed:CPAppearanceNameAqua];
return _CPAppearanceCurrent;
}
/*! Sets the current default CPAppearance
@param appearance the new current appearance
*/
+ (void)setCurrentAppearance:(CPAppearance)anAppearance
{
_CPAppearanceCurrent = anAppearance;
}
/*! Returns the CPAppearance object with the given name
@param name the name of the appearance
*/
+ (CPAppearance)appearanceNamed:(CPString)aName
{
if (![_CPAppearancesRegistry containsKey:aName])
{
[_CPAppearancesRegistry setObject:[[CPAppearance alloc] initWithAppearanceNamed:aName bundle:nil]
forKey:aName];
}
return [_CPAppearancesRegistry objectForKey:aName];
}
#pragma mark -
#pragma mark Initialization
/*! Creates a CPAppearance object initialized to the specified appearance file in the specified bundle
This method does actually nothing special. It just creates a default appearance object
*/
- (id)initWithAppearanceNamed:(CPString)aName bundle:(CPBundle)bundle
{
if (self = [super init])
{
_name = aName;
_allowsVibrancy = YES;
if ([_CPAppearancesRegistry containsKey:aName])
[CPException raise:CPInternalInconsistencyException reason:"Appearance with name '" + aName + "' is already declared."];
[_CPAppearancesRegistry setObject:self forKey:aName];
}
return self;
}
#pragma mark -
#pragma mark Implementation
- (BOOL)isEqual:(id)anObject
{
if (![anObject isKindOfClass:CPAppearance])
return NO;
return self._name == anObject._name;
}
- (CPString)description
{
return @"<CPAppearance @" + [self UID] + @" name: " + _name + ">";
}
#pragma mark -
#pragma mark CPCoding
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super init])
{
_name = [aCoder decodeObjectForKey:@"_name"];
_allowsVibrancy = [aCoder decodeBoolForKey:@"_allowsVibrancy"];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:_name forKey:@"_name"];
[aCoder encodeBool:_allowsVibrancy forKey:@"_allowsVibrancy"];
}
@end
+152 -284
View File
@@ -22,46 +22,36 @@
@import <Foundation/CPBundle.j> @import <Foundation/CPBundle.j>
@import "CPApplication_Constants.j"
@import "CPCompatibility.j" @import "CPCompatibility.j"
@import "CPColorPanel.j"
@import "CPCursor.j"
@import "CPDocumentController.j"
@import "CPEvent.j" @import "CPEvent.j"
@import "CPMenu.j" @import "CPMenu.j"
@import "CPResponder.j" @import "CPResponder.j"
@import "CPDocumentController.j"
@import "CPThemeBlend.j" @import "CPThemeBlend.j"
@import "CPCibLoading.j" @import "CPCibLoading.j"
@import "CPPanel.j"
@import "CPPlatform.j" @import "CPPlatform.j"
@import "CPWindowController.j"
@import "_CPPopoverWindow.j"
@typedef CPModalSession
var CPMainCibFile = @"CPMainCibFile", var CPMainCibFile = @"CPMainCibFile",
CPMainCibFileHumanFriendly = @"Main cib file base name", CPMainCibFileHumanFriendly = @"Main cib file base name";
CPEventModifierFlags = 0;
CPApp = nil;
@protocol CPApplicationDelegate <CPObject> CPApplicationWillFinishLaunchingNotification = @"CPApplicationWillFinishLaunchingNotification";
CPApplicationDidFinishLaunchingNotification = @"CPApplicationDidFinishLaunchingNotification";
CPApplicationWillTerminateNotification = @"CPApplicationWillTerminateNotification";
CPApplicationWillBecomeActiveNotification = @"CPApplicationWillBecomeActiveNotification";
CPApplicationDidBecomeActiveNotification = @"CPApplicationDidBecomeActiveNotification";
CPApplicationWillResignActiveNotification = @"CPApplicationWillResignActiveNotification";
CPApplicationDidResignActiveNotification = @"CPApplicationDidResignActiveNotification";
@optional CPTerminateNow = YES;
- (CPApplicationTerminateReply)applicationShouldTerminate:(CPApplication)sender; CPTerminateCancel = NO;
- (CPString)applicationShouldTerminateMessage:(CPApplication)sender; CPTerminateLater = -1; // not currently supported
- (void)applicationDidBecomeActive:(CPNotification)aNotification;
- (void)applicationDidChangeScreenParameters:(CPNotification)aNotification;
- (void)applicationDidFinishLaunching:(CPNotification)aNotification;
- (void)applicationDidResignActive:(CPNotification)aNotification;
- (void)applicationWillBecomeActive:(CPNotification)aNotification;
- (void)applicationWillFinishLaunching:(CPNotification)aNotification;
- (void)applicationWillResignActive:(CPNotification)aNotification;
- (void)applicationWillTerminate:(CPNotification)aNotification;
@end CPRunStoppedResponse = -1000;
CPRunAbortedResponse = -1001;
var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0, CPRunContinuesResponse = -1002;
CPApplicationDelegate_applicationShouldTerminateMessage_ = 1 << 1;
/*! /*!
@ingroup appkit @ingroup appkit
@@ -90,38 +80,34 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
*/ */
@implementation CPApplication : CPResponder @implementation CPApplication : CPResponder
{ {
CPArray _eventListeners; CPArray _eventListeners;
int _eventListenerInsertionIndex;
CPEvent _currentEvent; CPEvent _currentEvent;
CPWindow _lastMouseMoveWindow;
CPArray _windows; CPArray _windows;
CPWindow _keyWindow; CPWindow _keyWindow;
CPWindow _mainWindow; CPWindow _mainWindow;
CPWindow _previousKeyWindow; CPWindow _previousKeyWindow;
CPWindow _previousMainWindow; CPWindow _previousMainWindow;
CPDocumentController _documentController; CPDocumentController _documentController;
CPModalSession _currentSession; CPModalSession _currentSession;
// //
id <CPApplicationDelegate> _delegate; id _delegate;
CPInteger _implementedDelegateMethods; BOOL _finishedLaunching;
BOOL _isActive;
BOOL _finishedLaunching; CPDictionary _namedArgs;
BOOL _isActive; CPArray _args;
CPString _fullArgsString;
CPDictionary _namedArgs; CPImage _applicationIconImage;
CPArray _args;
CPString _fullArgsString;
CPImage _applicationIconImage; CPPanel _aboutPanel;
CPPanel _aboutPanel; CPThemeBlend _themeBlend @accessors(property=themeBlend);
CPThemeBlend _themeBlend @accessors(property=themeBlend);
} }
/*! /*!
@@ -151,9 +137,10 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
if (self) if (self)
{ {
_eventListeners = []; _eventListeners = [];
_eventListenerInsertionIndex = 0;
_windows = [[CPNull null]]; _windows = [];
[_windows addObject:nil];
} }
return self; return self;
@@ -167,13 +154,11 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
react to these events. react to these events.
@param aDelegate the delegate object @param aDelegate the delegate object
*/ */
- (void)setDelegate:(id <CPApplicationDelegate>)aDelegate - (void)setDelegate:(id)aDelegate
{ {
if (_delegate == aDelegate) if (_delegate == aDelegate)
return; return;
_implementedDelegateMethods = 0;
var defaultCenter = [CPNotificationCenter defaultCenter], var defaultCenter = [CPNotificationCenter defaultCenter],
delegateNotifications = delegateNotifications =
[ [
@@ -183,8 +168,7 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
CPApplicationDidBecomeActiveNotification, @selector(applicationDidBecomeActive:), CPApplicationDidBecomeActiveNotification, @selector(applicationDidBecomeActive:),
CPApplicationWillResignActiveNotification, @selector(applicationWillResignActive:), CPApplicationWillResignActiveNotification, @selector(applicationWillResignActive:),
CPApplicationDidResignActiveNotification, @selector(applicationDidResignActive:), CPApplicationDidResignActiveNotification, @selector(applicationDidResignActive:),
CPApplicationWillTerminateNotification, @selector(applicationWillTerminate:), CPApplicationWillTerminateNotification, @selector(applicationWillTerminate:)
CPApplicationDidChangeScreenParametersNotification, @selector(applicationDidChangeScreenParameters:)
], ],
count = [delegateNotifications count]; count = [delegateNotifications count];
@@ -214,12 +198,6 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
if ([_delegate respondsToSelector:selector]) if ([_delegate respondsToSelector:selector])
[defaultCenter addObserver:_delegate selector:selector name:notificationName object:self]; [defaultCenter addObserver:_delegate selector:selector name:notificationName object:self];
} }
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminate_;
if ([_delegate respondsToSelector:@selector(applicationShouldTerminateMessage:)])
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminateMessage_
} }
/*! /*!
@@ -241,22 +219,28 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
// At this point we clear the window.status to eliminate Safari's "Cancelled" error message // At this point we clear the window.status to eliminate Safari's "Cancelled" error message
// The message shouldn't be displayed, because only an XHR is cancelled, but it is a usability issue. // The message shouldn't be displayed, because only an XHR is cancelled, but it is a usability issue.
// We do it here so that applications can change it in willFinish or didFinishLaunching // We do it here so that applications can change it in willFinish or didFinishLaunching
#if PLATFORM(DOM)
window.status = " "; window.status = " ";
#endif
// We also want to set the default cursor on the body, so that buttons and things don't have an iBeam // We also want to set the default cursor on the body, so that buttons and things don't have an iBeam
[[CPCursor arrowCursor] set]; [[CPCursor arrowCursor] set];
var bundle = [CPBundle mainBundle], var bundle = [CPBundle mainBundle],
delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"]; types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
if ([types count] > 0)
_documentController = [CPDocumentController sharedDocumentController];
var delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"];
if (delegateClassName) if (delegateClassName)
{ {
var delegateClass = objj_getClass(delegateClassName); var delegateClass = objj_getClass(delegateClassName);
if (delegateClass) if (delegateClass)
[self setDelegate:[[delegateClass alloc] init]]; if ([_documentController class] == delegateClass)
[self setDelegate:_documentController];
else
[self setDelegate:[[delegateClass alloc] init]];
} }
var defaultCenter = [CPNotificationCenter defaultCenter]; var defaultCenter = [CPNotificationCenter defaultCenter];
@@ -265,23 +249,13 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
postNotificationName:CPApplicationWillFinishLaunchingNotification postNotificationName:CPApplicationWillFinishLaunchingNotification
object:self]; object:self];
var types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
if ([types count] > 0)
_documentController = [CPDocumentController sharedDocumentController];
var needsUntitled = !!_documentController, var needsUntitled = !!_documentController,
URLStrings = nil; URLStrings = window.cpOpeningURLStrings && window.cpOpeningURLStrings(),
index = 0,
#if PLATFORM(DOM)
URLStrings = window.cpOpeningURLStrings && window.cpOpeningURLStrings();
#endif
var index = 0,
count = [URLStrings count]; count = [URLStrings count];
for (; index < count; ++index) for (; index < count; ++index)
needsUntitled = ![self _openURL:[CPURL URLWithString:URLStrings[index]]] && needsUntitled; needsUntitled = ![self _openURL:[CPURL URLWithString:URLStrings[index]]] || needsUntitled;
if (needsUntitled && [_delegate respondsToSelector:@selector(applicationShouldOpenUntitledFile:)]) if (needsUntitled && [_delegate respondsToSelector:@selector(applicationShouldOpenUntitledFile:)])
needsUntitled = [_delegate applicationShouldOpenUntitledFile:self]; needsUntitled = [_delegate applicationShouldOpenUntitledFile:self];
@@ -364,7 +338,7 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
Copyright - Human readable copyright information. Copyright - Human readable copyright information.
</pre> </pre>
If you choose not the include any of the above keys, they will default If you choose not the include any of the above keys, they will default
to the following respective keys in your info.plist file. to the following respective keys in your info.plist file.
<pre> <pre>
@@ -388,32 +362,22 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"], applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"],
copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"]; copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"];
var windowWidth = 275, var aboutPanelPath = [[CPBundle bundleForClass:[CPWindowController class]] pathForResource:@"AboutPanel.cib"],
windowHeight = 223, aboutPanelController = [CPWindowController alloc],
imgWidth = 100, aboutPanelController = [aboutPanelController initWithWindowCibPath:aboutPanelPath owner:aboutPanelController],
imgHeight = 100, aboutPanel = [aboutPanelController window],
interField = 8, contentView = [aboutPanel contentView],
aboutPanel = [[CPWindow alloc] initWithContentRect:CGRectMake(0, 0, windowWidth, windowHeight) styleMask:CPClosableWindowMask], imageView = [contentView viewWithTag:1],
imageView = [[CPImageView alloc] initWithFrame:CGRectMake((windowWidth / 2) - (imgWidth / 2), interField, imgWidth, imgHeight)], applicationLabel = [contentView viewWithTag:2],
applicationLabel = [[CPTextField alloc] initWithFrame:CGRectMake(17, imgHeight + 16, windowWidth - 34, 24)], versionLabel = [contentView viewWithTag:3],
versionLabel = [[CPTextField alloc] initWithFrame:CGRectMake(17, imgHeight + 48, windowWidth - 34, 16)], copyrightLabel = [contentView viewWithTag:4],
copyrightLabel = [[CPTextField alloc] initWithFrame:CGRectMake(17, imgHeight + 72, windowWidth - 34, 32)], standardPath = [[CPBundle bundleForClass:[self class]] pathForResource:@"standardApplicationIcon.png"];
contentView = [aboutPanel contentView];
[applicationLabel setFont:[CPFont boldSystemFontOfSize:[CPFont systemFontSize] + 2]]; // FIXME move this into the CIB eventually
[applicationLabel setFont:[CPFont boldSystemFontOfSize:14.0]];
[applicationLabel setAlignment:CPCenterTextAlignment]; [applicationLabel setAlignment:CPCenterTextAlignment];
[versionLabel setFont:[CPFont systemFontOfSize:[CPFont systemFontSize] - 1]];
[versionLabel setAlignment:CPCenterTextAlignment]; [versionLabel setAlignment:CPCenterTextAlignment];
[copyrightLabel setFont:[CPFont systemFontOfSize:[CPFont systemFontSize] - 1]];
[copyrightLabel setAlignment:CPCenterTextAlignment]; [copyrightLabel setAlignment:CPCenterTextAlignment];
[copyrightLabel setLineBreakMode:CPLineBreakByWordWrapping];
[contentView addSubview:imageView];
[contentView addSubview:applicationLabel];
[contentView addSubview:versionLabel];
[contentView addSubview:copyrightLabel];
var standardPath = [[CPBundle bundleForClass:[self class]] pathForResource:@"standardApplicationIcon.png"];
[imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath [imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath
size:CGSizeMake(256, 256)]]; size:CGSizeMake(256, 256)]];
@@ -427,7 +391,7 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
else else
[versionLabel setStringValue:@""]; [versionLabel setStringValue:@""];
[copyrightLabel setStringValue:copyright || @""]; [copyrightLabel setStringValue:copyright || ""];
[aboutPanel center]; [aboutPanel center];
_aboutPanel = aboutPanel; _aboutPanel = aboutPanel;
@@ -437,11 +401,16 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
} }
- (void)_documentController:(CPDocumentController)docController didCloseAll:(BOOL)didCloseAll context:(Object)info - (void)_documentController:(NSDocumentController *)docController didCloseAll:(BOOL)didCloseAll context:(Object)info
{ {
// callback method for terminate: // callback method for terminate:
if (didCloseAll) if (didCloseAll)
[self replyToApplicationShouldTerminate:[self _sendDelegateApplicationShouldTerminate]]; {
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
[self replyToApplicationShouldTerminate:[_delegate applicationShouldTerminate:self]];
else
[self replyToApplicationShouldTerminate:YES];
}
} }
- (void)replyToApplicationShouldTerminate:(BOOL)terminate - (void)replyToApplicationShouldTerminate:(BOOL)terminate
@@ -455,22 +424,16 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
- (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps - (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps
{ {
if (_isActive)
return;
[self _willBecomeActive]; [self _willBecomeActive];
[CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps]; [CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps];
_isActive = YES; _isActive = YES;
[self _didBecomeActive]; [self _willResignActive];
} }
- (void)deactivate - (void)deactivate
{ {
if (!_isActive)
return;
[self _willResignActive]; [self _willResignActive];
[CPPlatform deactivate]; [CPPlatform deactivate];
@@ -496,15 +459,6 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
- (void)run - (void)run
{ {
[self finishLaunching]; [self finishLaunching];
[self sendEvent:[CPEvent otherEventWithType:CPAppKitDefined
location:CGPointMakeZero()
modifierFlags:0
timestamp:[CPEvent currentTimestamp]
windowNumber:[_keyWindow windowNumber]
context:nil
subtype:nil
data1:nil
data2:nil]];
} }
// Managing the Event Loop // Managing the Event Loop
@@ -546,8 +500,6 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
if (_eventListeners[count]._callback === _CPRunModalLoop) if (_eventListeners[count]._callback === _CPRunModalLoop)
{ {
_eventListeners.splice(count, 1); _eventListeners.splice(count, 1);
if (count <= _eventListenerInsertionIndex)
_eventListenerInsertionIndex--;
return; return;
} }
@@ -595,7 +547,7 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
// [theWindow._bridge _obscureWindowsBelowModalWindow]; // [theWindow._bridge _obscureWindowsBelowModalWindow];
[CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:YES]; [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
} }
/*! /*!
@@ -613,8 +565,8 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
/* @ignore */ /* @ignore */
- (BOOL)_handleKeyEquivalent:(CPEvent)anEvent - (BOOL)_handleKeyEquivalent:(CPEvent)anEvent
{ {
return [[self keyWindow] performKeyEquivalent:anEvent] || return [[self keyWindow] performKeyEquivalent:anEvent] ||
[[self mainMenu] performKeyEquivalent:anEvent]; [[self mainMenu] performKeyEquivalent:anEvent];
} }
/*! /*!
@@ -624,53 +576,38 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
- (void)sendEvent:(CPEvent)anEvent - (void)sendEvent:(CPEvent)anEvent
{ {
_currentEvent = anEvent; _currentEvent = anEvent;
CPEventModifierFlags = [anEvent modifierFlags];
var theWindow = [anEvent window]; var willPropagate = [[[anEvent window] platformWindow] _willPropagateCurrentDOMEvent];
// temporarily pretend we won't propagate the event. we'll restore the saved value later
// we do this outside the if so that changes user code might make in _handleKeyEquiv. are preserved
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO];
// Check if this is a candidate for key equivalent... // Check if this is a candidate for key equivalent...
if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent]) if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent])
// The key equivalent was handled. {
var characters = [anEvent characters],
modifierFlags = [anEvent modifierFlags];
// Unconditionally propagate on these keys to solve browser copy paste bugs
if ((characters == "c" || characters == "x" || characters == "v") && (modifierFlags & CPPlatformActionKeyMask))
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:YES];
return; return;
if ([anEvent type] == CPMouseMoved)
{
if (theWindow !== _lastMouseMoveWindow)
[_lastMouseMoveWindow _mouseExitedResizeRect];
_lastMouseMoveWindow = theWindow;
} }
/* // if we make it this far, then restore the original willPropagate value
Event listeners are processed from back to front so that newer event listeners normally take [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:willPropagate];
precedence. If during the execution of a callback a new event listener is added, it should
be inserted after the current callback but before any higher priority callbacks. This makes if (_eventListeners.length)
repeating event listeners (those that reinsert themselves) stable relative to each other.
*/
for (var i = _eventListeners.length - 1; i >= 0; i--)
{ {
var listener = _eventListeners[i]; if (_eventListeners[_eventListeners.length - 1]._mask & (1 << [anEvent type]))
_eventListeners.pop()._callback(anEvent);
if (listener._mask & (1 << [anEvent type])) return;
{
_eventListeners.splice(i, 1);
// In case the callback wants to add more listeners.
_eventListenerInsertionIndex = i;
listener._callback(anEvent);
if (listener._dequeue)
{
// Don't process the event normally and don't send it to any other listener.
_eventListenerInsertionIndex = _eventListeners.length;
return;
}
}
} }
_eventListenerInsertionIndex = _eventListeners.length; [[anEvent window] sendEvent:anEvent];
if (theWindow)
[theWindow sendEvent:anEvent];
} }
/*! /*!
@@ -706,10 +643,6 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
*/ */
- (CPWindow)windowWithWindowNumber:(int)aWindowNumber - (CPWindow)windowWithWindowNumber:(int)aWindowNumber
{ {
// Never allow _windows[0] to be returned - it's an internal CPNull placeholder.
if (!aWindowNumber)
return nil;
return _windows[aWindowNumber]; return _windows[aWindowNumber];
} }
@@ -718,8 +651,7 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
*/ */
- (CPArray)windows - (CPArray)windows
{ {
// Return all windows, but not the CPNull placeholder in _windows[0]. return _windows;
return [_windows subarrayWithRange:CPMakeRange(1, [_windows count] - 1)];
} }
/*! /*!
@@ -947,44 +879,31 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
/*! /*!
Fires a callback function when an event matching a given mask occurs. Fires a callback function when an event matching a given mask occurs.
@param aCallback - A js function to be fired.
If multiple callbacks are set which match the same event, later callbacks @prarm aMask - An event mask for the next event.
take priority over earlier callbacks, unless a new callback is set while @param anExpiration - The date for which this callback expires (not implemented).
an existing callback is being processed in which case it's given the same
priority as the currently processing callback.
@param aCallback A js function to be fired.
@prarm aMask An event mask for the next event.
@param anExpiration The date for which this callback expires (not implemented).
@param inMode (not implemented). @param inMode (not implemented).
@param shouldDequeue YES to remove the event from the queue after calling the callback, @param shouldDequeue (not implemented).
NO to deliver it normally.
*/ */
- (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue - (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
{ {
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, aCallback, shouldDequeue)); _eventListeners.push(_CPEventListenerMake(aMask, aCallback));
} }
/*! /*!
Assigns a target and action for the next event matching a given event mask. 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. The callback method called will be passed the CPEvent when it fires.
If multiple callbacks are set which match the same event, later callbacks @param aTarget - The target object for the callback.
take priority over earlier callbacks, unless a new callback is set while @param aSelector - The selector which should be called on the target object.
an existing callback is being processed in which case it's given the same @param aMask - The mask for a given event which should trigger the callback.
priority as the currently processing callback. @param anExpiration - The date for which the callback expires (not implemented).
@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 aMode (not implemented).
@param shouldDequeue YES to remove the event from the queue after calling the callback, @param shouldDequeue (not implemented).
NO to deliver it normally.
*/ */
- (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue - (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
{ {
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { if (aTarget != null) aTarget.isa.objj_msgSend1(aTarget, aSelector, anEvent); }, shouldDequeue)); _eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }));
} }
/*! /*!
@@ -1002,21 +921,21 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
@param aSheet the window to display as a sheet @param aSheet the window to display as a sheet
@param aWindow the window that will hold the sheet as a child @param aWindow the window that will hold the sheet as a child
@param aModalDelegate @param aModalDelegate
@param didEndSelector @param aDidEndSelector
@param contextInfo @param aContextInfo
*/ */
- (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)didEndSelector contextInfo:(id)contextInfo - (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo
{ {
if ([aWindow isSheet]) var styleMask = [aSheet styleMask];
if (!(styleMask & CPDocModalWindowMask))
{ {
[CPException raise:CPInternalInconsistencyException reason:@"The target window of beginSheet: cannot be a sheet"]; [CPException raise:CPInternalInconsistencyException reason:@"Currently only CPDocModalWindowMask style mask is supported for attached sheets"];
return; return;
} }
if (![aWindow attachedSheet]) [aWindow orderFront:self];
[aSheet._windowView _enableSheet:YES inWindow:aWindow]; [aSheet setPlatformWindow:[aWindow platformWindow]];
[aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
[aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:didEndSelector contextInfo:contextInfo];
} }
/*! /*!
@@ -1041,10 +960,10 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
var aWindow = [_windows objectAtIndex:count], var aWindow = [_windows objectAtIndex:count],
context = aWindow._sheetContext; context = aWindow._sheetContext;
if (context && context["sheet"] === sheet) if (context != nil && context["sheet"] === sheet)
{ {
context["returnCode"] = returnCode; context["returnCode"] = returnCode;
[aWindow _endSheet]; [aWindow _detachSheetWindow];
return; return;
} }
} }
@@ -1077,15 +996,14 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
*/ */
- (CPArray)arguments - (CPArray)arguments
{ {
// FIXME This should probably not access the window object #if !PLATFORM(DOM), but the unit tests rely on it. if (_fullArgsString !== window.location.hash)
if (window && window.location && _fullArgsString !== window.location.hash)
[self _reloadArguments]; [self _reloadArguments];
return _args; return _args;
} }
/*! /*!
Sets the arguments of your application. Sets the arguments of your application.
That is, set the slash seperated values of an array as the window location hash. That is, set the slash seperated values of an array as the window location hash.
For example if you pass an array: For example if you pass an array:
@@ -1105,9 +1023,8 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
if (!args || args.length == 0) if (!args || args.length == 0)
{ {
_args = []; _args = [];
// Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along. window.location.hash = @"#";
if (window && window.location)
window.location.hash = @"#";
return; return;
} }
@@ -1122,15 +1039,12 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
var hash = [toEncode componentsJoinedByString:@"/"]; var hash = [toEncode componentsJoinedByString:@"/"];
// Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along. window.location.hash = @"#" + hash;
if (window && window.location)
window.location.hash = @"#" + hash;
} }
- (void)_reloadArguments - (void)_reloadArguments
{ {
// FIXME This should probably not access the window object #if !PLATFORM(DOM), but the unit tests rely on it. _fullArgsString = window.location.hash;
_fullArgsString = (window && window.location) ? window.location.hash : "";
if (_fullArgsString.length) if (_fullArgsString.length)
{ {
@@ -1202,7 +1116,7 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
[[self keyWindow] orderFront:self]; [[self keyWindow] orderFront:self];
else if ([self mainWindow]) else if ([self mainWindow])
[[self mainWindow] makeKeyAndOrderFront:self]; [[self mainWindow] makeKeyAndOrderFront:self];
else if ([self mainMenu]) else
[[self mainMenu]._menuWindow makeKeyWindow]; //FIXME this may not actually work [[self mainMenu]._menuWindow makeKeyWindow]; //FIXME this may not actually work
_previousKeyWindow = nil; _previousKeyWindow = nil;
@@ -1220,22 +1134,6 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
userInfo:nil]; userInfo:nil];
} }
- (BOOL)_sendDelegateApplicationShouldTerminate
{
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminate_))
return YES;
return [_delegate applicationShouldTerminate:self];
}
- (CPString)_sendDelegateApplicationShouldTerminateMessage
{
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminateMessage_))
return @"You have attempted to leave this page. Are you sure you want to exit this page?";
return [_delegate applicationShouldTerminateMessage:self];
}
- (void)_didResignActive - (void)_didResignActive
{ {
if (self._activeMenu) if (self._activeMenu)
@@ -1260,7 +1158,7 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
+ (CPString)defaultThemeName + (CPString)defaultThemeName
{ {
return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo2"); return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo");
} }
@end @end
@@ -1268,32 +1166,24 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
var _CPModalSessionMake = function(aWindow, aStopCode) var _CPModalSessionMake = function(aWindow, aStopCode)
{ {
return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil }; return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil };
}; }
var _CPEventListenerMake = function(anEventMask, aCallback, shouldDequeue) var _CPEventListenerMake = function(anEventMask, aCallback)
{ {
return { _mask:anEventMask, _callback:aCallback, _dequeue:shouldDequeue }; return { _mask:anEventMask, _callback:aCallback };
}; }
// Make this a global for use in CPPlatformWindow+DOM.j. // Make this a global for use in CPPlatformWindow+DOM.j.
_CPRunModalLoop = function(anEvent) _CPRunModalLoop = function(anEvent)
{ {
[CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:YES]; [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
var theWindow = [anEvent window], var theWindow = [anEvent window],
modalSession = CPApp._currentSession; modalSession = CPApp._currentSession;
/* if (theWindow == modalSession._window || [theWindow worksWhenModal])
The special case for popovers here is not clear. In Cocoa the popover window does not respond YES to worksWhenModal, yet it works when there is a modal window. Maybe it starts its own modal session, but interaction with the original modal window seems to continue working as well. Regardless of correctness, this solution beats popovers not working at all from sheets.
*/
if (theWindow == modalSession._window ||
[theWindow worksWhenModal] ||
[theWindow attachedSheet] == modalSession._window || // -dw- allow modal parent of sheet to be repositioned
([theWindow isKindOfClass:_CPPopoverWindow] && [[theWindow targetView] window] === modalSession._window))
{
[theWindow sendEvent:anEvent]; [theWindow sendEvent:anEvent];
} }
};
/*! /*!
Starts the GUI and Cappuccino frameworks. This function should be Starts the GUI and Cappuccino frameworks. This function should be
@@ -1308,17 +1198,7 @@ function CPApplicationMain(args, namedArgs)
#if PLATFORM(DOM) #if PLATFORM(DOM)
// hook to allow recorder, etc to manipulate things before starting AppKit // hook to allow recorder, etc to manipulate things before starting AppKit
if (window.parent !== window && typeof window.parent._childAppIsStarting === "function") if (window.parent !== window && typeof window.parent._childAppIsStarting === "function")
{ window.parent._childAppIsStarting(window);
try
{
window.parent._childAppIsStarting(window);
}
catch(err)
{
// This could happen if we're in an iframe without access to the parent frame.
CPLog.warn("Failed to call parent frame's _childAppIsStarting().");
}
}
#endif #endif
var mainBundle = [CPBundle mainBundle], var mainBundle = [CPBundle mainBundle],
@@ -1358,7 +1238,7 @@ var _CPAppBootstrapperActions = nil;
{ {
var action = _CPAppBootstrapperActions.shift(); var action = _CPAppBootstrapperActions.shift();
if (self.isa.objj_msgSend0(self, action)) if (objj_msgSend(self, action))
return; return;
} }
@@ -1375,7 +1255,7 @@ var _CPAppBootstrapperActions = nil;
var defaultThemeName = [CPApplication defaultThemeName], var defaultThemeName = [CPApplication defaultThemeName],
themeURL = nil; themeURL = nil;
if (defaultThemeName === @"Aristo" || defaultThemeName === @"Aristo2") if (defaultThemeName === @"Aristo")
themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"]; themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"];
else else
themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"]; themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"];
@@ -1402,8 +1282,8 @@ var _CPAppBootstrapperActions = nil;
if (mainCibFile) if (mainCibFile)
{ {
[mainBundle loadCibFile:mainCibFile [mainBundle loadCibFile:mainCibFile
externalNameTable:@{ CPCibOwner: CPApp } externalNameTable:[CPDictionary dictionaryWithObject:CPApp forKey:CPCibOwner]
loadDelegate:self]; loadDelegate:self];
return YES; return YES;
} }
@@ -1420,25 +1300,26 @@ var _CPAppBootstrapperActions = nil;
// FIXME: We should implement autoenabling. // FIXME: We should implement autoenabling.
[mainMenu setAutoenablesItems:NO]; [mainMenu setAutoenablesItems:NO];
var newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"n"]; var bundle = [CPBundle bundleForClass:[CPApplication class]],
newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"n"];
[newMenuItem setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-new" forClass:_CPMenuView]]; [newMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/New.png"] size:CGSizeMake(16.0, 16.0)]];
[newMenuItem setAlternateImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-new" inState:CPThemeStateHighlighted forClass:_CPMenuView]]; [newMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/NewHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
[mainMenu addItem:newMenuItem]; [mainMenu addItem:newMenuItem];
var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"o"]; var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"o"];
[openMenuItem setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-open" forClass:_CPMenuView]]; [openMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Open.png"] size:CGSizeMake(16.0, 16.0)]];
[openMenuItem setAlternateImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-open" inState:CPThemeStateHighlighted forClass:_CPMenuView]]; [openMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/OpenHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
[mainMenu addItem:openMenuItem]; [mainMenu addItem:openMenuItem];
var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"], var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"],
saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil]; saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil];
[saveMenuItem setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-save" forClass:_CPMenuView]]; [saveMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Save.png"] size:CGSizeMake(16.0, 16.0)]];
[saveMenuItem setAlternateImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-save" inState:CPThemeStateHighlighted forClass:_CPMenuView]]; [saveMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/SaveHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
[saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]]; [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]];
[saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]]; [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]];
@@ -1459,8 +1340,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];
@@ -1480,7 +1361,7 @@ var _CPAppBootstrapperActions = nil;
+ (void)cibDidFailToLoad:(CPCib)aCib + (void)cibDidFailToLoad:(CPCib)aCib
{ {
throw new Error("Could not load main cib file. Did you forget to nib2cib it?"); throw new Error("Could not load main cib file (Did you forget to nib2cib it?).");
} }
+ (void)reset + (void)reset
@@ -1489,16 +1370,3 @@ var _CPAppBootstrapperActions = nil;
} }
@end @end
@implementation CPEvent (CPApplicationModifierFlags)
/*!
Returns the currently pressed modifier flags.
*/
+ (unsigned)modifierFlags
{
return CPEventModifierFlags;
}
@end
-41
View File
@@ -1,41 +0,0 @@
/*
* CPApplication_Constants.j
* AppKit
*
* Created by Aparajita Fishman.
* Copyright 2013 The Cappuccino Project
*
* 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
*/
CPApp = nil;
CPApplicationWillFinishLaunchingNotification = @"CPApplicationWillFinishLaunchingNotification";
CPApplicationDidFinishLaunchingNotification = @"CPApplicationDidFinishLaunchingNotification";
CPApplicationWillTerminateNotification = @"CPApplicationWillTerminateNotification";
CPApplicationWillBecomeActiveNotification = @"CPApplicationWillBecomeActiveNotification";
CPApplicationDidBecomeActiveNotification = @"CPApplicationDidBecomeActiveNotification";
CPApplicationWillResignActiveNotification = @"CPApplicationWillResignActiveNotification";
CPApplicationDidResignActiveNotification = @"CPApplicationDidResignActiveNotification";
CPApplicationDidChangeScreenParametersNotification = @"CPApplicationDidChangeScreenParametersNotification";
@typedef CPApplicationTerminateReply
CPTerminateNow = YES;
CPTerminateCancel = NO;
CPTerminateLater = -1; // not currently supported
CPRunStoppedResponse = -1000;
CPRunAbortedResponse = -1001;
CPRunContinuesResponse = -1002;
+60 -143
View File
@@ -23,7 +23,7 @@
*/ */
@import <Foundation/CPIndexSet.j> @import <Foundation/CPIndexSet.j>
@import <Foundation/CPPredicate.j>
@import "CPObjectController.j" @import "CPObjectController.j"
@import "CPKeyValueBinding.j" @import "CPKeyValueBinding.j"
@@ -65,51 +65,51 @@
+ (CPSet)keyPathsForValuesAffectingContentArray + (CPSet)keyPathsForValuesAffectingContentArray
{ {
return [CPSet setWithObjects:@"content"]; return [CPSet setWithObjects:"content"];
} }
+ (CPSet)keyPathsForValuesAffectingArrangedObjects + (CPSet)keyPathsForValuesAffectingArrangedObjects
{ {
// Also depends on "filterPredicate" but we'll handle that manually. // Also depends on "filterPredicate" but we'll handle that manually.
return [CPSet setWithObjects:@"content", @"sortDescriptors"]; return [CPSet setWithObjects:"content", "sortDescriptors"];
} }
+ (CPSet)keyPathsForValuesAffectingSelection + (CPSet)keyPathsForValuesAffectingSelection
{ {
return [CPSet setWithObjects:@"selectionIndexes"]; return [CPSet setWithObjects:"selectionIndexes"];
} }
+ (CPSet)keyPathsForValuesAffectingSelectionIndex + (CPSet)keyPathsForValuesAffectingSelectionIndex
{ {
return [CPSet setWithObjects:@"selectionIndexes"]; return [CPSet setWithObjects:"selectionIndexes"];
} }
+ (CPSet)keyPathsForValuesAffectingSelectionIndexes + (CPSet)keyPathsForValuesAffectingSelectionIndexes
{ {
// When the arranged objects change, selection preservation may cause the indexes // When the arranged objects change, selection preservation may cause the indexes
// to change. // to change.
return [CPSet setWithObjects:@"arrangedObjects"]; return [CPSet setWithObjects:"arrangedObjects"];
} }
+ (CPSet)keyPathsForValuesAffectingSelectedObjects + (CPSet)keyPathsForValuesAffectingSelectedObjects
{ {
// Don't need to depend on arrangedObjects here because selectionIndexes already does. // Don't need to depend on arrangedObjects here because selectionIndexes already does.
return [CPSet setWithObjects:@"selectionIndexes"]; return [CPSet setWithObjects:"selectionIndexes"];
} }
+ (CPSet)keyPathsForValuesAffectingCanRemove + (CPSet)keyPathsForValuesAffectingCanRemove
{ {
return [CPSet setWithObjects:@"selectionIndexes"]; return [CPSet setWithObjects:"selectionIndexes"];
} }
+ (CPSet)keyPathsForValuesAffectingCanSelectNext + (CPSet)keyPathsForValuesAffectingCanSelectNext
{ {
return [CPSet setWithObjects:@"selectionIndexes"]; return [CPSet setWithObjects:"selectionIndexes"];
} }
+ (CPSet)keyPathsForValuesAffectingCanSelectPrevious + (CPSet)keyPathsForValuesAffectingCanSelectPrevious
{ {
return [CPSet setWithObjects:@"selectionIndexes"]; return [CPSet setWithObjects:"selectionIndexes"];
} }
@@ -563,10 +563,9 @@
*/ */
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes - (BOOL)setSelectionIndexes:(CPIndexSet)indexes
{ {
[self _selectionWillChange]; [self _selectionWillChange]
var r = [self __setSelectionIndexes:indexes avoidEmpty:NO]; [self __setSelectionIndexes:indexes];
[self _selectionDidChange]; [self _selectionDidChange];
return r;
} }
/* /*
@@ -575,7 +574,7 @@
*/ */
- (BOOL)__setSelectionIndex:(int)theIndex - (BOOL)__setSelectionIndex:(int)theIndex
{ {
return [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex]]; [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex]];
} }
/* /*
@@ -584,43 +583,28 @@
*/ */
- (BOOL)__setSelectionIndexes:(CPIndexSet)indexes - (BOOL)__setSelectionIndexes:(CPIndexSet)indexes
{ {
return [self __setSelectionIndexes:indexes avoidEmpty:_avoidsEmptySelection]; if (!indexes)
} indexes = [CPIndexSet indexSet];
- (BOOL)__setSelectionIndexes:(CPIndexSet)indexes avoidEmpty:(BOOL)avoidEmpty if (![indexes count])
{
var newIndexes = indexes;
if (!newIndexes)
newIndexes = [CPIndexSet indexSet];
if (![newIndexes count])
{ {
if (avoidEmpty && [[self arrangedObjects] count]) if (_avoidsEmptySelection && [[self arrangedObjects] count])
newIndexes = [CPIndexSet indexSetWithIndex:0]; indexes = [CPIndexSet indexSetWithIndex:0];
} }
else else
{ {
var objectsCount = [[self arrangedObjects] count]; var objectsCount = [[self arrangedObjects] count];
// Don't trash the input - the caller might depend on it or we might have been
// given _selectionIndexes as the input in which case the equality test below
// would always succeed despite our change below.
newIndexes = [newIndexes copy];
// Remove out of bounds indexes. // Remove out of bounds indexes.
[newIndexes removeIndexesInRange:CPMakeRange(objectsCount, [newIndexes lastIndex] + 1)]; [indexes removeIndexesInRange:CPMakeRange(objectsCount, [indexes lastIndex] + 1)];
// When avoiding empty selection and the deleted selection was at the bottom, select the last item. // When avoiding empty selection and the deleted selection was at the bottom, select the last item.
if (![newIndexes count] && avoidEmpty && objectsCount) if (![indexes count] && _avoidsEmptySelection && objectsCount)
newIndexes = [CPIndexSet indexSetWithIndex:objectsCount - 1]; indexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
} }
if ([_selectionIndexes isEqualToIndexSet:newIndexes]) if ([_selectionIndexes isEqualToIndexSet:indexes])
return NO; return NO;
// If we haven't already created our own index instance, make sure to copy it here so that _selectionIndexes = [indexes copy];
// the copy the user sent in is decoupled from our internal copy.
_selectionIndexes = indexes === newIndexes ? [indexes copy] : newIndexes;
// Push back the new selection to the model for selectionIndexes if we have one. // Push back the new selection to the model for selectionIndexes if we have one.
// There won't be an infinite loop because of the equality check above. // There won't be an infinite loop because of the equality check above.
@@ -652,11 +636,10 @@
[self willChangeValueForKey:@"selectionIndexes"]; [self willChangeValueForKey:@"selectionIndexes"];
[self _selectionWillChange]; [self _selectionWillChange];
var r = [self __setSelectedObjects:objects avoidEmpty:NO]; [self __setSelectedObjects:objects];
[self didChangeValueForKey:@"selectionIndexes"]; [self didChangeValueForKey:@"selectionIndexes"];
[self _selectionDidChange]; [self _selectionDidChange];
return r;
} }
/* /*
@@ -664,11 +647,6 @@
@ignore @ignore
*/ */
- (BOOL)__setSelectedObjects:(CPArray)objects - (BOOL)__setSelectedObjects:(CPArray)objects
{
[self __setSelectedObjects:objects avoidEmpty:_avoidsEmptySelection];
}
- (BOOL)__setSelectedObjects:(CPArray)objects avoidEmpty:(BOOL)avoidEmpty
{ {
var set = [CPIndexSet indexSet], var set = [CPIndexSet indexSet],
count = [objects count], count = [objects count],
@@ -682,7 +660,7 @@
[set addIndex:index]; [set addIndex:index];
} }
[self __setSelectionIndexes:set avoidEmpty:avoidEmpty]; [self __setSelectionIndexes:set];
return YES; return YES;
} }
@@ -744,7 +722,6 @@
return; return;
var willClearPredicate = NO; var willClearPredicate = NO;
if (_clearsFilterPredicateOnInsertion && _filterPredicate) if (_clearsFilterPredicateOnInsertion && _filterPredicate)
{ {
[self willChangeValueForKey:@"filterPredicate"]; [self willChangeValueForKey:@"filterPredicate"];
@@ -793,18 +770,10 @@
// not appear in arrangedObjects and we do not have to update at all. // not appear in arrangedObjects and we do not have to update at all.
*/ */
// TODO: Remove these lines when granular notifications are implemented // This will also send notificaitons for arrangedObjects.
var proxy = [_CPKVOProxy proxyForObject:self];
[proxy setAdding:YES];
// This will also send notifications for arrangedObjects.
[self didChangeValueForKey:@"content"]; [self didChangeValueForKey:@"content"];
if (willClearPredicate) if (willClearPredicate)
[self didChangeValueForKey:@"filterPredicate"]; [self didChangeValueForKey:@"filterPredicate"];
// TODO: Remove this line when granular notifications are implemented
[proxy setAdding:NO];
} }
/*! /*!
@@ -820,7 +789,6 @@
return; return;
var willClearPredicate = NO; var willClearPredicate = NO;
if (_clearsFilterPredicateOnInsertion && _filterPredicate) if (_clearsFilterPredicateOnInsertion && _filterPredicate)
{ {
[self willChangeValueForKey:@"filterPredicate"]; [self willChangeValueForKey:@"filterPredicate"];
@@ -858,15 +826,9 @@
if ([self avoidsEmptySelection] && [[self selectionIndexes] count] <= 0 && [_contentObject count] > 0) if ([self avoidsEmptySelection] && [[self selectionIndexes] count] <= 0 && [_contentObject count] > 0)
[self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]]; [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]];
var proxy = [_CPKVOProxy proxyForObject:self];
[proxy setAdding:YES];
[self didChangeValueForKey:@"content"]; [self didChangeValueForKey:@"content"];
if (willClearPredicate) if (willClearPredicate)
[self didChangeValueForKey:@"filterPredicate"]; [self didChangeValueForKey:@"filterPredicate"];
[proxy setAdding:NO];
} }
/*! /*!
@@ -895,9 +857,6 @@
[_arrangedObjects removeObjectAtIndex:pos]; [_arrangedObjects removeObjectAtIndex:pos];
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:-1]; [_selectionIndexes shiftIndexesStartingAtIndex:pos by:-1];
// This will automatically handle the avoidsEmptySelection case.
[self __setSelectionIndexes:_selectionIndexes];
} }
[self didChangeValueForKey:@"content"]; [self didChangeValueForKey:@"content"];
@@ -913,9 +872,7 @@
if (![self canAdd]) if (![self canAdd])
return; return;
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject]; [self insert:sender];
[self addObject:newObject];
} }
/*! /*!
@@ -927,13 +884,9 @@
if (![self canInsert]) if (![self canInsert])
return; return;
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject], var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject];
lastSelectedIndex = [_selectionIndexes lastIndex];
if (lastSelectedIndex !== CPNotFound) [self addObject:newObject];
[self insertObject:newObject atArrangedObjectIndex:lastSelectedIndex];
else
[self addObject:newObject];
} }
/*! /*!
@@ -945,15 +898,6 @@
[self removeObjectsAtArrangedObjectIndexes:_selectionIndexes]; [self removeObjectsAtArrangedObjectIndexes:_selectionIndexes];
} }
/*!
Removes the object at the specified index in the controller's arranged objects from the content array.
@param int index - index of the object to remove.
*/
- (void)removeObjectAtArrangedObjectIndex:(int)index
{
[self removeObjectsAtArrangedObjectIndexes:[CPIndexSet indexSetWithIndex:index]];
}
/*! /*!
Removes the objects at the specified indexes in the controller's arranged objects from the content array. Removes the objects at the specified indexes in the controller's arranged objects from the content array.
@param CPIndexSet indexes - indexes of the objects to remove. @param CPIndexSet indexes - indexes of the objects to remove.
@@ -968,38 +912,36 @@
_disableSetContent = YES; _disableSetContent = YES;
var arrangedObjects = [self arrangedObjects], var arrangedObjects = [self arrangedObjects],
index = [anIndexSet lastIndex],
position = CPNotFound, position = CPNotFound,
newSelectionIndexes = [_selectionIndexes copy]; newSelectionIndexes = [_selectionIndexes copy];
[anIndexSet enumerateIndexesWithOptions:CPEnumerationReverse while (index !== CPNotFound)
usingBlock:function(anIndex) {
var object = [arrangedObjects objectAtIndex:index];
// First try the simple case which should work if there are no sort descriptors.
if ([_contentObject objectAtIndex:index] === object)
[_contentObject removeObjectAtIndex:index];
else
{ {
var object = [arrangedObjects objectAtIndex:anIndex]; // Since we don't have a reverse mapping between the sorted order and the
// unsorted one, we'll just simply have to remove an arbitrary pointer. It might
// be the 'wrong' one - as in not the one the user selected - but the wrong
// one is still just another pointer to the same object, so the user will not
// be able to see any difference.
contentIndex = [_contentObject indexOfObjectIdenticalTo:object];
[_contentObject removeObjectAtIndex:contentIndex];
}
[arrangedObjects removeObjectAtIndex:index];
// First try the simple case which should work if there are no sort descriptors. // Deselect this row if it was selected, and either way shift all selection indexes
if ([_contentObject objectAtIndex:anIndex] === object) // following it up by 1.
[_contentObject removeObjectAtIndex:anIndex]; [newSelectionIndexes removeIndex:index];
else [newSelectionIndexes shiftIndexesStartingAtIndex:index by:-1];
{
// Since we don't have a reverse mapping between the sorted order and the
// unsorted one, we'll just simply have to remove an arbitrary pointer. It might
// be the 'wrong' one - as in not the one the user selected - but the wrong
// one is still just another pointer to the same object, so the user will not
// be able to see any difference.
var contentIndex = [_contentObject indexOfObjectIdenticalTo:object];
[_contentObject removeObjectAtIndex:contentIndex];
}
[arrangedObjects removeObjectAtIndex:anIndex];
if (!_avoidsEmptySelection || [newSelectionIndexes count] > 1)
{
[newSelectionIndexes removeIndex:anIndex];
[newSelectionIndexes shiftIndexesStartingAtIndex:anIndex by:-1];
}
else if ([newSelectionIndexes lastIndex] !== anIndex)
[newSelectionIndexes shiftIndexesStartingAtIndex:anIndex by:-1];
}];
index = [anIndexSet indexLessThanIndex:index];
}
// Allow handlesContentAsCompoundValue reverse sets to trigger. // Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange]; [[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
_disableSetContent = NO; _disableSetContent = NO;
@@ -1095,12 +1037,12 @@
@implementation CPArrayController (CPBinder) @implementation CPArrayController (CPBinder)
+ (Class)_binderClassForBinding:(CPString)aBinding + (Class)_binderClassForBinding:(CPString)theBinding
{ {
if (aBinding == @"contentArray") if (theBinding == @"contentArray")
return [_CPArrayControllerContentBinder class]; return [_CPArrayControllerContentBinder class];
return [super _binderClassForBinding:aBinding]; return [super _binderClassForBinding:theBinding];
} }
@end @end
@@ -1112,47 +1054,22 @@
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],
isCompound = [self handlesContentAsCompoundValue], isCompound = [self handlesContentAsCompoundValue];
dotIndex = keyPath.lastIndexOf("."),
firstPart = dotIndex !== CPNotFound ? keyPath.substring(0, dotIndex) : nil,
isSelectionProxy = firstPart && [[destination valueForKeyPath:firstPart] isKindOfClass:CPControllerSelectionProxy],
newValue;
if (!isCompound && !isSelectionProxy) if (!isCompound)
{ {
newValue = [destination mutableArrayValueForKeyPath:keyPath]; newValue = [destination mutableArrayValueForKeyPath:keyPath];
} }
else else
{ {
// 1. If handlesContentAsCompoundValue we cannot just set up a proxy. // handlesContentAsCompoundValue == YES so we cannot just set up a proxy.
// Every read and every write must go through transformValue and // Every read and every write must go through transformValue and
// reverseTransformValue, and the resulting object cannot be described by // reverseTransformValue, and the resulting object cannot be described by
// a key path. // a key path.
// 2. If isSelectionProxy, we don't want to proxy a proxy - that's bad
// for performance and won't work with markers.
newValue = [destination valueForKeyPath:keyPath]; newValue = [destination valueForKeyPath:keyPath];
} }
var isPlaceholder = CPIsControllerMarker(newValue); newValue = [self transformValue:newValue withOptions:options];
if (isPlaceholder)
{
if (newValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
{
[CPException raise:CPGenericException
reason:@"can't transform non applicable key on: " + _source + " value: " + newValue];
}
newValue = [self _placeholderForMarker:newValue];
// This seems to be what Cocoa does.
if (!newValue)
newValue = [CPMutableArray array];
}
else
newValue = [self transformValue:newValue withOptions:options];
if (isCompound) if (isCompound)
{ {
+62
View File
@@ -0,0 +1,62 @@
/*
* CPAttributedString+Additions.j
* AppKit
*
* Created by Randy Luecke
* Copyright 2011, RCLConcepts, LLC.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
AppKit adds two methods to the CPAttributedString class to support drawing string directly in an CPView.
AppKit also adds similar methods to CPString.
*/
@implementation CPAttributedString (AppKitAdditions)
/*!
Draws a string in the current graphics context.
This method and draws the reciver on a single "infinately long" line.
@param aPoint - The starting point to draw the string
*/
- (void)drawAtPoint:(CGPoint)aPoint
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
line = CTLineCreateWithAttributedString([self copy]);
CGContextSetTextPosition(context, aPoint.x, aPoint.y);
CTLineDraw(line, context);
}
/*!
Draws a string in the current graphics context.
@param aRect - The rect for which the string should be drawn into
*/
- (void)drawInRect:(CGRect)aRect
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
frameSetter = CTFramesetterCreateWithAttributedString([self copy]),
path = CGPathCreateMutable();
CGPathAddRect(path, nil, aRect);
var frame = CTFramesetterCreateFrame(frameSetter, CPMakeRange(0, [self length]), path, nil);
CTFrameDraw(frame, context);
}
@end
+6 -94
View File
@@ -20,6 +20,7 @@
* License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Copyright 2009 280 North, Inc. * Copyright 2009 280 North, Inc.
*/ */
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@@ -48,8 +49,6 @@ var DefaultLineWidth = 1.0;
{ {
CGPath _path; CGPath _path;
float _lineWidth; float _lineWidth;
CPArray _lineDashes;
float _lineDashesPhase;
} }
/*! /*!
@@ -63,11 +62,11 @@ var DefaultLineWidth = 1.0;
/*! /*!
Create a new CPBezierPath object initialized with an oval path drawn within a rectangular path. Create a new CPBezierPath object initialized with an oval path drawn within a rectangular path.
*/ */
+ (CPBezierPath)bezierPathWithOvalInRect:(CGRect)aRect + (CPBezierPath)bezierPathWithOvalInRect:(CGRect)rect
{ {
var path = [self bezierPath]; var path = [self bezierPath];
[path appendBezierPathWithOvalInRect:aRect]; [path appendBezierPathWithOvalInRect:rect];
return path; return path;
} }
@@ -75,20 +74,11 @@ var DefaultLineWidth = 1.0;
/*! /*!
Create a new CPBezierPath object initialized with a rectangular path. Create a new CPBezierPath object initialized with a rectangular path.
*/ */
+ (CPBezierPath)bezierPathWithRect:(CGRect)aRect + (CPBezierPath)bezierPathWithRect:(CGRect)rect
{ {
var path = [self bezierPath]; var path = [self bezierPath];
[path appendBezierPathWithRect:aRect]; [path appendBezierPathWithRect:rect];
return path;
}
+ (CPBezierPath)bezierPathWithRoundedRect:(CGRect)aRect xRadius:(float)xRadius yRadius:(float)yRadius
{
var path = [self bezierPath];
[path appendBezierPathWithRoundedRect:aRect xRadius:xRadius yRadius:yRadius];
return path; return path;
} }
@@ -147,8 +137,6 @@ var DefaultLineWidth = 1.0;
{ {
_path = CGPathCreateMutable(); _path = CGPathCreateMutable();
_lineWidth = [[self class] defaultLineWidth]; _lineWidth = [[self class] defaultLineWidth];
_lineDashesPhase = 0;
_lineDashes = [];
} }
return self; return self;
@@ -178,19 +166,6 @@ var DefaultLineWidth = 1.0;
CGPathAddCurveToPoint(_path, nil, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y); CGPathAddCurveToPoint(_path, nil, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);
} }
- (CGRect)bounds
{
// TODO: this should return this. The controlPointBounds is not a tight fit.
// return CGPathGetBoundingBox(_path);
return [self controlPointBounds];
}
- (CGRect)controlPointBounds
{
return CGPathGetBoundingBox(_path);
}
/*! /*!
Create a line segment between the first and last points in the subpath, closing it. Create a line segment between the first and last points in the subpath, closing it.
*/ */
@@ -209,7 +184,6 @@ var DefaultLineWidth = 1.0;
CGContextBeginPath(ctx); CGContextBeginPath(ctx);
CGContextAddPath(ctx, _path); CGContextAddPath(ctx, _path);
CGContextSetLineWidth(ctx, [self lineWidth]); CGContextSetLineWidth(ctx, [self lineWidth]);
CGContextSetLineDash(ctx, _lineDashesPhase, _lineDashes);
CGContextStrokePath(ctx); CGContextStrokePath(ctx);
} }
@@ -223,50 +197,10 @@ var DefaultLineWidth = 1.0;
CGContextBeginPath(ctx); CGContextBeginPath(ctx);
CGContextAddPath(ctx, _path); CGContextAddPath(ctx, _path);
CGContextSetLineWidth(ctx, [self lineWidth]); CGContextSetLineWidth(ctx, [self lineWidth]);
CGContextSetLineDash(ctx, _lineDashesPhase, _lineDashes);
CGContextClosePath(ctx); CGContextClosePath(ctx);
CGContextFillPath(ctx); CGContextFillPath(ctx);
} }
/*!
Cocoa compatibility.
*/
- (void)getLineDash:(CPArrayRef)patternRef count:(NSInteger)count phase:(CGFloatRef)phaseRef
{
return [self getLineDash:patternRef phase:phaseRef];
}
/*!
Retrieve the line dash pattern and phase and write them into the provided references.
*/
- (void)getLineDash:(CPArrayRef)patternRef phase:(CGFloatRef)phaseRef
{
if (patternRef)
@deref(patternRef) = [_lineDashes copy];
if (phaseRef)
@deref(phaseRef) = _lineDashesPhase;
}
/*!
Cocoa compatibility.
*/
- (void)setLineDash:(CPArray)aPattern count:(NSInteger)count phase:(CGFloat)aPhase
{
[self setLineDash:aPattern phase:aPhase];
}
/*!
Set stroke line dash pattern.
@param aPattern an array of stroke-skip lengths such as [2, 2, 4, 4]
@param aPhase amount of shift for the starting position of the first stroke
*/
- (void)setLineDash:(CPArray)aPattern phase:(CGFloat)aPhase
{
_lineDashes = aPattern;
_lineDashesPhase = aPhase;
}
/*! /*!
Get the line width. Get the line width.
*/ */
@@ -339,15 +273,10 @@ var DefaultLineWidth = 1.0;
CGPathAddPath(_path, nil, CGPathWithRoundedRectangleInRect(rect, xRadius, yRadius, YES, YES, YES, YES)); CGPathAddPath(_path, nil, CGPathWithRoundedRectangleInRect(rect, xRadius, yRadius, YES, YES, YES, YES));
} }
- (void)appendBezierPathWithArcFromPoint:(CGPoint)fromPoint toPoint:(CGPoint)toPoint radius:(float)radius
{
CGPathAddArcToPoint(_path, null, fromPoint.x, fromPoint.y, toPoint.x, toPoint.y, radius);
}
/*! /*!
Append the contents of a CPBezierPath object. Append the contents of a CPBezierPath object.
*/ */
- (void)appendBezierPath:(CPBezierPath)other - (void)appendBezierPath:(NSBezierPath *)other
{ {
CGPathAddPath(_path, nil, other._path); CGPathAddPath(_path, nil, other._path);
} }
@@ -360,21 +289,4 @@ var DefaultLineWidth = 1.0;
_path = CGPathCreateMutable(); _path = CGPathCreateMutable();
} }
- (void)addClip
{
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
CGContextAddPath(ctx, _path);
CGContextClip(ctx);
}
- (void)setClip
{
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
CGContextBeginPath(ctx);
CGContextAddPath(ctx, _path);
CGContextClip(ctx);
}
@end @end
+99 -324
View File
@@ -20,11 +20,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "CPTextField.j"
@import "CPView.j" @import "CPView.j"
// CPBoxType // CPBoxType
@typedef CPBoxType
CPBoxPrimary = 0; CPBoxPrimary = 0;
CPBoxSecondary = 1; CPBoxSecondary = 1;
CPBoxSeparator = 2; CPBoxSeparator = 2;
@@ -32,23 +30,11 @@ CPBoxOldStyle = 3;
CPBoxCustom = 4; CPBoxCustom = 4;
// CPBorderType // CPBorderType
@typedef CPBorderType
CPNoBorder = 0; CPNoBorder = 0;
CPLineBorder = 1; CPLineBorder = 1;
CPBezelBorder = 2; CPBezelBorder = 2;
CPGrooveBorder = 3; CPGrooveBorder = 3;
// CPTitlePosition
@typedef CPTitlePosition
CPNoTitle = 0;
CPAboveTop = 1;
CPAtTop = 2;
CPBelowTop = 3;
CPAboveBottom = 4;
CPAtBottom = 5;
CPBelowBottom = 6;
/*! /*!
@ingroup appkit @ingroup appkit
@class CPBox @class CPBox
@@ -59,38 +45,15 @@ CPBelowBottom = 6;
{ {
CPBoxType _boxType; CPBoxType _boxType;
CPBorderType _borderType; CPBorderType _borderType;
CPColor _borderColor;
CPColor _fillColor;
float _cornerRadius;
float _borderWidth;
CPSize _contentMargin;
CPView _contentView; CPView _contentView;
CPString _title @accessors(getter=title);
int _titlePosition @accessors(getter=titlePosition);
CPTextField _titleView;
}
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if ([aBinding hasPrefix:CPDisplayPatternTitleBinding])
return [CPTitleWithPatternBinding class];
return [super _binderClassForBinding:aBinding];
}
+ (CPString)defaultThemeClass
{
return @"box";
}
+ (CPDictionary)themeAttributes
{
return @{
@"background-color": [CPNull null],
@"border-color": [CPNull null],
@"border-width": 1.0,
@"corner-radius": 3.0,
@"inner-shadow-offset": CGSizeMakeZero(),
@"inner-shadow-size": 6.0,
@"inner-shadow-color": [CPNull null],
@"content-margin": CGSizeMakeZero(),
};
} }
+ (id)boxEnclosingView:(CPView)aView + (id)boxEnclosingView:(CPView)aView
@@ -98,7 +61,6 @@ CPBelowBottom = 6;
var box = [[self alloc] initWithFrame:CGRectMakeZero()], var box = [[self alloc] initWithFrame:CGRectMakeZero()],
enclosingView = [aView superview]; enclosingView = [aView superview];
[box setAutoresizingMask:[aView autoresizingMask]];
[box setFrameFromContentFrame:[aView frame]]; [box setFrameFromContentFrame:[aView frame]];
[enclosingView replaceSubview:aView with:box]; [enclosingView replaceSubview:aView with:box];
@@ -108,16 +70,18 @@ CPBelowBottom = 6;
return box; return box;
} }
- (id)initWithFrame:(CGRect)frameRect - (id)initWithFrame:(CPRect)frameRect
{ {
self = [super initWithFrame:frameRect]; self = [super initWithFrame:frameRect];
if (self) if (self)
{ {
_borderType = CPBezelBorder; _borderType = CPBezelBorder;
_fillColor = [CPColor clearColor];
_borderColor = [CPColor blackColor];
_titlePosition = CPNoTitle; _borderWidth = 1.0;
_titleView = [CPTextField labelWithTitle:@""]; _contentMargin = CGSizeMake(0.0, 0.0);
_contentView = [[CPView alloc] initWithFrame:[self bounds]]; _contentView = [[CPView alloc] initWithFrame:[self bounds]];
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; [_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
@@ -136,7 +100,7 @@ CPBelowBottom = 6;
@return the border rectangle of the box @return the border rectangle of the box
*/ */
- (CGRect)borderRect - (CPRect)borderRect
{ {
return [self bounds]; return [self bounds];
} }
@@ -226,54 +190,58 @@ CPBelowBottom = 6;
- (CPColor)borderColor - (CPColor)borderColor
{ {
return [self valueForThemeAttribute:@"border-color"]; return _borderColor;
} }
- (void)setBorderColor:(CPColor)color - (void)setBorderColor:(CPColor)color
{ {
if ([color isEqual:[self borderColor]]) if ([color isEqual:_borderColor])
return; return;
[self setValue:color forThemeAttribute:@"border-color"]; _borderColor = color;
[self setNeedsDisplay:YES];
} }
- (float)borderWidth - (float)borderWidth
{ {
return [self valueForThemeAttribute:@"border-width"]; return _borderWidth;
} }
- (void)setBorderWidth:(float)width - (void)setBorderWidth:(float)width
{ {
if (width === [self borderWidth]) if (width === _borderWidth)
return; return;
[self setValue:width forThemeAttribute:@"border-width"]; _borderWidth = width;
[self setNeedsDisplay:YES];
} }
- (float)cornerRadius - (float)cornerRadius
{ {
return [self valueForThemeAttribute:@"corner-radius"]; return _cornerRadius;
} }
- (void)setCornerRadius:(float)radius - (void)setCornerRadius:(float)radius
{ {
if (radius === [self cornerRadius]) if (radius === _cornerRadius)
return; return;
[self setValue:radius forThemeAttribute:@"corner-radius"]; _cornerRadius = radius;
[self setNeedsDisplay:YES];
} }
- (CPColor)fillColor - (CPColor)fillColor
{ {
return [self valueForThemeAttribute:@"background-color"]; return _fillColor;
} }
- (void)setFillColor:(CPColor)color - (void)setFillColor:(CPColor)color
{ {
if ([color isEqual:[self fillColor]]) if ([color isEqual:_fillColor])
return; return;
[self setValue:color forThemeAttribute:@"background-color"]; _fillColor = color;
[self setNeedsDisplay:YES];
} }
- (CPView)contentView - (CPView)contentView
@@ -286,160 +254,48 @@ CPBelowBottom = 6;
if (aView === _contentView) if (aView === _contentView)
return; return;
var borderWidth = [self borderWidth], [aView setFrame:CGRectInset([self bounds], _contentMargin.width + _borderWidth, _contentMargin.height + _borderWidth)];
contentMargin = [self valueForThemeAttribute:@"content-margin"];
[aView setFrame:CGRectInset([self bounds], contentMargin.width + borderWidth, contentMargin.height + borderWidth)];
[aView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; [aView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
[self replaceSubview:_contentView with:aView];
// A nil contentView is allowed (tested in Cocoa 2013-02-22).
if (!aView)
[_contentView removeFromSuperview];
else if (_contentView)
[self replaceSubview:_contentView with:aView];
else
[self addSubview:aView];
_contentView = aView; _contentView = aView;
} }
- (CGSize)contentViewMargins - (CPSize)contentViewMargins
{ {
return [self valueForThemeAttribute:@"content-margin"]; return _contentMargin;
} }
- (void)setContentViewMargins:(CGSize)size - (void)setContentViewMargins:(CPSize)size
{ {
if (size.width < 0 || size.height < 0) if (size.width < 0 || size.height < 0)
[CPException raise:CPGenericException reason:@"Margins must be positive"]; [CPException raise:CPGenericException reason:@"Margins must be positive"];
[self setValue:CGSizeMakeCopy(size) forThemeAttribute:@"content-margin"]; _contentMargin = CGSizeMakeCopy(size);
[self setNeedsDisplay:YES];
} }
- (void)setFrameFromContentFrame:(CGRect)aRect - (void)setFrameFromContentFrame:(CPRect)aRect
{ {
var offset = [self _titleHeightOffset], [self setFrame:CGRectInset(aRect, -(_contentMargin.width + _borderWidth), -(_contentMargin.height + _borderWidth))];
borderWidth = [self borderWidth],
contentMargin = [self valueForThemeAttribute:@"content-margin"];
[self setFrame:CGRectInset(aRect, -(contentMargin.width + borderWidth), -(contentMargin.height + offset[0] + borderWidth))];
}
- (void)setTitle:(CPString)aTitle
{
if (aTitle == _title)
return;
_title = aTitle;
[self _manageTitlePositioning];
}
- (void)setTitlePosition:(int)aTitlePotisition
{
if (aTitlePotisition == _titlePosition)
return;
_titlePosition = aTitlePotisition;
[self _manageTitlePositioning];
}
- (CPFont)titleFont
{
return [_titleView font];
}
- (void)setTitleFont:(CPFont)aFont
{
[_titleView setFont:aFont];
}
/*!
Return the text field used to display the receiver's title.
This is the Cappuccino equivalent to the `titleCell` method.
*/
- (CPTextField)titleView
{
return _titleView;
}
- (void)_manageTitlePositioning
{
if (_titlePosition == CPNoTitle)
{
[_titleView removeFromSuperview];
[self setNeedsDisplay:YES];
return;
}
[_titleView setStringValue:_title];
[_titleView sizeToFit];
[self addSubview:_titleView];
switch (_titlePosition)
{
case CPAtTop:
case CPAboveTop:
case CPBelowTop:
[_titleView setFrameOrigin:CGPointMake(5.0, 0.0)];
[_titleView setAutoresizingMask:CPViewNotSizable];
break;
case CPAboveBottom:
case CPAtBottom:
case CPBelowBottom:
var h = [_titleView frameSize].height;
[_titleView setFrameOrigin:CGPointMake(5.0, [self frameSize].height - h)];
[_titleView setAutoresizingMask:CPViewMinYMargin];
break;
}
[self sizeToFit];
[self setNeedsDisplay:YES]; [self setNeedsDisplay:YES];
} }
- (void)sizeToFit - (void)sizeToFit
{ {
var contentFrame = [_contentView frame], var contentFrame = [_contentView frame];
offset = [self _titleHeightOffset],
contentMargin = [self valueForThemeAttribute:@"content-margin"];
if (!contentFrame) [self setFrameSize:CGSizeMake(contentFrame.size.width + _contentMargin.width * 2,
contentFrame.size.height + _contentMargin.height * 2)];
[_contentView setFrameOrigin:CGPointMake(_contentMargin.width, _contentMargin.height)];
}
- (void)drawRect:(CPRect)rect
{
if (_borderType === CPNoBorder)
return; return;
[_contentView setFrameOrigin:CGPointMake(contentMargin.width, contentMargin.height + offset[1])];
}
- (float)_titleHeightOffset
{
if (_titlePosition == CPNoTitle)
return [0.0, 0.0];
switch (_titlePosition)
{
case CPAtTop:
return [[_titleView frameSize].height, [_titleView frameSize].height];
case CPAtBottom:
return [[_titleView frameSize].height, 0.0];
default:
return [0.0, 0.0];
}
}
- (void)setValue:(id)aValue forKey:(CPString)aKey
{
if (aKey === CPDisplayPatternTitleBinding)
[self setTitle:aValue || @""];
else
[super setValue:aValue forKey:aKey];
}
- (void)drawRect:(CGRect)rect
{
var bounds = [self bounds]; var bounds = [self bounds];
switch (_boxType) switch (_boxType)
@@ -448,158 +304,88 @@ CPBelowBottom = 6;
// NSBox does not include a horizontal flag for the separator type. We have to determine // NSBox does not include a horizontal flag for the separator type. We have to determine
// the type of separator to draw by the width and height of the frame. // the type of separator to draw by the width and height of the frame.
if (CGRectGetWidth(bounds) === 5.0) if (CGRectGetWidth(bounds) === 5.0)
return [self _drawVerticalSeparatorInRect:bounds]; return [self _drawVerticalSeperatorInRect:bounds];
else if (CGRectGetHeight(bounds) === 5.0) else if (CGRectGetHeight(bounds) === 5.0)
return [self _drawHorizontalSeparatorInRect:bounds]; return [self _drawHorizontalSeperatorInRect:bounds];
break; break;
} }
if (_titlePosition == CPAtTop)
{
bounds.origin.y += [_titleView frameSize].height;
bounds.size.height -= [_titleView frameSize].height;
}
if (_titlePosition == CPAtBottom)
{
bounds.size.height -= [_titleView frameSize].height;
}
// Primary or secondary type boxes always draw the same way, unless they are CPNoBorder.
if ((_boxType === CPBoxPrimary || _boxType === CPBoxSecondary) && _borderType !== CPNoBorder)
{
[self _drawPrimaryBorderInRect:bounds];
return;
}
switch (_borderType) switch (_borderType)
{ {
case CPBezelBorder: case CPBezelBorder:
[self _drawBezelBorderInRect:bounds]; [self _drawBezelBorderInRect:bounds];
break; break;
case CPGrooveBorder: default:
case CPLineBorder: case CPLineBorder:
[self _drawLineBorderInRect:bounds]; [self _drawLineBorderInRect:bounds];
break; break;
case CPNoBorder:
[self _drawNoBorderInRect:bounds];
break;
} }
} }
- (void)_drawHorizontalSeparatorInRect:(CGRect)aRect - (void)_drawHorizontalSeperatorInRect:(CGRect)aRect
{ {
var context = [[CPGraphicsContext currentContext] graphicsPort]; var context = [[CPGraphicsContext currentContext] graphicsPort];
CGContextSetStrokeColor(context, [self borderColor]); CGContextSetStrokeColor(context, [self borderColor]);
CGContextSetLineWidth(context, 1.0); CGContextSetLineWidth(context, 1.0);
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMidY(aRect)); CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMinY(aRect) + 0.5);
CGContextAddLineToPoint(context, CGRectGetWidth(aRect), CGRectGetMidY(aRect)); CGContextAddLineToPoint(context, CGRectGetWidth(aRect), CGRectGetMinY(aRect) + 0.5);
CGContextStrokePath(context); CGContextStrokePath(context);
} }
- (void)_drawVerticalSeparatorInRect:(CGRect)aRect - (void)_drawVerticalSeperatorInRect:(CGRect)aRect
{ {
var context = [[CPGraphicsContext currentContext] graphicsPort]; var context = [[CPGraphicsContext currentContext] graphicsPort];
CGContextSetStrokeColor(context, [self borderColor]); CGContextSetStrokeColor(context, [self borderColor]);
CGContextSetLineWidth(context, 1.0); CGContextSetLineWidth(context, 1.0);
CGContextMoveToPoint(context, CGRectGetMidX(aRect), CGRectGetMinY(aRect)); CGContextMoveToPoint(context, CGRectGetMinX(aRect) + 0.5, CGRectGetMinY(aRect));
CGContextAddLineToPoint(context, CGRectGetMidX(aRect), CGRectGetHeight(aRect)); CGContextAddLineToPoint(context, CGRectGetMinX(aRect) + 0.5, CGRectGetHeight(aRect));
CGContextStrokePath(context); CGContextStrokePath(context);
} }
- (void)_drawLineBorderInRect:(CGRect)aRect
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
cornerRadius = [self cornerRadius],
borderWidth = [self borderWidth];
aRect = CGRectInset(aRect, borderWidth / 2.0, borderWidth / 2.0);
CGContextSetFillColor(context, [self fillColor]);
CGContextSetStrokeColor(context, [self borderColor]);
CGContextSetLineWidth(context, borderWidth);
CGContextFillRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
CGContextStrokeRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
}
- (void)_drawBezelBorderInRect:(CGRect)aRect - (void)_drawBezelBorderInRect:(CGRect)aRect
{ {
var context = [[CPGraphicsContext currentContext] graphicsPort], var context = [[CPGraphicsContext currentContext] graphicsPort],
cornerRadius = [self cornerRadius], sides = [CPMinYEdge, CPMaxXEdge, CPMaxYEdge, CPMinXEdge],
borderWidth = [self borderWidth], sideGray = 190.0 / 255.0,
shadowOffset = [self valueForThemeAttribute:@"inner-shadow-offset"], grays = [142.0 / 255.0, sideGray, sideGray, sideGray],
shadowSize = [self valueForThemeAttribute:@"inner-shadow-size"], borderWidth = _borderWidth;
shadowColor = [self valueForThemeAttribute:@"inner-shadow-color"];
var baseRect = aRect; while (borderWidth--)
aRect = CGRectInset(aRect, borderWidth / 2.0, borderWidth / 2.0); aRect = CPDrawTiledRects(aRect, aRect, sides, grays);
CGContextSaveGState(context);
CGContextSetStrokeColor(context, [self borderColor]);
CGContextSetLineWidth(context, borderWidth);
CGContextSetFillColor(context, [self fillColor]);
CGContextFillRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
CGContextStrokeRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
CGContextRestoreGState(context);
}
- (void)_drawPrimaryBorderInRect:(CGRect)aRect
{
// Draw the "primary" style CPBox.
var context = [[CPGraphicsContext currentContext] graphicsPort],
cornerRadius = [self cornerRadius],
borderWidth = [self borderWidth],
shadowOffset = [self valueForThemeAttribute:@"inner-shadow-offset"],
shadowSize = [self valueForThemeAttribute:@"inner-shadow-size"],
shadowColor = [self valueForThemeAttribute:@"inner-shadow-color"],
baseRect = aRect;
aRect = CGRectInset(aRect, borderWidth / 2.0, borderWidth / 2.0);
CGContextSaveGState(context);
CGContextSetStrokeColor(context, [self borderColor]);
CGContextSetLineWidth(context, borderWidth);
CGContextSetFillColor(context, [self fillColor]);
CGContextFillRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
CGContextBeginPath(context);
// Note we can't use the 0.5 inset rectangle when setting up clipping. The clipping has to be
// on integer coordinates for this to look right in Chrome.
CGContextAddPath(context, CGPathWithRoundedRectangleInRect(baseRect, cornerRadius, cornerRadius, YES, YES, YES, YES));
CGContextClip(context);
CGContextSetShadowWithColor(context, shadowOffset, shadowSize, shadowColor);
CGContextStrokeRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
CGContextRestoreGState(context);
}
- (void)_drawNoBorderInRect:(CGRect)aRect
{
var context = [[CPGraphicsContext currentContext] graphicsPort];
CGContextSetFillColor(context, [self fillColor]); CGContextSetFillColor(context, [self fillColor]);
CGContextFillRect(context, aRect); CGContextFillRect(context, aRect);
} }
- (void)_drawLineBorderInRect:(CGRect)aRect
{
var context = [[CPGraphicsContext currentContext] graphicsPort];
aRect = CGRectInset(aRect, _borderWidth / 2.0, _borderWidth / 2.0);
CGContextSetFillColor(context, [self fillColor]);
CGContextSetStrokeColor(context, [self borderColor]);
CGContextSetLineWidth(context, _borderWidth);
CGContextFillRoundedRectangleInRect(context, aRect, _cornerRadius, YES, YES, YES, YES);
CGContextStrokeRoundedRectangleInRect(context, aRect, _cornerRadius, YES, YES, YES, YES);
}
@end @end
var CPBoxTypeKey = @"CPBoxTypeKey", var CPBoxTypeKey = @"CPBoxTypeKey",
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey", CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
CPBoxTitle = @"CPBoxTitle", CPBoxBorderColorKey = @"CPBoxBorderColorKey",
CPBoxTitlePosition = @"CPBoxTitlePosition", CPBoxFillColorKey = @"CPBoxFillColorKey",
CPBoxTitleView = @"CPBoxTitleView", CPBoxCornerRadiusKey = @"CPBoxCornerRadiusKey",
CPBoxContentView = @"CPBoxContentView"; CPBoxBorderWidthKey = @"CPBoxBorderWidthKey",
CPBoxContentMarginKey = @"CPBoxContentMarginKey";
@implementation CPBox (CPCoding) @implementation CPBox (CPCoding)
@@ -612,33 +398,18 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
_boxType = [aCoder decodeIntForKey:CPBoxTypeKey]; _boxType = [aCoder decodeIntForKey:CPBoxTypeKey];
_borderType = [aCoder decodeIntForKey:CPBoxBorderTypeKey]; _borderType = [aCoder decodeIntForKey:CPBoxBorderTypeKey];
_title = [aCoder decodeObjectForKey:CPBoxTitle]; _borderColor = [aCoder decodeObjectForKey:CPBoxBorderColorKey];
_titlePosition = [aCoder decodeIntForKey:CPBoxTitlePosition]; _fillColor = [aCoder decodeObjectForKey:CPBoxFillColorKey];
_titleView = [aCoder decodeObjectForKey:CPBoxTitleView] || [CPTextField labelWithTitle:_title];
if (_boxType != CPBoxSeparator) _cornerRadius = [aCoder decodeFloatForKey:CPBoxCornerRadiusKey];
{ _borderWidth = [aCoder decodeFloatForKey:CPBoxBorderWidthKey];
// FIXME: we have a problem with CIB decoding here.
// We should be able to simply add : _contentView = [self subviews][0] _contentMargin = [aCoder decodeSizeForKey:CPBoxContentMarginKey];
// but first box subview seems to be malformed (badly decoded).
// For example, when deployed, this view doesn't have its _trackingAreas array initialized. _contentView = [self subviews][0];
// As a (temporary) workaround, we encode/decode the _contentView property. We then transfer the subview hierarchy
// and replace the first (and only) box subview with this _contentView
_contentView = [aCoder decodeObjectForKey:CPBoxContentView] || [[CPView alloc] initWithFrame:[self bounds]];
var malformedContentView = [self subviews][0];
[_contentView setSubviews:[malformedContentView subviews]];
[self replaceSubview:malformedContentView with:_contentView];
}
else
{
_titlePosition = CPNoTitle;
}
[self setAutoresizesSubviews:YES]; [self setAutoresizesSubviews:YES];
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; [_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
[self _manageTitlePositioning];
} }
return self; return self;
@@ -650,10 +421,14 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
[aCoder encodeInt:_boxType forKey:CPBoxTypeKey]; [aCoder encodeInt:_boxType forKey:CPBoxTypeKey];
[aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey]; [aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey];
[aCoder encodeObject:_title forKey:CPBoxTitle];
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePosition]; [aCoder encodeObject:_borderColor forKey:CPBoxBorderColorKey];
[aCoder encodeObject:_titleView forKey:CPBoxTitleView]; [aCoder encodeObject:_fillColor forKey:CPBoxFillColorKey];
[aCoder encodeObject:_contentView forKey:CPBoxContentView];
[aCoder encodeFloat:_cornerRadius forKey:CPBoxCornerRadiusKey];
[aCoder encodeFloat:_borderWidth forKey:CPBoxBorderWidthKey];
[aCoder encodeSize:_contentMargin forKey:CPBoxContentMarginKey];
} }
@end @end
+136 -324
View File
@@ -20,111 +20,63 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import <Foundation/CPObject.j>
@import <Foundation/CPIndexSet.j> @import <Foundation/CPIndexSet.j>
@import "CPControl.j" @import "CPControl.j"
@import "CPImage.j" @import "CPImage.j"
@import "CPScrollView.j"
@import "CPTableView.j" @import "CPTableView.j"
@import "CPTextField.j" @import "CPTextField.j"
@import "CPScrollView.j"
@global CPApp
@protocol CPBrowserDelegate <CPObject>
@optional
- (BOOL)browser:(CPBrowser)browser acceptDrop:(id)info atRow:(CPInteger)row column:(CPInteger)column dropOperation:(CPTableViewDropOperation)dropOperation;
- (BOOL)browser:(CPBrowser)browser canDragRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(CPInteger)column withEvent:(CPEvent )event;
- (BOOL)browser:(CPBrowser)browser isLeafItem:(id)item;
- (BOOL)browser:(CPBrowser)browser shouldSelectRowIndexes:(CPIndexSet)anIndexSet inColumn:(CPInteger)column;
- (BOOL)browser:(CPBrowser)browser writeRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(CPInteger)column toPasteboard:(CPPasteboard)pasteboard;
- (CPDragOperation)browser:(CPBrowser)browser validateDrop:(id)info proposedRow:(CPInteger)row column:(CPInteger)column dropOperation:(CPTableViewDropOperation)dropOperation;
- (CPImage)browser:(CPBrowser)browser imageValueForItem:(id)anItem;
- (CPImage)browser:(CPBrowser)browser draggingImageForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(CPInteger)column withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
- (CPImage)browser:(CPBrowser)browser imageValueForItem:(id)item;
- (CPIndexSet)browser:(CPBrowser)browser selectionIndexesForProposedSelection:(CPIndexSet)proposedSelectionIndexes inColumn:(CPInteger)column;
- (CPInteger)browser:(CPBrowser)browser numberOfChildrenOfItem:(id)item;
- (CPView)browser:(CPBrowser)browser draggingViewForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(CPInteger)column withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
- (id)browser:(CPBrowser)browser child:(CPInteger)index ofItem:(id)item;
- (id)browser:(CPBrowser)browser objectValueForItem:(id)item;
- (id)rootItemForBrowser:(CPBrowser)browser;
- (void)browser:(CPBrowser)browser didChangeLastColumn:(CPInteger)oldLastColumn toColumn:(CPInteger)column;
- (void)browser:(CPBrowser)browser didResizeColumn:(CPInteger)column;
- (void)browserSelectionIsChanging:(CPBrowser)browser;
- (void)browserSelectionDidChange:(CPBrowser)browser;
@end
var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_ = 1 << 1,
CPBrowserDelegate_browser_canDragRowsWithIndexes_inColumn_withEvent_ = 1 << 2,
CPBrowserDelegate_browser_isLeafItem_ = 1 << 3,
CPBrowserDelegate_browser_shouldSelectRowIndexes_inColumn_ = 1 << 4,
CPBrowserDelegate_browser_writeRowsWithIndexes_inColumn_toPasteboard_ = 1 << 5,
CPBrowserDelegate_browser_validateDrop_proposedRow_column_dropOperation_ = 1 << 6,
CPBrowserDelegate_browser_imageValueForItem_ = 1 << 7,
CPBrowserDelegate_browser_draggingImageForRowsWithIndexes_inColumn_withEvent_offset_ = 1 << 8,
CPBrowserDelegate_browser_imageValueForItem_ = 1 << 9,
CPBrowserDelegate_browser_selectionIndexesForProposedSelection_inColumn_ = 1 << 10,
CPBrowserDelegate_browser_numberOfChildrenOfItem_ = 1 << 11,
CPBrowserDelegate_browser_draggingViewForRowsWithIndexes_inColumn_withEvent_offset_ = 1 << 12,
CPBrowserDelegate_browser_child_ofItem_ = 1 << 13,
CPBrowserDelegate_browser_objectValueForItem_ = 1 << 14,
CPBrowserDelegate_rootItemForBrowser_ = 1 << 15,
CPBrowserDelegate_browser_didChangeLastColumn_toColumn_ = 1 << 16,
CPBrowserDelegate_browser_didResizeColumn_ = 1 << 17,
CPBrowserDelegate_browserSelectionIsChanging_ = 1 << 18,
CPBrowserDelegate_browserSelectionDidChange_ = 1 << 19;
/*! /*!
@ingroup appkit @ingroup appkit
@class CPBrowser @class CPBrowser
*/ */
@implementation CPBrowser : CPControl @implementation CPBrowser : CPControl
{ {
id <CPBrowserDelegate> _delegate; id _delegate;
CPString _pathSeparator; CPString _pathSeparator;
unsigned _implementedDelegateMethods;
CPView _contentView; CPView _contentView;
CPScrollView _horizontalScrollView; CPScrollView _horizontalScrollView;
CPView _prototypeView; CPView _prototypeView;
CPArray _tableViews; CPArray _tableViews;
CPArray _tableDelegates; CPArray _tableDelegates;
id _rootItem; id _rootItem;
BOOL _delegateSupportsImages; BOOL _delegateSupportsImages;
SEL _doubleAction @accessors(property=doubleAction); SEL _doubleAction @accessors(property=doubleAction);
BOOL _allowsMultipleSelection; BOOL _allowsMultipleSelection;
BOOL _allowsEmptySelection; BOOL _allowsEmptySelection;
Class _tableViewClass @accessors(property=tableViewClass); Class _tableViewClass @accessors(property=tableViewClass);
float _rowHeight; float _rowHeight;
float _imageWidth; float _imageWidth;
float _leafWidth; float _leafWidth;
float _minColumnWidth; float _minColumnWidth;
float _defaultColumnWidth @accessors(property=defaultColumnWidth); float _defaultColumnWidth @accessors(property=defaultColumnWidth);
CPArray _columnWidths; CPArray _columnWidths;
} }
+ (CPString)defaultThemeClass + (CPImage)branchImage
{ {
return "browser"; return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]]
pathForResource:"browser-leaf.png"]
size:CGSizeMake(9,9)];
} }
+ (CPDictionary)themeAttributes + (CPImage)highlightedBranchImage
{ {
return @{ return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]]
@"image-control-resize": [CPNull null], pathForResource:"browser-leaf-highlighted.png"]
@"image-control-leaf": [CPNull null], size:CGSizeMake(9,9)];
@"image-control-leaf-pressed": [CPNull null]
};
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
@@ -184,74 +136,10 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
[CPKeyedArchiver archivedDataWithRootObject:_prototypeView]]; [CPKeyedArchiver archivedDataWithRootObject:_prototypeView]];
} }
- (void)setDelegate:(id <CPBrowserDelegate>)anObject - (void)setDelegate:(id)anObject
{ {
if (_delegate === anObject)
return;
_delegate = anObject; _delegate = anObject;
_implementedDelegateMethods = 0; _delegateSupportsImages = [_delegate respondsToSelector:@selector(browser:imageValueForItem:)];
_delegateSupportsImages = NO;
if ([_delegate respondsToSelector:@selector(browser:acceptDrop:atRow:column:dropOperation:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_;
if ([_delegate respondsToSelector:@selector(browser:canDragRowsWithIndexes:inColumn:withEvent:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_canDragRowsWithIndexes_inColumn_withEvent_;
if ([_delegate respondsToSelector:@selector(browser:isLeafItem:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_isLeafItem_;
if ([_delegate respondsToSelector:@selector(browser:shouldSelectRowIndexes:inColumn:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_shouldSelectRowIndexes_inColumn_;
if ([_delegate respondsToSelector:@selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_writeRowsWithIndexes_inColumn_toPasteboard_;
if ([_delegate respondsToSelector:@selector(browser:validateDrop:proposedRow:column:dropOperation:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_validateDrop_proposedRow_column_dropOperation_;
if ([_delegate respondsToSelector:@selector(browser:imageValueForItem:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_imageValueForItem_;
if ([_delegate respondsToSelector:@selector(browser:draggingImageForRowsWithIndexes:inColumn:withEvent:offset:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_draggingImageForRowsWithIndexes_inColumn_withEvent_offset_;
if ([_delegate respondsToSelector:@selector(browser:imageValueForItem:)])
{
_delegateSupportsImages = YES;
_implementedDelegateMethods |= CPBrowserDelegate_browser_imageValueForItem_;
}
if ([_delegate respondsToSelector:@selector(browser:selectionIndexesForProposedSelection:inColumn:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_selectionIndexesForProposedSelection_inColumn_;
if ([_delegate respondsToSelector:@selector(browser:numberOfChildrenOfItem:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_numberOfChildrenOfItem_;
if ([_delegate respondsToSelector:@selector(browser:draggingViewForRowsWithIndexes:inColumn:withEvent:offset:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_draggingViewForRowsWithIndexes_inColumn_withEvent_offset_;
if ([_delegate respondsToSelector:@selector(browser:child:ofItem:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_child_ofItem_;
if ([_delegate respondsToSelector:@selector(browser:objectValueForItem:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_objectValueForItem_;
if ([_delegate respondsToSelector:@selector(rootItemForBrowser:)])
_implementedDelegateMethods |= CPBrowserDelegate_rootItemForBrowser_;
if ([_delegate respondsToSelector:@selector(browser:didChangeLastColumn:toColumn:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_didChangeLastColumn_toColumn_;
if ([_delegate respondsToSelector:@selector(browser:didChangeLastColumn:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_didResizeColumn_;
if ([_delegate respondsToSelector:@selector(browserSelectionIsChanging:)])
_implementedDelegateMethods |= CPBrowserDelegate_browserSelectionIsChanging_;
if ([_delegate respondsToSelector:@selector(browserSelectionDidChange:)])
_implementedDelegateMethods |= CPBrowserDelegate_browserSelectionDidChange_;
[self loadColumnZero]; [self loadColumnZero];
} }
@@ -273,13 +161,16 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
- (void)loadColumnZero - (void)loadColumnZero
{ {
_rootItem = [self _sendDelegateRootItemForBrowser]; if ([_delegate respondsToSelector:@selector(rootItemForBrowser:)])
_rootItem = [_delegate rootItemForBrowser:self];
else
_rootItem = nil;
[self setLastColumn:-1]; [self setLastColumn:-1];
[self addColumn]; [self addColumn];
} }
- (void)setLastColumn:(CPInteger)columnIndex - (void)setLastColumn:(int)columnIndex
{ {
if (columnIndex >= _tableViews.length) if (columnIndex >= _tableViews.length)
return; return;
@@ -287,18 +178,14 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
var oldValue = _tableViews.length - 1, var oldValue = _tableViews.length - 1,
indexPlusOne = columnIndex + 1; // unloads all later columns. indexPlusOne = columnIndex + 1; // unloads all later columns.
if (columnIndex > 0)
[_tableViews[columnIndex - 1] setNeedsDisplay:YES];
[_tableViews[columnIndex] setNeedsDisplay:YES];
[[_tableViews.slice(indexPlusOne) valueForKey:"enclosingScrollView"] [[_tableViews.slice(indexPlusOne) valueForKey:"enclosingScrollView"]
makeObjectsPerformSelector:@selector(removeFromSuperview)]; makeObjectsPerformSelector:@selector(removeFromSuperview)];
_tableViews = _tableViews.slice(0, indexPlusOne); _tableViews = _tableViews.slice(0, indexPlusOne);
_tableDelegates = _tableDelegates.slice(0, indexPlusOne); _tableDelegates = _tableDelegates.slice(0, indexPlusOne);
[self _sendDelegateBrowserDidChangeLastColumn:oldValue toColumn:columnIndex]; if ([_delegate respondsToSelector:@selector(browser:didChangeLastColumn:toColumn:)])
[_delegate browser:self didChangeLastColumn:oldValue toColumn:columnIndex];
[self tile]; [self tile];
} }
@@ -371,7 +258,7 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
var column = [[CPTableColumn alloc] initWithIdentifier:@"Image"], var column = [[CPTableColumn alloc] initWithIdentifier:@"Image"],
view = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; view = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
[view setImageScaling:CPImageScaleProportionallyDown]; [view setImageScaling:CPScaleProportionally];
[column setDataView:view]; [column setDataView:view];
[column setResizingMask:CPTableColumnNoResizing]; [column setResizingMask:CPTableColumnNoResizing];
@@ -389,8 +276,8 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
var column = [[CPTableColumn alloc] initWithIdentifier:@"Leaf"], var column = [[CPTableColumn alloc] initWithIdentifier:@"Leaf"],
view = [[_CPBrowserLeafView alloc] initWithFrame:CGRectMakeZero()]; view = [[_CPBrowserLeafView alloc] initWithFrame:CGRectMakeZero()];
[view setBranchImage:[self valueForThemeAttribute:@"image-control-leaf"]]; [view setBranchImage:[[self class] branchImage]];
[view setHighlightedBranchImage:[self valueForThemeAttribute:@"image-control-leaf-pressed"]]; [view setHighlightedBranchImage:[[self class] highlightedBranchImage]];
[column setDataView:view]; [column setDataView:view];
[column setResizingMask:CPTableColumnNoResizing]; [column setResizingMask:CPTableColumnNoResizing];
@@ -398,7 +285,7 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
[aTableView addTableColumn:column]; [aTableView addTableColumn:column];
} }
- (void)reloadColumn:(CPInteger)column - (void)reloadColumn:(int)column
{ {
[[self tableViewInColumn:column] reloadData]; [[self tableViewInColumn:column] reloadData];
} }
@@ -466,17 +353,17 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
// ITEMS // ITEMS
- (id)itemAtRow:(CPInteger)row inColumn:(CPInteger)column - (id)itemAtRow:(int)row inColumn:(int)column
{ {
return [_tableDelegates[column] childAtIndex:row]; return [_tableDelegates[column] childAtIndex:row];
} }
- (BOOL)isLeafItem:(id)item - (BOOL)isLeafItem:(id)item
{ {
return (_implementedDelegateMethods & CPBrowserDelegate_browser_isLeafItem_) && [_delegate browser:self isLeafItem:item]; return [_delegate respondsToSelector:@selector(browser:isLeafItem:)] && [_delegate browser:self isLeafItem:item];
} }
- (id)parentForItemsInColumn:(CPInteger)column - (id)parentForItemsInColumn:(int)column
{ {
return [_tableDelegates[column] _item]; return [_tableDelegates[column] _item];
} }
@@ -538,26 +425,11 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
- (void)keyDown:(CPEvent)anEvent - (void)keyDown:(CPEvent)anEvent
{ {
var key = [anEvent charactersIgnoringModifiers], var column = [self selectedColumn];
column = [self selectedColumn]; if (column === -1)
if (column === CPNotFound)
return; return;
if (key === CPLeftArrowFunctionKey || key === CPRightArrowFunctionKey) [_tableViews[column] keyDown:anEvent];
{
if (key === CPLeftArrowFunctionKey)
{
var previousColumn = column - 1,
selectedRow = [self selectedRowInColumn:previousColumn];
[self selectRow:selectedRow inColumn:previousColumn];
}
else
[self selectRow:0 inColumn:column + 1];
}
else
[_tableViews[column] keyDown:anEvent];
} }
// SIZING // SIZING
@@ -600,7 +472,9 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
{ {
_columnWidths[column] = aWidth; _columnWidths[column] = aWidth;
[self _sendDelegateBrowserDidResizeColumn:column]; if ([_delegate respondsToSelector:@selector(browser:didResizeColumn:)])
[_delegate browser:self didResizeColumn:column];
[self tile]; [self tile];
} }
@@ -715,12 +589,15 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
if (column < 0 || column > [self lastColumn] + 1) if (column < 0 || column > [self lastColumn] + 1)
return; return;
indexSet = [self _sendDelegateBrowserSelectionIndexesForProposedSelection:indexSet inColumn:column]; if ([_delegate respondsToSelector:@selector(browser:selectionIndexesForProposedSelection:inColumn:)])
indexSet = [_delegate browser:self selectionIndexesForProposedSelection:indexSet inColumn:column];
if (![self _sendDelegateBrowserShouldSelectRowIndexes:indexSet inColumn:column]) if ([_delegate respondsToSelector:@selector(browser:shouldSelectRowIndexes:inColumn:)] &&
![_delegate browser:self shouldSelectRowIndexes:indexSet inColumn:column])
return; return;
[self _sendDelegateBrowserSelectionIsChanging]; if ([_delegate respondsToSelector:@selector(browserSelectionIsChanging:)])
[_delegate browserSelectionIsChanging:self];
if (column > [self lastColumn]) if (column > [self lastColumn])
[self addColumn]; [self addColumn];
@@ -731,7 +608,8 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
[self scrollColumnToVisible:column]; [self scrollColumnToVisible:column];
[self _sendDelegateBrowserSelectionDidChange]; if ([_delegate respondsToSelector:@selector(browserSelectionDidChange:)])
[_delegate browserSelectionDidChange:self];
} }
- (void)setBackgroundColor:(CPColor)aColor - (void)setBackgroundColor:(CPColor)aColor
@@ -753,6 +631,30 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
[_tableViews makeObjectsPerformSelector:@selector(registerForDraggedTypes:) withObject:types]; [_tableViews makeObjectsPerformSelector:@selector(registerForDraggedTypes:) withObject:types];
} }
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent
{
if ([_delegate respondsToSelector:@selector(browser:canDragRowsWithIndexes:inColumn:withEvent:)])
return [_delegate browser:self canDragRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent];
return YES;
}
- (CPImage)draggingImageForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
{
if ([_delegate respondsToSelector:@selector(browser:draggingImageForRowsWithIndexes:inColumn:withEvent:offset:)])
return [_delegate browser:self draggingImageForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset];
return nil;
}
- (CPView)draggingViewForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
{
if ([_delegate respondsToSelector:@selector(browser:draggingViewForRowsWithIndexes:inColumn:withEvent:offset:)])
return [_delegate browser:self draggingViewForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset];
return nil;
}
@end @end
@implementation CPBrowser (CPCoding) @implementation CPBrowser (CPCoding)
@@ -802,6 +704,8 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
@end @end
var _CPBrowserResizeControlBackgroundImage = nil;
@implementation _CPBrowserResizeControl : CPView @implementation _CPBrowserResizeControl : CPView
{ {
CGPoint _mouseDownX; CGPoint _mouseDownX;
@@ -810,14 +714,22 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
unsigned _width; unsigned _width;
} }
+ (CPImage)backgroundImage
{
if (!_CPBrowserResizeControlBackgroundImage)
{
var path = [[CPBundle bundleForClass:[self class]] pathForResource:"browser-resize-control.png"];
_CPBrowserResizeControlBackgroundImage = [[CPImage alloc] initWithContentsOfFile:path
size:CGSizeMake(15, 14)];
}
return _CPBrowserResizeControlBackgroundImage;
}
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
{ {
if (self = [super initWithFrame:aFrame]) if (self = [super initWithFrame:aFrame])
{ [self setBackgroundColor:[CPColor colorWithPatternImage:[[self class] backgroundImage]]];
var browser = [[CPBrowser alloc] init];
[self setBackgroundColor:[CPColor colorWithPatternImage:[browser valueForThemeAttribute:@"image-control-resize"]]];
}
return self; return self;
} }
@@ -848,7 +760,7 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
CPBrowser _browser @accessors; CPBrowser _browser @accessors;
} }
- (id)initWithFrame:(CGRect)aFrame - (void)initWithFrame:(CGRect)aFrame
{ {
if (self = [super initWithFrame:aFrame]) if (self = [super initWithFrame:aFrame])
{ {
@@ -902,26 +814,18 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
return _browser; return _browser;
} }
/*!
@ignore
*/
- (BOOL)_isFocused
{
return ([super _isFocused] || [_browser tableViewInColumn:[_browser selectedColumn]] === self);
}
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint - (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint
{ {
return [_browser canDragRowsWithIndexes:rowIndexes inColumn:[_browser columnOfTableView:self] withEvent:[CPApp currentEvent]]; return [_browser canDragRowsWithIndexes:rowIndexes inColumn:[_browser columnOfTableView:self] withEvent:[CPApp currentEvent]];
} }
- (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset - (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPointPointer)dragImageOffset
{ {
return [_browser draggingImageForRowsWithIndexes:dragRows inColumn:[_browser columnOfTableView:self] withEvent:dragEvent offset:dragImageOffset] || return [_browser draggingImageForRowsWithIndexes:dragRows inColumn:[_browser columnOfTableView:self] withEvent:dragEvent offset:dragImageOffset] ||
[super dragImageForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragImageOffset]; [super dragImageForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragImageOffset];
} }
- (CPView)dragViewForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CGPoint)dragViewOffset - (CPView)dragViewForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPoint)dragViewOffset
{ {
var count = theTableColumns.length; var count = theTableColumns.length;
while (count--) while (count--)
@@ -934,9 +838,35 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
[super dragViewForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragViewOffset]; [super dragViewForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragViewOffset];
} }
- (void)moveUp:(id)sender
{
[super moveUp:sender];
[_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]];
}
- (void)moveDown:(id)sender
{
[super moveDown:sender];
[_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]];
}
- (void)moveLeft:(id)sender
{
var previousColumn = [_browser selectedColumn] - 1,
selectedRow = [_browser selectedRowInColumn:previousColumn];
[_browser selectRow:selectedRow inColumn:previousColumn];
}
- (void)moveRight:(id)sender
{
[_browser selectRow:0 inColumn:[_browser selectedColumn] + 1];
}
@end @end
@import <Foundation/CPObject.j>
@implementation _CPBrowserTableDelegate : CPObject @implementation _CPBrowserTableDelegate : CPObject
{ {
CPBrowser _browser @accessors; CPBrowser _browser @accessors;
@@ -971,28 +901,22 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
[_browser _column:_index clickedRow:[selectedIndexes count] === 1 ? [selectedIndexes firstIndex] : -1]; [_browser _column:_index clickedRow:[selectedIndexes count] === 1 ? [selectedIndexes firstIndex] : -1];
} }
- (void)tableViewSelectionDidChange:(CPNotification)aNotification - (id)childAtIndex:(unsigned)index
{
var selectedIndexes = [[aNotification object] selectedRowIndexes];
[_browser selectRowIndexes:selectedIndexes inColumn:_index];
}
- (id)childAtIndex:(CPUInteger)index
{ {
return [_delegate browser:_browser child:index ofItem:_item]; return [_delegate browser:_browser child:index ofItem:_item];
} }
- (BOOL)tableView:(CPTableView)aTableView acceptDrop:(id)info row:(CPInteger)row dropOperation:(CPTableViewDropOperation)operation - (BOOL)tableView:(CPTableView)aTableView acceptDrop:(id)info row:(int)row dropOperation:(CPTableViewDropOperation)operation
{ {
if (_browser._implementedDelegateMethods & CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_) if ([_delegate respondsToSelector:@selector(browser:acceptDrop:atRow:column:dropOperation:)])
return [_delegate browser:_browser acceptDrop:info atRow:row column:_index dropOperation:operation]; return [_delegate browser:_browser acceptDrop:info atRow:row column:_index dropOperation:operation];
else else
return NO; return NO;
} }
- (CPDragOperation)tableView:(CPTableView)aTableView validateDrop:(id)info proposedRow:(CPInteger)row proposedDropOperation:(CPTableViewDropOperation)operation - (CPDragOperation)tableView:(CPTableView)aTableView validateDrop:(id)info proposedRow:(int)row proposedDropOperation:(CPTableViewDropOperation)operation
{ {
if (_browser._implementedDelegateMethods & CPBrowserDelegate_browser_validateDrop_proposedRow_column_dropOperation_) if ([_delegate respondsToSelector:@selector(browser:validateDrop:proposedRow:column:dropOperation:)])
return [_delegate browser:_browser validateDrop:info proposedRow:row column:_index dropOperation:operation]; return [_delegate browser:_browser validateDrop:info proposedRow:row column:_index dropOperation:operation];
else else
return CPDragOperationNone; return CPDragOperationNone;
@@ -1000,7 +924,7 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
- (BOOL)tableView:(CPTableView)aTableView writeRowsWithIndexes:(CPIndexSet)rowIndexes toPasteboard:(CPPasteboard)pboard - (BOOL)tableView:(CPTableView)aTableView writeRowsWithIndexes:(CPIndexSet)rowIndexes toPasteboard:(CPPasteboard)pboard
{ {
if (_browser._implementedDelegateMethods & CPBrowserDelegate_browser_writeRowsWithIndexes_inColumn_toPasteboard_) if ([_delegate respondsToSelector:@selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:)])
return [_delegate browser:_browser writeRowsWithIndexes:rowIndexes inColumn:_index toPasteboard:pboard]; return [_delegate browser:_browser writeRowsWithIndexes:rowIndexes inColumn:_index toPasteboard:pboard];
else else
return NO; return NO;
@@ -1055,10 +979,10 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
var imageView = [self layoutEphemeralSubviewNamed:@"image-view" var imageView = [self layoutEphemeralSubviewNamed:@"image-view"
positioned:CPWindowAbove positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:nil], relativeToEphemeralSubviewNamed:nil],
isHighlighted = [self hasThemeState:CPThemeStateSelectedDataView]; isHighlighted = [self themeState] & CPThemeStateSelectedDataView;
[imageView setImage: _isLeaf ? (isHighlighted ? _highlightedBranchImage : _branchImage) : nil]; [imageView setImage: _isLeaf ? (isHighlighted ? _highlightedBranchImage : _branchImage) : nil];
[imageView setImageScaling:CPImageScaleNone]; [imageView setImageScaling:CPScaleNone];
} }
- (void)encodeWithCoder:(CPCoder)aCoder - (void)encodeWithCoder:(CPCoder)aCoder
@@ -1070,7 +994,7 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
[aCoder encodeObject:_highlightedBranchImage forKey:"_CPBrowserLeafViewHighlightedBranchImageKey"]; [aCoder encodeObject:_highlightedBranchImage forKey:"_CPBrowserLeafViewHighlightedBranchImageKey"];
} }
- (id)initWithCoder:(CPCoder)aCoder - (void)initWithCoder:(CPCoder)aCoder
{ {
if (self = [super initWithCoder:aCoder]) if (self = [super initWithCoder:aCoder])
{ {
@@ -1083,115 +1007,3 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
} }
@end @end
@implementation CPBrowser (CPBrowserDelegate)
/*!
@ignore
Call delegate rootItemForBrowser:
*/
- (id)_sendDelegateRootItemForBrowser
{
if (!(_implementedDelegateMethods & CPBrowserDelegate_rootItemForBrowser_))
return nil;
return [_delegate rootItemForBrowser:self];
}
/*!
@ignore
Call delegate browser:didChangeLastColumn:toColumn:
*/
- (void)_sendDelegateBrowserDidChangeLastColumn:(CPInteger)lastColumn toColumn:(CPInteger)newColumn
{
if (!(_implementedDelegateMethods & CPBrowserDelegate_browser_didChangeLastColumn_toColumn_))
return;
[_delegate browser:self didChangeLastColumn:lastColumn toColumn:newColumn];
}
/*!
@ignore
Call delegate browser:didResizeColumn:
*/
- (void)_sendDelegateBrowserDidResizeColumn:(CPInteger)column
{
if (!(_implementedDelegateMethods & CPBrowserDelegate_browser_didResizeColumn_))
return;
[_delegate browser:self didResizeColumn:column];
}
/*!
@ignore
Call delegate browserSelectionIsChanging:
*/
- (void)_sendDelegateBrowserSelectionIsChanging
{
if (!(_implementedDelegateMethods & CPBrowserDelegate_browserSelectionIsChanging_))
return;
[_delegate browserSelectionIsChanging:self];
}
/*!
@ignore
Call delegate browser:shouldSelectRowIndexes:inColumn:
*/
- (BOOL)_sendDelegateBrowserShouldSelectRowIndexes:(CPIndexSet)anIndexSet inColumn:(CPInteger)aColumn
{
if (!(_implementedDelegateMethods & CPBrowserDelegate_browser_shouldSelectRowIndexes_inColumn_))
return YES;
return [_delegate browser:self shouldSelectRowIndexes:anIndexSet inColumn:aColumn];
}
/*!
@ignore
Call delegate browser:selectionIndexesForProposedSelection:inColumn:
*/
- (CPIndexSet)_sendDelegateBrowserSelectionIndexesForProposedSelection:(CPIndexSet)anIndexSet inColumn:(CPInteger)aColumn
{
if (!(_implementedDelegateMethods & CPBrowserDelegate_browser_selectionIndexesForProposedSelection_inColumn_))
return anIndexSet;
return [_delegate browser:self selectionIndexesForProposedSelection:anIndexSet inColumn:aColumn];
}
/*!
@ignore
Call delegate browserSelectionDidChange
*/
- (void)_sendDelegateBrowserSelectionDidChange
{
if (!(_implementedDelegateMethods & CPBrowserDelegate_browserSelectionDidChange_))
return;
[_delegate browserSelectionDidChange:self];
}
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(CPInteger)columnIndex withEvent:(CPEvent)dragEvent
{
if (_implementedDelegateMethods & CPBrowserDelegate_browser_canDragRowsWithIndexes_inColumn_withEvent_)
return [_delegate browser:self canDragRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent];
return YES;
}
- (CPImage)draggingImageForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(CPInteger)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
{
if (_implementedDelegateMethods & CPBrowserDelegate_browser_draggingImageForRowsWithIndexes_inColumn_withEvent_offset_)
return [_delegate browser:self draggingImageForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset];
return nil;
}
- (CPView)draggingViewForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(CPInteger)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
{
if (_implementedDelegateMethods & CPBrowserDelegate_browser_draggingViewForRowsWithIndexes_inColumn_withEvent_offset_)
return [_delegate browser:self draggingViewForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset];
return nil;
}
@end
+122 -291
View File
@@ -25,30 +25,28 @@
@import "CPControl.j" @import "CPControl.j"
@import "CPStringDrawing.j" @import "CPStringDrawing.j"
@import "CPText.j"
@import "CPWindow_Constants.j"
/* @group CPBezelStyle */ /* @group CPBezelStyle */
@typedef CPBezelStyle
CPRoundedBezelStyle = 1; // Push CPRoundedBezelStyle = 1;
CPRegularSquareBezelStyle = 2; // Bevel CPRegularSquareBezelStyle = 2;
CPThickSquareBezelStyle = 3; CPThickSquareBezelStyle = 3;
CPThickerSquareBezelStyle = 4; CPThickerSquareBezelStyle = 4;
CPDisclosureBezelStyle = 5; // Disclosure triangle CPDisclosureBezelStyle = 5;
CPShadowlessSquareBezelStyle = 6; // Square CPShadowlessSquareBezelStyle = 6;
CPCircularBezelStyle = 7; // Round CPCircularBezelStyle = 7;
CPTexturedSquareBezelStyle = 8; // Textured CPTexturedSquareBezelStyle = 8;
CPHelpButtonBezelStyle = 9; // Help CPHelpButtonBezelStyle = 9;
CPSmallSquareBezelStyle = 10; // Gradient CPSmallSquareBezelStyle = 10;
CPTexturedRoundedBezelStyle = 11; // Round Textured CPTexturedRoundedBezelStyle = 11;
CPRoundRectBezelStyle = 12; // Round Rect CPRoundRectBezelStyle = 12;
CPRecessedBezelStyle = 13; // Recessed CPRecessedBezelStyle = 13;
CPRoundedDisclosureBezelStyle = 14; // Disclosure CPRoundedDisclosureBezelStyle = 14;
CPHUDBezelStyle = -1; CPHUDBezelStyle = -1;
/* @group CPButtonType */ /* @group CPButtonType */
@typedef CPButtonType
CPMomentaryLightButton = 0; CPMomentaryLightButton = 0;
CPPushOnPushOffButton = 1; CPPushOnPushOffButton = 1;
CPToggleButton = 2; CPToggleButton = 2;
@@ -60,11 +58,11 @@ CPMomentaryPushInButton = 7;
CPMomentaryPushButton = 0; CPMomentaryPushButton = 0;
CPMomentaryLight = 7; CPMomentaryLight = 7;
CPNoButtonMask = 0; CPNoButtonMask = 0;
CPContentsButtonMask = 1; CPContentsButtonMask = 1;
CPPushInButtonMask = 2; CPPushInButtonMask = 2;
CPGrayButtonMask = 4; CPGrayButtonMask = 4;
CPBackgroundButtonMask = 8; CPBackgroundButtonMask = 8;
CPNoCellMask = CPNoButtonMask; CPNoCellMask = CPNoButtonMask;
CPContentsCellMask = CPContentsButtonMask; CPContentsCellMask = CPContentsButtonMask;
@@ -74,18 +72,14 @@ CPChangeBackgroundCellMask = CPBackgroundButtonMask;
CPButtonStateMixed = CPThemeState("mixed"); CPButtonStateMixed = CPThemeState("mixed");
CPButtonStateBezelStyleRounded = CPThemeState("rounded"); CPButtonStateBezelStyleRounded = CPThemeState("rounded");
CPButtonStateBezelStyleRoundRect = CPThemeState("roundRect");
// add all future correspondance between bezel styles and theme state here. // add all future correspondance between bezel styles and theme state here.
var CPButtonBezelStyleStateMap = @{ var CPButtonBezelStyleStateMap = [CPDictionary dictionaryWithObjects:[CPButtonStateBezelStyleRounded, nil]
CPRoundedBezelStyle: CPButtonStateBezelStyleRounded, forKeys:[CPRoundedBezelStyle, CPRoundRectBezelStyle]];
CPRoundRectBezelStyle: CPButtonStateBezelStyleRoundRect,
};
/// @cond IGNORE
CPButtonDefaultHeight = 25.0; CPButtonDefaultHeight = 24.0;
CPButtonImageOffset = 3.0; CPButtonImageOffset = 3.0;
/// @endcond
/*! /*!
@ingroup appkit @ingroup appkit
@@ -108,6 +102,7 @@ CPButtonImageOffset = 3.0;
// NS-style Display Properties // NS-style Display Properties
CPBezelStyle _bezelStyle; CPBezelStyle _bezelStyle;
CPControlSize _controlSize;
CPString _keyEquivalent; CPString _keyEquivalent;
unsigned _keyEquivalentModifierMask; unsigned _keyEquivalentModifierMask;
@@ -116,16 +111,6 @@ CPButtonImageOffset = 3.0;
CPTimer _continuousTimer; CPTimer _continuousTimer;
float _periodicDelay; float _periodicDelay;
float _periodicInterval; float _periodicInterval;
BOOL _isTracking;
}
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding === CPTargetBinding || [aBinding hasPrefix:CPArgumentBinding])
return [CPActionBinding class];
return [super _binderClassForBinding:aBinding];
} }
+ (id)buttonWithTitle:(CPString)aTitle + (id)buttonWithTitle:(CPString)aTitle
@@ -149,21 +134,16 @@ CPButtonImageOffset = 3.0;
return @"button"; return @"button";
} }
+ (CPDictionary)themeAttributes + (id)themeAttributes
{ {
return @{ return [CPDictionary dictionaryWithObjects:[[CPNull null], 0.0, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null]]
@"image": [CPNull null], forKeys:[@"image", @"image-offset", @"bezel-inset", @"content-inset", @"bezel-color"]];
@"image-offset": 0.0,
@"bezel-inset": CGInsetMakeZero(),
@"content-inset": CGInsetMakeZero(),
@"bezel-color": [CPNull null],
};
} }
/*! /*!
Initializes and returns a newly allocated CPButton object with a specified frame rectangle. Initializes and returns a newly allocated CPButton object with a specified frame rectangle.
@param aFrame The frame rectangle for the created button object. @param aFrame The frame rectangle for the created button object.
@return An initialized CPView object or nil if the object couldn't be created. @return An initialized CPView object or nil if the object couldn't be created.
*/ */
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
{ {
@@ -175,7 +155,7 @@ CPButtonImageOffset = 3.0;
[self setValue:CPCenterTextAlignment forThemeAttribute:@"alignment"]; [self setValue:CPCenterTextAlignment forThemeAttribute:@"alignment"];
[self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"]; [self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
[self setValue:CPImageLeft forThemeAttribute:@"image-position"]; [self setValue:CPImageLeft forThemeAttribute:@"image-position"];
[self setValue:CPImageScaleNone forThemeAttribute:@"image-scaling"]; [self setValue:CPScaleNone forThemeAttribute:@"image-scaling"];
[self setBezelStyle:CPRoundRectBezelStyle]; [self setBezelStyle:CPRoundRectBezelStyle];
[self setBordered:YES]; [self setBordered:YES];
@@ -188,33 +168,19 @@ CPButtonImageOffset = 3.0;
- (void)_init - (void)_init
{ {
_controlSize = CPRegularControlSize;
_keyEquivalent = @""; _keyEquivalent = @"";
_keyEquivalentModifierMask = 0; _keyEquivalentModifierMask = 0;
// Continuous button defaults. // Continuous button defaults.
_periodicInterval = 0.05; _periodicInterval = 0.05;
_periodicDelay = 0.5; _periodicDelay = 0.5;
[self setButtonType:CPMomentaryPushInButton];
} }
#pragma mark -
#pragma mark Control Size
- (void)setControlSize:(CPControlSize)aControlSize
{
[super setControlSize:aControlSize];
if ([self isBordered])
[self _sizeToControlSize];
}
#pragma mark -
// Setting the state // Setting the state
/*! /*!
Returns a Boolean value indicating whether the button allows a mixed state. Returns a Boolean value indicating whether the button allows a mixed state.
@return \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
@@ -240,20 +206,24 @@ CPButtonImageOffset = 3.0;
} }
/*! /*!
Sets the value of the button using an Objective-J object. Sets the value of the button using an Objective-J object.
@param anObjectValue The value of the button interpreted as an Objective-J object. @param anObjectValue The value of the button interpreted as an Objective-J object.
*/ */
- (void)setObjectValue:(id)anObjectValue - (void)setObjectValue:(id)anObjectValue
{ {
if (!anObjectValue || anObjectValue === @"" || ([anObjectValue intValue] === 0)) if (!anObjectValue || anObjectValue === @"" || ([anObjectValue intValue] === 0))
anObjectValue = CPOffState; anObjectValue = CPOffState;
else if (![anObjectValue isKindOfClass:[CPNumber class]]) else if (![anObjectValue isKindOfClass:[CPNumber class]])
anObjectValue = CPOnState; anObjectValue = CPOnState;
else if (anObjectValue >= CPOnState) else if (anObjectValue >= CPOnState)
anObjectValue = CPOnState; anObjectValue = CPOnState
else if (anObjectValue < CPOffState) else if (anObjectValue < CPOffState)
if ([self allowsMixedState]) if ([self allowsMixedState])
anObjectValue = CPMixedState; anObjectValue = CPMixedState;
else else
anObjectValue = CPOnState; anObjectValue = CPOnState;
@@ -261,35 +231,24 @@ CPButtonImageOffset = 3.0;
switch ([self objectValue]) switch ([self objectValue])
{ {
case CPMixedState: case CPMixedState: [self unsetThemeState:CPThemeStateSelected];
[self unsetThemeState:CPThemeStateSelected]; [self setThemeState:CPButtonStateMixed];
[self setThemeState:CPButtonStateMixed]; break;
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self setThemeState:CPThemeStateHighlighted];
else
[self unsetThemeState:CPThemeStateHighlighted];
break;
case CPOnState: case CPOnState: [self unsetThemeState:CPButtonStateMixed];
[self unsetThemeState:CPButtonStateMixed]; [self setThemeState:CPThemeStateSelected];
[self setThemeState:CPThemeStateSelected]; break;
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self setThemeState:CPThemeStateHighlighted];
else
[self unsetThemeState:CPThemeStateHighlighted];
break;
case CPOffState: case CPOffState: [self unsetThemeState:CPThemeStateSelected | CPButtonStateMixed];
[self unsetThemeStates:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]];
} }
} }
/*! /*!
Returns the button's next state. Returns the button's next state.
@return The button's state. A button can have two or three states. @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) If it has two, this value is either \c CPOffState (the normal or unpressed state)
or \c CPOnState (the alternate or pressed 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). 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
{ {
@@ -304,7 +263,7 @@ CPButtonImageOffset = 3.0;
} }
/*! /*!
Sets the button's state to the next available state. Sets the button's next state to \c aState.
@param aState Possible states are any of the CPButton globals: @param aState Possible states are any of the CPButton globals:
\c CPOffState, \c CPOnState, \c CPMixedState \c CPOffState, \c CPOnState, \c CPMixedState
*/ */
@@ -335,9 +294,9 @@ CPButtonImageOffset = 3.0;
} }
/*! /*!
Sets the title displayed by the button when in its normal state. 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 @param aTitle The string to set as the button's title. This title is always shown on buttons
that dont use their alternate contents when highlighting or displaying their alternate state. that dont use their alternate contents when highlighting or displaying their alternate state.
*/ */
- (void)setTitle:(CPString)aTitle - (void)setTitle:(CPString)aTitle
{ {
@@ -351,9 +310,9 @@ CPButtonImageOffset = 3.0;
} }
/*! /*!
Returns the title displayed on the button when its in its normal state. Returns the title displayed on the button when its in its normal state.
@return The title displayed on the receiver when its in its normal state @return The title displayed on the receiver when its in its normal state
or the empty string if the button doesnt display a title. or the empty string if the button doesnt display a title.
*/ */
- (CPString)title - (CPString)title
{ {
@@ -378,7 +337,7 @@ CPButtonImageOffset = 3.0;
- (void)setImage:(CPImage)anImage - (void)setImage:(CPImage)anImage
{ {
[self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateNormal]; [self setValue:anImage forThemeAttribute:@"image"];
} }
- (CPImage)image - (CPImage)image
@@ -415,19 +374,11 @@ CPButtonImageOffset = 3.0;
- (void)setShowsStateBy:(CPInteger)aMask - (void)setShowsStateBy:(CPInteger)aMask
{ {
// CPPushInCellMask cannot be set for showsStateBy.
aMask &= ~CPPushInCellMask;
if (_showsStateBy === aMask) if (_showsStateBy === aMask)
return; return;
_showsStateBy = aMask; _showsStateBy = aMask;
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask) && [self state] != CPOffState)
[self setThemeState:CPThemeStateHighlighted];
else
[self unsetThemeState:CPThemeStateHighlighted];
[self setNeedsDisplay:YES]; [self setNeedsDisplay:YES];
[self setNeedsLayout]; [self setNeedsLayout];
} }
@@ -451,56 +402,42 @@ CPButtonImageOffset = 3.0;
} }
} }
- (CPInteger)highlightsBy
{
return _highlightsBy;
}
- (void)setButtonType:(CPButtonType)aButtonType - (void)setButtonType:(CPButtonType)aButtonType
{ {
switch (aButtonType) switch (aButtonType)
{ {
case CPMomentaryLightButton: case CPMomentaryLightButton: [self setHighlightsBy:CPChangeBackgroundCellMask];
[self setHighlightsBy:CPChangeGrayCellMask | CPChangeBackgroundCellMask]; [self setShowsStateBy:CPNoCellMask];
[self setShowsStateBy:CPNoCellMask]; break;
break;
case CPMomentaryPushInButton: case CPMomentaryPushInButton: [self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask];
[self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask | CPChangeBackgroundCellMask]; [self setShowsStateBy:CPNoCellMask];
[self setShowsStateBy:CPNoCellMask]; break;
break;
case CPMomentaryChangeButton: case CPMomentaryChangeButton: [self setHighlightsBy:CPContentsCellMask];
[self setHighlightsBy:CPContentsCellMask]; [self setShowsStateBy:CPNoCellMask];
[self setShowsStateBy:CPNoCellMask]; break;
break;
case CPPushOnPushOffButton: case CPPushOnPushOffButton: [self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask];
[self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask | CPChangeBackgroundCellMask]; [self setShowsStateBy:CPChangeBackgroundCellMask];
[self setShowsStateBy:CPChangeBackgroundCellMask | CPChangeGrayCellMask]; break;
break;
case CPOnOffButton: case CPOnOffButton: [self setHighlightsBy:CPChangeBackgroundCellMask];
[self setHighlightsBy:CPChangeGrayCellMask | CPChangeBackgroundCellMask]; [self setShowsStateBy:CPChangeBackgroundCellMask];
[self setShowsStateBy:CPChangeGrayCellMask | CPChangeBackgroundCellMask]; break;
break;
case CPToggleButton: case CPToggleButton: [self setHighlightsBy:CPPushInCellMask | CPContentsCellMask];
[self setHighlightsBy:CPPushInCellMask | CPContentsCellMask]; [self setShowsStateBy:CPContentsCellMask];
[self setShowsStateBy:CPContentsCellMask]; break;
break;
case CPSwitchButton: case CPSwitchButton: [CPException raise:CPInvalidArgumentException
[CPException raise:CPInvalidArgumentException reason:"The CPSwitchButton type is not supported in Cappuccino, use the CPCheckBox class instead."];
reason:"The CPSwitchButton type is not supported in Cappuccino, use the CPCheckBox class instead."];
case CPRadioButton: case CPRadioButton: [CPException raise:CPInvalidArgumentException
[CPException raise:CPInvalidArgumentException reason:"The CPRadioButton type is not supported in Cappuccino, use the CPRadio class instead."];
reason:"The CPRadioButton type is not supported in Cappuccino, use the CPRadio class instead."];
default: default: [CPException raise:CPInvalidArgumentException
[CPException raise:CPInvalidArgumentException reason:"Unknown button type."];
reason:"Unknown button type."];
} }
[self setImageDimsWhenDisabled:YES]; [self setImageDimsWhenDisabled:YES];
@@ -557,45 +494,20 @@ CPButtonImageOffset = 3.0;
- (BOOL)startTrackingAt:(CGPoint)aPoint - (BOOL)startTrackingAt:(CGPoint)aPoint
{ {
_isTracking = YES; [self highlight:YES];
var startedTracking = [super startTrackingAt:aPoint]; return [super startTrackingAt:aPoint];
if (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))
{
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self highlight:[self state] == CPOffState];
else
[self highlight:YES];
}
else
{
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self highlight:[self state] != CPOffState];
else
[self highlight:NO];
}
return startedTracking;
} }
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
{ {
_isTracking = NO; [self highlight:NO];
[self invalidateTimers];
[super stopTracking:lastPoint at:aPoint mouseIsUp:mouseIsUp];
if (mouseIsUp && CGRectContainsPoint([self bounds], aPoint)) if (mouseIsUp && CGRectContainsPoint([self bounds], aPoint))
[self setNextState]; [self setNextState];
else
{
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self highlight:[self state] != CPOffState];
else
[self highlight:NO];
}
[self setNeedsLayout];
[self setNeedsDisplay:YES];
[self invalidateTimers];
} }
- (void)invalidateTimers - (void)invalidateTimers
@@ -617,18 +529,35 @@ CPButtonImageOffset = 3.0;
{ {
var contentInset = [self currentValueForThemeAttribute:@"content-inset"]; var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
return CGRectInsetByInset(bounds, contentInset); if (_CGInsetIsEmpty(contentInset))
return bounds;
bounds = _CGRectMakeCopy(bounds);
bounds.origin.x += contentInset.left;
bounds.origin.y += contentInset.top;
bounds.size.width -= contentInset.left + contentInset.right;
bounds.size.height -= contentInset.top + contentInset.bottom;
return bounds;
} }
- (CGRect)bezelRectForBounds:(CGRect)bounds - (CGRect)bezelRectForBounds:(CGRect)bounds
{ {
// Is this necessary? The theme itself can just change its inset to a zero inset when !CPThemeStateBordered.
if (![self isBordered]) if (![self isBordered])
return bounds; return _CGRectMakeZero();
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"]; var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
return CGRectInsetByInset(bounds, bezelInset); if (_CGInsetIsEmpty(bezelInset))
return bounds;
bounds = _CGRectMakeCopy(bounds);
bounds.origin.x += bezelInset.left;
bounds.origin.y += bezelInset.top;
bounds.size.width -= bezelInset.left + bezelInset.right;
bounds.size.height -= bezelInset.top + bezelInset.bottom;
return bounds;
} }
- (CGSize)_minimumFrameSize - (CGSize)_minimumFrameSize
@@ -688,14 +617,14 @@ CPButtonImageOffset = 3.0;
{ {
if (aName === "bezel-view") if (aName === "bezel-view")
{ {
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()]; var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[view setHitTests:NO]; [view setHitTests:NO];
return view; return view;
} }
else else
return [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()]; return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
} }
- (void)layoutSubviews - (void)layoutSubviews
@@ -712,51 +641,8 @@ CPButtonImageOffset = 3.0;
if (contentView) if (contentView)
{ {
var title = nil, [contentView setText:([self hasThemeState:CPThemeStateHighlighted] && _alternateTitle) ? _alternateTitle : _title];
image = nil; [contentView setImage:[self currentValueForThemeAttribute:@"image"]];
if (_isTracking)
{
if (_highlightsBy & CPContentsCellMask)
{
if (_showsStateBy & CPContentsCellMask)
{
title = ([self state] == CPOffState && _alternateTitle) ? _alternateTitle : _title;
image = ([self state] == CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
}
else
{
title = [self alternateTitle];
image = [self alternateImage];
}
}
else if (_showsStateBy & CPContentsCellMask)
{
title = ([self state] != CPOffState && _alternateTitle) ? _alternateTitle : _title;
image = ([self state] != CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
}
else
{
title = _title;
image = [self image];
}
}
else
{
if (_showsStateBy & CPContentsCellMask)
{
title = ([self state] != CPOffState && _alternateTitle) ? _alternateTitle : _title;
image = ([self state] != CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
}
else
{
title = _title;
image = [self image];
}
}
[contentView setText:title];
[contentView setImage:image];
[contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]]; [contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]];
[contentView setFont:[self currentValueForThemeAttribute:@"font"]]; [contentView setFont:[self currentValueForThemeAttribute:@"font"]];
@@ -855,51 +741,9 @@ CPButtonImageOffset = 3.0;
return NO; return NO;
[self performClick:nil]; [self performClick:nil];
return YES; return YES;
} }
/*!
Perform a click on the receiver.
@param sender - The sender object
*/
- (void)performClick:(id)sender
{
// This is slightly different from [super performClick:] in that the highlight behaviour is dependent on
// highlightsBy and showsStateBy.
if (![self isEnabled])
return;
[self setState:[self nextState]];
var shouldHighlight = NO;
if (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))
{
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
shouldHighlight = [self state] == CPOffState;
else
shouldHighlight = YES;
}
[self highlight:shouldHighlight];
try
{
[self sendAction:[self action] to:[self target]];
}
catch (e)
{
throw e;
}
finally
{
if (shouldHighlight)
[CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unhighlightButtonTimerDidFinish:) userInfo:nil repeats:NO];
}
}
@end @end
@implementation CPButton (NS) @implementation CPButton (NS)
@@ -940,9 +784,7 @@ var CPButtonImageKey = @"CPButtonImageKey",
CPButtonKeyEquivalentKey = @"CPButtonKeyEquivalentKey", CPButtonKeyEquivalentKey = @"CPButtonKeyEquivalentKey",
CPButtonKeyEquivalentMaskKey = @"CPButtonKeyEquivalentMaskKey", CPButtonKeyEquivalentMaskKey = @"CPButtonKeyEquivalentMaskKey",
CPButtonPeriodicDelayKey = @"CPButtonPeriodicDelayKey", CPButtonPeriodicDelayKey = @"CPButtonPeriodicDelayKey",
CPButtonPeriodicIntervalKey = @"CPButtonPeriodicIntervalKey", CPButtonPeriodicIntervalKey = @"CPButtonPeriodicIntervalKey";
CPButtonHighlightsByKey = @"CPButtonHighlightsByKey",
CPButtonShowsStateByKey = @"CPButtonShowsStateByKey";
@implementation CPButton (CPCoding) @implementation CPButton (CPCoding)
@@ -960,20 +802,9 @@ var CPButtonImageKey = @"CPButtonImageKey",
_title = [aCoder decodeObjectForKey:CPButtonTitleKey]; _title = [aCoder decodeObjectForKey:CPButtonTitleKey];
_alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey]; _alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey];
_allowsMixedState = [aCoder decodeBoolForKey:CPButtonAllowsMixedStateKey];
if ([aCoder containsValueForKey:CPButtonHighlightsByKey]) if ([aCoder containsValueForKey:CPButtonAllowsMixedStateKey])
{ _allowsMixedState = [aCoder decodeBoolForKey:CPButtonAllowsMixedStateKey];
// If one exists, assume both do.
_highlightsBy = [aCoder decodeIntForKey:CPButtonHighlightsByKey];
_showsStateBy = [aCoder decodeIntForKey:CPButtonShowsStateByKey];
}
else
{
// Backwards compatibility: if this CPButton was encoded before coding of
// highlightsBy and showsStateBy were added, we should just use the
// default values from _init rather than overwriting with 0, 0.
}
[self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]]; [self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]];
@@ -1012,9 +843,6 @@ var CPButtonImageKey = @"CPButtonImageKey",
[aCoder encodeBool:_allowsMixedState forKey:CPButtonAllowsMixedStateKey]; [aCoder encodeBool:_allowsMixedState forKey:CPButtonAllowsMixedStateKey];
[aCoder encodeInt:_highlightsBy forKey:CPButtonHighlightsByKey];
[aCoder encodeInt:_showsStateBy forKey:CPButtonShowsStateByKey];
[aCoder encodeBool:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey]; [aCoder encodeBool:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey];
[aCoder encodeInt:[self imagePosition] forKey:CPButtonImagePositionKey]; [aCoder encodeInt:[self imagePosition] forKey:CPButtonImagePositionKey];
@@ -1028,3 +856,6 @@ var CPButtonImageKey = @"CPButtonImageKey",
} }
@end @end
@import "CPCheckBox.j"
@import "CPRadio.j"
+15 -80
View File
@@ -1,35 +1,6 @@
/*
* CPButtonBar.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPView.j" @import "CPView.j"
@import "CPWindow_Constants.j"
@class CPSplitView
@global CPPopUpButtonStatePullsDown
@global CPKeyValueChangeOldKey
@global CPKeyValueChangeNewKey
@global CPKeyValueObservingOptionNew
@global CPKeyValueObservingOptionOld
@implementation CPButtonBar : CPView @implementation CPButtonBar : CPView
{ {
@@ -41,7 +12,7 @@
+ (id)plusButton + (id)plusButton
{ {
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)], var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-plus" forClass:[CPButtonBar class]]; image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"plus_button.png"] size:CGSizeMake(11, 12)];
[button setBordered:NO]; [button setBordered:NO];
[button setImage:image]; [button setImage:image];
@@ -53,7 +24,7 @@
+ (id)minusButton + (id)minusButton
{ {
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)], var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-minus" forClass:[CPButtonBar class]]; image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"minus_button.png"] size:CGSizeMake(11, 4)];
[button setBordered:NO]; [button setBordered:NO];
[button setImage:image]; [button setImage:image];
@@ -65,7 +36,7 @@
+ (id)actionPopupButton + (id)actionPopupButton
{ {
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)], var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-action" forClass:[CPButtonBar class]]; image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"action_button.png"] size:CGSizeMake(22, 14)];
[button addItemWithTitle:nil]; [button addItemWithTitle:nil];
[[button lastItem] setImage:image]; [[button lastItem] setImage:image];
@@ -82,19 +53,10 @@
return @"button-bar"; return @"button-bar";
} }
+ (CPDictionary)themeAttributes + (id)themeAttributes
{ {
return @{ return [CPDictionary dictionaryWithObjects:[CGInsetMake(0.0, 0.0, 0.0, 0.0), CGSizeMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null]]
@"resize-control-inset": CGInsetMake(0.0, 0.0, 0.0, 0.0), forKeys:[@"resize-control-inset", @"resize-control-size", @"resize-control-color", @"bezel-color", @"button-bezel-color", @"button-text-color"]];
@"resize-control-size": CGSizeMakeZero(),
@"resize-control-color": [CPNull null],
@"bezel-color": [CPNull null],
@"button-bezel-color": [CPNull null],
@"button-text-color": [CPNull null],
@"button-image-plus": [CPNull null],
@"button-image-minus": [CPNull null],
@"button-image-action": [CPNull null],
};
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
@@ -112,8 +74,6 @@
- (void)awakeFromCib - (void)awakeFromCib
{ {
[super awakeFromCib];
var view = [self superview], var view = [self superview],
subview = self; subview = self;
@@ -134,20 +94,10 @@
- (void)setButtons:(CPArray)buttons - (void)setButtons:(CPArray)buttons
{ {
for (var i = [_buttons count] - 1; i >= 0; i--)
{
[_buttons[i] removeFromSuperview];
[_buttons[i] removeObserver:self forKeyPath:@"hidden"];
}
_buttons = [CPArray arrayWithArray:buttons]; _buttons = [CPArray arrayWithArray:buttons];
for (var i = [_buttons count] - 1; i >= 0; i--) for (var i = 0, count = [_buttons count]; i < count; i++)
{
[_buttons[i] addObserver:self forKeyPath:@"hidden" options:CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld context:nil];
[_buttons[i] setBordered:YES]; [_buttons[i] setBordered:YES];
}
[self setNeedsLayout]; [self setNeedsLayout];
} }
@@ -233,15 +183,8 @@
count = [buttonsNotHidden count]; count = [buttonsNotHidden count];
while (count--) while (count--)
{ if ([buttonsNotHidden[count] isHidden])
var button = buttonsNotHidden[count]; [buttonsNotHidden removeObject:buttonsNotHidden[count]];
if ([button isHidden])
{
[button removeFromSuperview];
[buttonsNotHidden removeObject:button];
}
}
var currentButtonOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX([self bounds]) + 1 : -1, var currentButtonOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX([self bounds]) + 1 : -1,
bounds = [self bounds], bounds = [self bounds],
@@ -272,15 +215,15 @@
currentButtonOffset += width - 1; currentButtonOffset += width - 1;
} }
[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered]]; [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal | CPThemeStateBordered];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, ]]; [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted | CPThemeStateBordered];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered]]; [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled | CPThemeStateBordered];
[button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered]; [button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
// FIXME shouldn't need this // FIXME shouldn't need this
[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]]; [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]]; [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]]; [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
[self addSubview:button]; [self addSubview:button];
} }
@@ -296,14 +239,6 @@
} }
} }
- (void)observeValueForKeyPath:(CPString)keyPath ofObject:(id)object change:(CPDictionary)change context:(id)context
{
if ([change objectForKey:CPKeyValueChangeOldKey] == [change objectForKey:CPKeyValueChangeNewKey])
return;
[self setNeedsLayout];
}
- (void)setFrameSize:(CGSize)aSize - (void)setFrameSize:(CGSize)aSize
{ {
[super setFrameSize:aSize]; [super setFrameSize:aSize];
+41 -59
View File
@@ -44,12 +44,12 @@ CPCheckBoxImageOffset = 4.0;
return @"check-box"; return @"check-box";
} }
+ (Class)_binderClassForBinding:(CPString)aBinding + (Class)_binderClassForBinding:(CPString)theBinding
{ {
if (aBinding === CPValueBinding) if (theBinding === CPValueBinding)
return [_CPCheckBoxValueBinder class]; return [_CPCheckBoxValueBinder class];
return [super _binderClassForBinding:aBinding]; return [super _binderClassForBinding:theBinding];
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
@@ -94,72 +94,54 @@ CPCheckBoxImageOffset = 4.0;
[self takeStateFromKeyPath:aKeyPath ofObjects:objects]; [self takeStateFromKeyPath:aKeyPath ofObjects:objects];
} }
- (CPImage)image
{
return [self currentValueForThemeAttribute:@"image"];
}
- (CPImage)alternateImage
{
return [self currentValueForThemeAttribute:@"image"];
}
- (BOOL)startTrackingAt:(CGPoint)aPoint
{
var startedTracking = [super startTrackingAt:aPoint];
[self highlight:YES];
return startedTracking;
}
#pragma mark -
#pragma mark Override methods from CPButton
- (CGSize)_minimumFrameSize
{
var size = [super _minimumFrameSize],
contentView = [self ephemeralSubviewNamed:@"content-view"];
if (!contentView && [[self title] length])
{
var minSize = [self currentValueForThemeAttribute:@"min-size"],
maxSize = [self currentValueForThemeAttribute:@"max-size"];
// Here we always add the min size to the control which is the size of the view of the checkBox
size.width += minSize.width + CPCheckBoxImageOffset;
if (maxSize.width >= 0.0)
size.width = MIN(size.width, maxSize.width);
}
return size;
}
@end @end
@implementation _CPCheckBoxValueBinder : CPBinder @implementation _CPCheckBoxValueBinder : CPBinder
{ {
} }
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options - (void)setValueFor:(CPString)theBinding
{ {
[super _updatePlaceholdersWithOptions:options]; var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey],
options = [_info objectForKey:CPOptionsKey],
newValue = [destination valueForKeyPath:keyPath],
isPlaceholder = CPIsControllerMarker(newValue);
[self _setPlaceholder:CPMixedState forMarker:CPMultipleValuesMarker isDefault:YES]; if (isPlaceholder)
[self _setPlaceholder:CPOffState forMarker:CPNoSelectionMarker isDefault:YES]; {
[self _setPlaceholder:CPOffState forMarker:CPNotApplicableMarker isDefault:YES]; switch (newValue)
[self _setPlaceholder:CPOffState forMarker:CPNullMarker isDefault:YES]; {
} case CPMultipleValuesMarker:
newValue = CPMixedState;
break;
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding case CPNoSelectionMarker:
{ newValue = CPOffState;
[_source setAllowsMixedState:(aValue === CPMixedState)]; break;
[_source setState:aValue];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding case CPNotApplicableMarker:
{ if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
[_source setState:aValue]; [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 @end
+36 -15
View File
@@ -22,7 +22,6 @@
@import "CPView.j" @import "CPView.j"
@class CPScrollView
/*! /*!
@ingroup appkit @ingroup appkit
@@ -45,13 +44,44 @@
if (_documentView == aView) if (_documentView == aView)
return; return;
var defaultCenter = [CPNotificationCenter defaultCenter];
if (_documentView) if (_documentView)
{
[defaultCenter
removeObserver:self
name:CPViewFrameDidChangeNotification
object:_documentView];
[defaultCenter
removeObserver:self
name:CPViewBoundsDidChangeNotification
object:_documentView];
[_documentView removeFromSuperview]; [_documentView removeFromSuperview];
}
_documentView = aView; _documentView = aView;
if (_documentView) if (_documentView)
{
[self addSubview:_documentView]; [self addSubview:_documentView];
[_documentView setPostsFrameChangedNotifications:YES];
[_documentView setPostsBoundsChangedNotifications:YES];
[defaultCenter
addObserver:self
selector:@selector(viewFrameChanged:)
name:CPViewFrameDidChangeNotification
object:_documentView];
[defaultCenter
addObserver:self
selector:@selector(viewBoundsChanged:)
name:CPViewBoundsDidChangeNotification
object:_documentView];
}
} }
/*! /*!
@@ -71,19 +101,19 @@
- (CGPoint)constrainScrollPoint:(CGPoint)aPoint - (CGPoint)constrainScrollPoint:(CGPoint)aPoint
{ {
if (!_documentView) if (!_documentView)
return CGPointMakeZero(); return _CGPointMakeZero();
var documentFrame = [_documentView frame]; var documentFrame = [_documentView frame];
aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(CGRectGetWidth(documentFrame) - CGRectGetWidth(_bounds), 0.0))); aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(_bounds), 0.0)));
aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(CGRectGetHeight(documentFrame) - CGRectGetHeight(_bounds), 0.0))); aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(_CGRectGetHeight(documentFrame) - _CGRectGetHeight(_bounds), 0.0)));
return aPoint; return aPoint;
} }
- (void)setBoundsOrigin:(CGPoint)aPoint - (void)setBoundsOrigin:(CGPoint)aPoint
{ {
if (CGPointEqualToPoint(_bounds.origin, aPoint)) if (_CGPointEqualToPoint(_bounds.origin, aPoint))
return; return;
[super setBoundsOrigin:aPoint]; [super setBoundsOrigin:aPoint];
@@ -193,11 +223,6 @@
return [self scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)]; return [self scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
} }
- (CGRect)documentVisibleRect
{
return [_documentView visibleRect];
}
@end @end
@@ -208,11 +233,7 @@ var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
- (id)initWithCoder:(CPCoder)aCoder - (id)initWithCoder:(CPCoder)aCoder
{ {
if (self = [super initWithCoder:aCoder]) if (self = [super initWithCoder:aCoder])
{ [self setDocumentView:[aCoder decodeObjectForKey:CPClipViewDocumentViewKey]];
// Don't call setDocumentView: here. It calls addSubview:, but it's A) not necessary since the
// view hierarchy is fully encoded and B) dangerous if the subview is not fully decoded.
_documentView = [aCoder decodeObjectForKey:CPClipViewDocumentViewKey];
}
return self; return self;
} }
+271 -842
View File
File diff suppressed because it is too large Load Diff
+1 -26
View File
@@ -22,38 +22,13 @@
@import "CPViewController.j" @import "CPViewController.j"
/*! /*!
Represents an object inside a CPCollectionView. Represents an object inside a CPCollectionView.
*/ */
@implementation CPCollectionViewItem : CPViewController @implementation CPCollectionViewItem : CPViewController
{ {
BOOL _isSelected; BOOL _isSelected;
CPData _cachedArchive;
}
- (id)copy
{
var cibName = [self cibName],
copy;
if (cibName)
{
copy = [[[self class] alloc] initWithCibName:cibName bundle:[self cibBundle]];
}
else
{
if (!_cachedArchive)
_cachedArchive = [CPKeyedArchiver archivedDataWithRootObject:self];
copy = [CPKeyedUnarchiver unarchiveObjectWithData:_cachedArchive];
// copy connections
}
[copy setRepresentedObject:[self representedObject]];
[copy setSelected:_isSelected];
return copy;
} }
// Setting the Represented Object // Setting the Represented Object
+44 -1022
View File
File diff suppressed because it is too large Load Diff
+67 -73
View File
@@ -20,35 +20,12 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "CPApplication.j"
@import "CPButton.j" @import "CPButton.j"
@import "CPCookie.j" @import "CPCookie.j"
@import "CPPanel.j" @import "CPPanel.j"
@import "CPPasteboard.j"
@import "CPView.j" @import "CPView.j"
@class CPSlider
@class _CPColorPanelToolbar
@class _CPColorPanelSwatches
@class _CPColorPanelPreview
@global CPApp
/*
A color wheel
@global
@group CPColorPanelMode
*/
CPWheelColorPickerMode = 1;
/*
Slider based picker
@global
@group CPColorPanelMode
*/
CPSliderColorPickerMode = 2;
CPColorPickerViewWidth = 265;
CPColorPickerViewHeight = 370;
CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification"; CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification";
@@ -61,6 +38,22 @@ var PREVIEW_HEIGHT = 20.0,
var SharedColorPanel = nil, var SharedColorPanel = nil,
ColorPickerClasses = []; ColorPickerClasses = [];
/*
A color wheel
@global
@group CPColorPanelMode
*/
CPWheelColorPickerMode = 1;
/*
Slider based picker
@global
@group CPColorPanelMode
*/
CPSliderColorPickerMode = 2;
CPColorPickerViewWidth = 265,
CPColorPickerViewHeight = 370;
/*! /*!
@ingroup appkit @ingroup appkit
@class CPColorPanel @class CPColorPanel
@@ -71,6 +64,7 @@ var SharedColorPanel = nil,
*/ */
@implementation CPColorPanel : CPPanel @implementation CPColorPanel : CPPanel
{ {
_CPColorPanelToolbar _toolbar;
_CPColorPanelSwatches _swatchView; _CPColorPanelSwatches _swatchView;
_CPColorPanelPreview _previewView; _CPColorPanelPreview _previewView;
@@ -129,7 +123,7 @@ var SharedColorPanel = nil,
if (self) if (self)
{ {
//[[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]]; [[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]];
[self setTitle:@"Color Panel"]; [self setTitle:@"Color Panel"];
[self setLevel:CPFloatingWindowLevel]; [self setLevel:CPFloatingWindowLevel];
@@ -150,7 +144,7 @@ var SharedColorPanel = nil,
- (void)setColor:(CPColor)aColor - (void)setColor:(CPColor)aColor
{ {
_color = aColor; _color = aColor;
[_previewView setBackgroundColor:_color]; [_previewView setBackgroundColor: _color];
[CPApp sendAction:@selector(changeColor:) to:nil from:self]; [CPApp sendAction:@selector(changeColor:) to:nil from:self];
@@ -254,8 +248,8 @@ var SharedColorPanel = nil,
var height = (TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5 + SWATCH_HEIGHT + 32), var height = (TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5 + SWATCH_HEIGHT + 32),
bounds = [[self contentView] bounds]; bounds = [[self contentView] bounds];
[view setFrameSize:CGSizeMake(bounds.size.width - 10, bounds.size.height - height)]; [view setFrameSize:CPSizeMake(bounds.size.width - 10, bounds.size.height - height)];
[view setFrameOrigin:CGPointMake(5, height)]; [view setFrameOrigin:CPPointMake(5, height)];
} }
[_currentView removeFromSuperview]; [_currentView removeFromSuperview];
@@ -305,7 +299,7 @@ var SharedColorPanel = nil,
bounds = [contentView bounds]; bounds = [contentView bounds];
_toolbar = [[CPView alloc] initWithFrame:CGRectMake(0, 6, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)]; _toolbar = [[CPView alloc] initWithFrame:CGRectMake(0, 6, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)];
[_toolbar setAutoresizingMask:CPViewWidthSizable]; [_toolbar setAutoresizingMask: CPViewWidthSizable];
var totalToolbarWidth = count * ICON_WIDTH + (count - 1) * ICON_PADDING, var totalToolbarWidth = count * ICON_WIDTH + (count - 1) * ICON_PADDING,
leftOffset = (CGRectGetWidth(bounds) - totalToolbarWidth) / 2.0, leftOffset = (CGRectGetWidth(bounds) - totalToolbarWidth) / 2.0,
@@ -345,7 +339,7 @@ var SharedColorPanel = nil,
[previewBox addSubview:_previewView]; [previewBox addSubview:_previewView];
var _previewLabel = [[CPTextField alloc] initWithFrame:CGRectMake(10, TOOLBAR_HEIGHT + 10, 60, 15)]; var _previewLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 10, 60, 15)];
[_previewLabel setStringValue:"Preview:"]; [_previewLabel setStringValue:"Preview:"];
[_previewLabel setTextColor:[CPColor blackColor]]; [_previewLabel setTextColor:[CPColor blackColor]];
[_previewLabel setAlignment:CPRightTextAlignment]; [_previewLabel setAlignment:CPRightTextAlignment];
@@ -363,14 +357,14 @@ var SharedColorPanel = nil,
[swatchBox addSubview:_swatchView]; [swatchBox addSubview:_swatchView];
var _swatchLabel = [[CPTextField alloc] initWithFrame:CGRectMake(10, TOOLBAR_HEIGHT + 8 + PREVIEW_HEIGHT + 6, 60, 15)]; var _swatchLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 8 + PREVIEW_HEIGHT + 6, 60, 15)];
[_swatchLabel setStringValue:"Swatches:"]; [_swatchLabel setStringValue: "Swatches:"];
[_swatchLabel setTextColor:[CPColor blackColor]]; [_swatchLabel setTextColor:[CPColor blackColor]];
[_swatchLabel setAlignment:CPRightTextAlignment]; [_swatchLabel setAlignment:CPRightTextAlignment];
var opacityLabel = [[CPTextField alloc] initWithFrame:CGRectMake(10, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 35, 60, 20)]; var opacityLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 35, 60, 20)];
[opacityLabel setStringValue:"Opacity:"]; [opacityLabel setStringValue: "Opacity:"];
[opacityLabel setTextColor:[CPColor blackColor]]; [opacityLabel setTextColor:[CPColor blackColor]];
[opacityLabel setAlignment:CPRightTextAlignment]; [opacityLabel setAlignment:CPRightTextAlignment];
@@ -395,7 +389,7 @@ var SharedColorPanel = nil,
_action = nil; _action = nil;
_activePicker = nil; _activePicker = nil;
[_previewView setBackgroundColor:_color]; [_previewView setBackgroundColor: _color];
if (buttonForLater) if (buttonForLater)
[self _setPicker:buttonForLater]; [self _setPicker:buttonForLater];
@@ -413,29 +407,28 @@ var SharedColorPanel = nil,
CPColorDragType = "CPColorDragType"; CPColorDragType = "CPColorDragType";
var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie"; var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
/* @ignore */ /* @ignore */
@implementation _CPColorPanelSwatches : CPView @implementation _CPColorPanelSwatches : CPView
{ {
CPArray _swatches; CPView[] _swatches;
CPColor _dragColor; CPColor _dragColor;
CPColorPanel _colorPanel; CPColorPanel _colorPanel;
CPCookie _swatchCookie; CPCookie _swatchCookie;
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CPRect)aFrame
{ {
self = [super initWithFrame:aFrame]; self = [super initWithFrame:aFrame];
[self setBackgroundColor:[CPColor grayColor]]; [self setBackgroundColor: [CPColor grayColor]];
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]]; [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
var whiteColor = [CPColor whiteColor]; var whiteColor = [CPColor whiteColor];
_swatchCookie = [[CPCookie alloc] initWithName:CPColorPanelSwatchesCookie]; _swatchCookie = [[CPCookie alloc] initWithName: CPColorPanelSwatchesCookie];
var colorList = [self startingColorList]; var colorList = [self startingColorList];
_swatches = []; _swatches = [];
@@ -443,15 +436,15 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
for (var i = 0; i < 50; i++) for (var i = 0; i < 50; i++)
{ {
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
var view = [[CPView alloc] initWithFrame:CGRectMake(13 * i + 1, 1, 12, 12)], var view = [[CPView alloc] initWithFrame: CPRectMake(13 * i + 1, 1, 12, 12)],
fillView = [[CPView alloc] initWithFrame:CGRectInset([view bounds], 1.0, 1.0)]; fillView = [[CPView alloc] initWithFrame:CGRectInset([view bounds], 1.0, 1.0)];
[view setBackgroundColor:whiteColor]; [view setBackgroundColor:whiteColor];
[fillView setBackgroundColor:(i < colorList.length) ? colorList[i] : whiteColor]; [fillView setBackgroundColor: (i < colorList.length) ? colorList[i] : whiteColor];
[view addSubview:fillView]; [view addSubview:fillView];
[self addSubview:view]; [self addSubview: view];
_swatches.push(view); _swatches.push(view);
} }
@@ -468,7 +461,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
{ {
var cookieValue = [_swatchCookie value]; var cookieValue = [_swatchCookie value];
if (!cookieValue) if (cookieValue == "")
{ {
return [ return [
[CPColor blackColor], [CPColor blackColor],
@@ -483,12 +476,13 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
]; ];
} }
var cookieValue = eval(cookieValue); var cookieValue = eval(cookieValue),
result = [];
return [cookieValue arrayByApplyingBlock:function(value) for (var i = 0; i < cookieValue.length; i++)
{ result.push([CPColor colorWithHexString: cookieValue[i]]);
return [CPColor colorWithHexString:value];
}]; return result;
} }
- (CPArray)saveColorList - (CPArray)saveColorList
@@ -501,7 +495,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
var future = new Date(); var future = new Date();
future.setYear(2019); future.setYear(2019);
[_swatchCookie setValue:JSON.stringify(result) expires:future domain:nil]; [_swatchCookie setValue: JSON.stringify(result) expires:future domain: nil];
} }
- (void)setColorPanel:(CPColorPanel)panel - (void)setColorPanel:(CPColorPanel)panel
@@ -534,7 +528,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
if (!CGRectContainsPoint(bounds, point) || point.x > [self bounds].size.width - 1 || point.x < 1) if (!CGRectContainsPoint(bounds, point) || point.x > [self bounds].size.width - 1 || point.x < 1)
return NO; return NO;
[_colorPanel setColor:[self colorAtIndex:FLOOR(point.x / 13)] updatePicker:YES]; [_colorPanel setColor: [self colorAtIndex:FLOOR(point.x / 13)] updatePicker: YES];
} }
- (void)mouseDragged:(CPEvent)anEvent - (void)mouseDragged:(CPEvent)anEvent
@@ -551,10 +545,10 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
_dragColor = [[swatch subviews][0] backgroundColor]; _dragColor = [[swatch subviews][0] backgroundColor];
var bounds = CGRectMakeCopy([swatch bounds]); var bounds = CPRectCreateCopy([swatch bounds]);
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
var dragView = [[CPView alloc] initWithFrame:bounds], var dragView = [[CPView alloc] initWithFrame: bounds],
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)]; dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
[dragView setBackgroundColor:[CPColor blackColor]]; [dragView setBackgroundColor:[CPColor blackColor]];
@@ -562,13 +556,13 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
[dragView addSubview:dragFillView]; [dragView addSubview:dragFillView];
[self dragView:dragView [self dragView: dragView
at:CGPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
offset:CGPointMake(0.0, 0.0) offset: CPPointMake(0.0, 0.0)
event:anEvent event: anEvent
pasteboard:nil pasteboard: nil
source:self source: self
slideBack:YES]; slideBack: YES];
} }
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
@@ -577,7 +571,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:aType]; [aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:aType];
} }
- (void)performDragOperation:(id /*<CPDraggingInfo>*/)aSender - (void)performDragOperation:(id <CPDraggingInfo>)aSender
{ {
var location = [self convertPoint:[aSender draggingLocation] fromView:nil], var location = [self convertPoint:[aSender draggingLocation] fromView:nil],
pasteboard = [aSender draggingPasteboard], pasteboard = [aSender draggingPasteboard],
@@ -586,7 +580,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
if (![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1) if (![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1)
return NO; return NO;
[self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex:FLOOR(location.x / 13)]; [self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex: FLOOR(location.x / 13)];
} }
@end @end
@@ -597,7 +591,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
CPColorPanel _colorPanel; CPColorPanel _colorPanel;
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CPRect)aFrame
{ {
self = [super initWithFrame:aFrame]; self = [super initWithFrame:aFrame];
@@ -616,7 +610,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
return _colorPanel; return _colorPanel;
} }
- (void)performDragOperation:(id /*<CPDraggingInfo>*/)aSender - (void)performDragOperation:(id <CPDraggingInfo>)aSender
{ {
var pasteboard = [aSender draggingPasteboard]; var pasteboard = [aSender draggingPasteboard];
@@ -638,10 +632,10 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self]; [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
var bounds = CGRectMake(0, 0, 15, 15); var bounds = CPRectMake(0, 0, 15, 15);
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
var dragView = [[CPView alloc] initWithFrame:bounds], var dragView = [[CPView alloc] initWithFrame: bounds],
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)]; dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
[dragView setBackgroundColor:[CPColor blackColor]]; [dragView setBackgroundColor:[CPColor blackColor]];
@@ -649,13 +643,13 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
[dragView addSubview:dragFillView]; [dragView addSubview:dragFillView];
[self dragView:dragView [self dragView: dragView
at:CGPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
offset:CGPointMake(0.0, 0.0) offset: CPPointMake(0.0, 0.0)
event:anEvent event: anEvent
pasteboard:nil pasteboard: nil
source:self source: self
slideBack:YES]; slideBack: YES];
} }
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
+47 -60
View File
@@ -24,14 +24,6 @@
@import "CPView.j" @import "CPView.j"
@class CPSlider
@class CPColorPanel
@class __CPColorWheel
@global CPColorPickerViewWidth
@global CPColorPickerViewHeight
@global CPWheelColorPickerMode
/*! /*!
@ingroup appkit @ingroup appkit
@@ -52,11 +44,10 @@
*/ */
- (id)initWithPickerMask:(int)aMask colorPanel:(CPColorPanel)aPanel - (id)initWithPickerMask:(int)aMask colorPanel:(CPColorPanel)aPanel
{ {
if (self = [super init]) self = [super init];
{
_panel = aPanel; _panel = aPanel;
_mask = aMask; _mask = aMask;
}
return self; return self;
} }
@@ -85,6 +76,7 @@
*/ */
- (void)setMode:(CPColorPanelMode)mode - (void)setMode:(CPColorPanelMode)mode
{ {
return;
} }
/*! /*!
@@ -93,6 +85,7 @@
*/ */
- (void)setColor:(CPColor)aColor - (void)setColor:(CPColor)aColor
{ {
return;
} }
@end @end
@@ -117,7 +110,7 @@
- (id)initView - (id)initView
{ {
var aFrame = CGRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight); aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
_pickerView = [[CPView alloc] initWithFrame:aFrame]; _pickerView = [[CPView alloc] initWithFrame:aFrame];
[_pickerView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; [_pickerView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
@@ -125,8 +118,7 @@
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)]; _brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)];
[_brightnessSlider setValue:15.0 forThemeAttribute:@"track-width"]; [_brightnessSlider setValue:15.0 forThemeAttribute:@"track-width"];
var brightnessImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPColorPicker class]] pathForResource:@"brightness_bar.png"] size:CGSizeMake(272, 20)]; [_brightnessSlider setValue:[CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPColorPicker class]] pathForResource:@"brightness_bar.png"]]] forThemeAttribute:@"track-color"];
[_brightnessSlider setValue:[CPColor colorWithPatternImage:brightnessImage] forThemeAttribute:@"track-color"];
[_brightnessSlider setMinValue:0.0]; [_brightnessSlider setMinValue:0.0];
[_brightnessSlider setMaxValue:100.0]; [_brightnessSlider setMaxValue:100.0];
@@ -136,9 +128,9 @@
[_brightnessSlider setAction:@selector(brightnessSliderDidChange:)]; [_brightnessSlider setAction:@selector(brightnessSliderDidChange:)];
[_brightnessSlider setAutoresizingMask:CPViewWidthSizable | CPViewMinYMargin]; [_brightnessSlider setAutoresizingMask:CPViewWidthSizable | CPViewMinYMargin];
_hueSaturationView = [[__CPColorWheel alloc] initWithFrame:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height - 38)]; _hueSaturationView = [[__CPColorWheel alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, aFrame.size.height - 38)];
[_hueSaturationView setDelegate:self]; [_hueSaturationView setDelegate: self];
[_hueSaturationView setAutoresizingMask:(CPViewWidthSizable | CPViewHeightSizable)]; [_hueSaturationView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)];
[_pickerView addSubview:_hueSaturationView]; [_pickerView addSubview:_hueSaturationView];
[_pickerView addSubview:_brightnessSlider]; [_pickerView addSubview:_brightnessSlider];
@@ -161,12 +153,12 @@
brightness = [_brightnessSlider floatValue]; brightness = [_brightnessSlider floatValue];
[_hueSaturationView setWheelBrightness:brightness / 100.0]; [_hueSaturationView setWheelBrightness:brightness / 100.0];
[_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hue / 360.0 saturation:saturation / 100.0 brightness:1]]; [_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hue saturation:saturation brightness:100]];
var colorPanel = [self colorPanel], var colorPanel = [self colorPanel],
opacity = [colorPanel opacity]; opacity = [colorPanel opacity];
_cachedColor = [CPColor colorWithHue:hue / 360.0 saturation:saturation / 100.0 brightness:brightness / 100.0 alpha:opacity]; _cachedColor = [CPColor colorWithHue:hue saturation:saturation brightness:brightness alpha:opacity];
[[self colorPanel] setColor:_cachedColor]; [[self colorPanel] setColor:_cachedColor];
} }
@@ -197,10 +189,10 @@
var hsb = [newColor hsbComponents]; var hsb = [newColor hsbComponents];
[_hueSaturationView setPositionToColor:newColor]; [_hueSaturationView setPositionToColor:newColor];
[_brightnessSlider setFloatValue:hsb[2] * 100.0]; [_brightnessSlider setFloatValue:hsb[2]];
[_hueSaturationView setWheelBrightness:hsb[2]]; [_hueSaturationView setWheelBrightness:hsb[2] / 100.0];
[_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hsb[0] saturation:hsb[1] brightness:1]]; [_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hsb[0] saturation:hsb[1] brightness:100]];
} }
- (CPImage)provideNewButtonImage - (CPImage)provideNewButtonImage
@@ -231,64 +223,60 @@
float _radius; float _radius;
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CPRect)aFrame
{ {
if (self = [super initWithFrame:aFrame]) self = [super initWithFrame:aFrame];
{
var path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel.png"];
_wheelImage = new Image();
_wheelImage.src = path;
_wheelImage.style.position = "absolute";
path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel_black.png"];
_blackWheelImage = new Image();
_blackWheelImage.src = path;
_blackWheelImage.style.opacity = "0";
_blackWheelImage.style.filter = "alpha(opacity=0)"
_blackWheelImage.style.position = "absolute";
#if PLATFORM(DOM) #if PLATFORM(DOM)
var path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel.png"]; _DOMElement.appendChild(_wheelImage);
_DOMElement.appendChild(_blackWheelImage);
_wheelImage = new Image();
_wheelImage.src = path;
_wheelImage.style.position = "absolute";
path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel_black.png"];
_blackWheelImage = new Image();
_blackWheelImage.src = path;
_blackWheelImage.style.opacity = "0";
_blackWheelImage.style.filter = "alpha(opacity=0)";
_blackWheelImage.style.position = "absolute";
_DOMElement.appendChild(_wheelImage);
_DOMElement.appendChild(_blackWheelImage);
#endif #endif
[self setWheelSize:aFrame.size]; [self setWheelSize:aFrame.size];
_crosshair = [[CPView alloc] initWithFrame:CGRectMake(_radius - 2, _radius - 2, 4, 4)]; _crosshair = [[CPView alloc] initWithFrame:CPRectMake(_radius - 2, _radius - 2, 4, 4)];
[_crosshair setBackgroundColor:[CPColor blackColor]]; [_crosshair setBackgroundColor:[CPColor blackColor]];
var view = [[CPView alloc] initWithFrame:CGRectInset([_crosshair bounds], 1.0, 1.0)]; var view = [[CPView alloc] initWithFrame:CGRectInset([_crosshair bounds], 1.0, 1.0)];
[view setBackgroundColor:[CPColor whiteColor]]; [view setBackgroundColor:[CPColor whiteColor]];
[_crosshair addSubview:view]; [_crosshair addSubview:view];
[self addSubview:_crosshair]; [self addSubview:_crosshair];
}
return self; return self;
} }
- (void)setWheelBrightness:(float)brightness - (void)setWheelBrightness:(float)brightness
{ {
#if PLATFORM(DOM)
_blackWheelImage.style.opacity = 1.0 - brightness; _blackWheelImage.style.opacity = 1.0 - brightness;
_blackWheelImage.style.filter = "alpha(opacity=" + (1.0 - brightness) * 100 + ")" _blackWheelImage.style.filter = "alpha(opacity=" + (1.0 - brightness) * 100 + ")"
#endif
} }
- (void)setFrameSize:(CGSize)aSize - (void)setFrameSize:(CPSize)aSize
{ {
[super setFrameSize:aSize]; [super setFrameSize:aSize];
[self setWheelSize:aSize]; [self setWheelSize:aSize];
} }
- (void)setWheelSize:(CGSize)aSize - (void)setWheelSize:(CPSize)aSize
{ {
var min = MIN(aSize.width, aSize.height); var min = MIN(aSize.width, aSize.height);
#if PLATFORM(DOM)
_blackWheelImage.style.width = min; _blackWheelImage.style.width = min;
_blackWheelImage.style.height = min; _blackWheelImage.style.height = min;
_blackWheelImage.width = min; _blackWheelImage.width = min;
@@ -302,7 +290,6 @@
_wheelImage.height = min; _wheelImage.height = min;
_wheelImage.style.top = (aSize.height - min) / 2.0 + "px"; _wheelImage.style.top = (aSize.height - min) / 2.0 + "px";
_wheelImage.style.left = (aSize.width - min) / 2.0 + "px"; _wheelImage.style.left = (aSize.width - min) / 2.0 + "px";
#endif
_radius = min / 2.0; _radius = min / 2.0;
@@ -362,15 +349,15 @@
_angle = [self radiansToDegrees:angle]; _angle = [self radiansToDegrees:angle];
_distance = (distance / _radius) * 100.0; _distance = (distance / _radius) * 100.0;
[_crosshair setFrameOrigin:CGPointMake(COS(angle) * distance + midX - 2.0, SIN(angle) * distance + midY - 2.0)]; [_crosshair setFrameOrigin:CPPointMake(COS(angle) * distance + midX - 2.0, SIN(angle) * distance + midY - 2.0)];
} }
- (void)setPositionToColor:(CPColor)aColor - (void)setPositionToColor:(CPColor)aColor
{ {
var hsb = [aColor hsbComponents], var hsb = [aColor hsbComponents],
bounds = [self bounds], bounds = [self bounds],
angle = [self degreesToRadians:hsb[0] * 360.0], angle = [self degreesToRadians:hsb[0]],
distance = hsb[1] * _radius; distance = (hsb[1] / 100.0) * _radius;
[self setAngle:angle distance:distance]; [self setAngle:angle distance:distance];
} }
-64
View File
@@ -1,64 +0,0 @@
/*
* CPColorSpace.j
* AppKit
*
* Created by Alexander Ljungberg.
* Copyright 2012, SlevenBits Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPObject.j>
var sRGBColorSpace = nil;
/*!
Represent color spaces.
*/
@implementation CPColorSpace : CPObject
{
CGColorSpace _cgColorSpace;
}
/*!
Return an object representing the standard sRGB color space.
*/
+ (CPColorSpace)sRGBColorSpace
{
if (!sRGBColorSpace)
sRGBColorSpace = [[self alloc] initWithCGColorSpace:CGColorSpaceCreateDeviceRGB()];
return sRGBColorSpace;
}
- (id)initWithCGColorSpace:(CGColorSpace)cgColorSpace
{
if (self = [super init])
{
_cgColorSpace = cgColorSpace;
}
return self;
}
/*!
Return a Core Graphics color space representing the receiver.
*/
- (CGColorSpace)CGColorSpace
{
return _cgColorSpace;
}
@end
+83 -190
View File
@@ -43,38 +43,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
BOOL _bordered; BOOL _bordered;
CPColor _color; CPColor _color;
} CPView _wellView;
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding == CPValueBinding)
return [CPColorWellValueBinder class];
return [super _binderClassForBinding:aBinding];
}
+ (CPString)defaultThemeClass
{
return @"colorwell";
}
+ (CPDictionary)themeAttributes
{
return @{
@"bezel-inset": CGInsetMakeZero(),
@"bezel-color": [CPNull null],
@"content-inset": CGInsetMake(3.0, 3.0, 3.0, 3.0),
@"content-border-inset": CGInsetMakeZero(),
@"content-border-color": [CPNull null],
};
}
- (void)_reverseSetBinding
{
var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
theBinding = [binderClass getBinding:CPValueBinding forObject:self];
[theBinding reverseSetValueFor:@"color"];
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
@@ -84,14 +53,19 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
if (self) if (self)
{ {
_active = NO; _active = NO;
_bordered = YES;
_color = [CPColor whiteColor]; _color = [CPColor whiteColor];
[self setBordered:YES];
[self drawBezelWithHighlight:NO];
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
[self _registerForNotifications];
} }
return self; return self;
} }
- (void)_registerNotifications - (void)_registerForNotifications
{ {
var defaultCenter = [CPNotificationCenter defaultCenter]; var defaultCenter = [CPNotificationCenter defaultCenter];
@@ -108,39 +82,25 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
object:[CPColorPanel sharedColorPanel]]; object:[CPColorPanel sharedColorPanel]];
} }
- (void)_removeNotifications
{
var defaultCenter = [CPNotificationCenter defaultCenter];
[defaultCenter
removeObserver:self
name:_CPColorWellDidBecomeExclusiveNotification
object:nil];
[defaultCenter
removeObserver:self
name:CPWindowWillCloseNotification
object:[CPColorPanel sharedColorPanel]];
}
/*!
Sets whether the color well is bordered.
*/
- (void)setBordered:(BOOL)shouldBeBordered
{
if (shouldBeBordered)
[self setThemeState:CPThemeStateBordered];
else
[self unsetThemeState:CPThemeStateBordered];
}
/*! /*!
Returns whether the color well is bordered Returns whether the color well is bordered
*/ */
- (BOOL)isBordered - (BOOL)isBordered
{ {
return [self hasThemeState:CPThemeStateBordered]; return _bordered;
}
/*!
Sets the color well's current color.
*/
- (void)setBordered:(BOOL)bordered
{
if (_bordered == bordered)
return;
_bordered = bordered;
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
} }
// Managing Color From Color Wells // Managing Color From Color Wells
@@ -163,7 +123,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
_color = aColor; _color = aColor;
[self setNeedsLayout]; [self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
} }
/*! /*!
@@ -226,6 +186,31 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
return _active; return _active;
} }
// Drawing a Color Well
- (void)drawBezelWithHighlight:(BOOL)shouldHighlight
{
}
/*!
Draws the colored area inside the color well without borders.
@param aRect the location at which to draw
*/
- (void)drawWellInside:(CGRect)aRect
{
if (!_wellView)
{
_wellView = [[CPView alloc] initWithFrame:aRect];
[_wellView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
[self addSubview:_wellView];
}
else
[_wellView setFrame:aRect];
[_wellView setBackgroundColor:_color];
}
- (void)colorPanelDidChangeColor:(CPNotification)aNotification - (void)colorPanelDidChangeColor:(CPNotification)aNotification
{ {
[self takeColorFrom:[aNotification object]]; [self takeColorFrom:[aNotification object]];
@@ -244,143 +229,37 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
[self deactivate]; [self deactivate];
} }
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp - (void)mouseDown:(CPEvent)anEvent
{ {
[self highlight:NO]; if (![self isEnabled])
return;
if (!mouseIsUp || !CGRectContainsPoint([self bounds], aPoint) || ![self isEnabled]) [self drawBezelWithHighlight:YES];
}
- (void)mouseDragged:(CPEvent)anEvent
{
if (![self isEnabled])
return;
[self drawBezelWithHighlight:CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil])];
}
- (void)mouseUp:(CPEvent)anEvent
{
[self drawBezelWithHighlight:NO];
if (!CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]) || ![self isEnabled])
return; return;
[self activate:YES]; [self activate:YES];
var colorPanel = [CPColorPanel sharedColorPanel]; var colorPanel = [CPColorPanel sharedColorPanel];
[colorPanel setPlatformWindow:[[self window] platformWindow]];
[colorPanel setColor:_color]; [colorPanel setColor:_color];
[colorPanel orderFront:self]; [colorPanel orderFront:self];
} }
- (CGRect)contentRectForBounds:(CGRect)bounds
{
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
return CGRectInsetByInset(bounds, contentInset);
}
- (CGRect)bezelRectForBounds:(CGRect)bounds
{
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
return CGRectInsetByInset(bounds, bezelInset);
}
- (CGRect)contentBorderRectForBounds:(CGRect)bounds
{
var contentBorderInset = [self currentValueForThemeAttribute:@"content-border-inset"];
return CGRectInsetByInset(bounds, contentBorderInset);
}
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
{
switch (aName)
{
case "bezel-view":
return [self bezelRectForBounds:[self bounds]];
case "content-view":
return [self contentRectForBounds:[self bounds]];
case "content-border-view":
return [self contentBorderRectForBounds:[self bounds]];
}
return [super rectForEphemeralSubviewNamed:aName];
}
- (CPView)createEphemeralSubviewNamed:(CPString)aName
{
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
[view setHitTests:NO];
return view;
}
- (void)layoutSubviews
{
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"];
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"bezel-view"];
[contentView setBackgroundColor:_color];
var contentBorderView = [self layoutEphemeralSubviewNamed:@"content-border-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"content-view"];
[contentBorderView setBackgroundColor:[self currentValueForThemeAttribute:@"content-border-color"]];
}
#pragma mark -
#pragma mark Observers method
- (void)_addObservers
{
if (_isObserving)
return;
[super _addObservers];
[self _registerNotifications];
}
- (void)_removeObservers
{
if (!_isObserving)
return;
[super _removeObservers];
[self _removeNotifications];
}
@end
@implementation CPColorWellValueBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
var placeholderColor = [CPColor blackColor];
[self _setPlaceholder:placeholderColor forMarker:CPMultipleValuesMarker isDefault:YES];
[self _setPlaceholder:placeholderColor forMarker:CPNoSelectionMarker isDefault:YES];
[self _setPlaceholder:placeholderColor forMarker:CPNotApplicableMarker isDefault:YES];
[self _setPlaceholder:placeholderColor forMarker:CPNullMarker isDefault:YES];
}
- (id)valueForBinding:(CPString)aBinding
{
return [_source color];
}
- (void)setValue:(id)aValue forBinding:(CPString)theBinding
{
[_source setColor:aValue];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
[_source setColor:aValue];
}
@end @end
var CPColorWellColorKey = "CPColorWellColorKey", var CPColorWellColorKey = "CPColorWellColorKey",
@@ -399,8 +278,13 @@ var CPColorWellColorKey = "CPColorWellColorKey",
if (self) if (self)
{ {
_active = NO; _active = NO;
_bordered = [aCoder decodeBoolForKey:CPColorWellBorderedKey];
_color = [aCoder decodeObjectForKey:CPColorWellColorKey]; _color = [aCoder decodeObjectForKey:CPColorWellColorKey];
[self setBordered:[aCoder decodeBoolForKey:CPColorWellBorderedKey]];
[self drawBezelWithHighlight:NO];
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
[self _registerForNotifications];
} }
return self; return self;
@@ -412,10 +296,19 @@ var CPColorWellColorKey = "CPColorWellColorKey",
*/ */
- (void)encodeWithCoder:(CPCoder)aCoder - (void)encodeWithCoder:(CPCoder)aCoder
{ {
// We do this in order to avoid encoding the _wellView, which
// should just automatically be created programmatically as needed.
var actualSubviews = _subviews;
_subviews = [_subviews copy];
[_subviews removeObjectIdenticalTo:_wellView];
[super encodeWithCoder:aCoder]; [super encodeWithCoder:aCoder];
_subviews = actualSubviews;
[aCoder encodeObject:_color forKey:CPColorWellColorKey]; [aCoder encodeObject:_color forKey:CPColorWellColorKey];
[aCoder encodeObject:[self isBordered] forKey:CPColorWellBorderedKey]; [aCoder encodeObject:_bordered forKey:CPColorWellBorderedKey];
} }
@end @end
-1299
View File
File diff suppressed because it is too large Load Diff
+66 -317
View File
@@ -20,18 +20,17 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "CPEvent_Constants.j" @import "CPEvent.j"
@import "CPPlatform.j" @import "CPPlatform.j"
// Browser Engines // Browser Engines
CPUnknownBrowserEngine = 0; CPUnknownBrowserEngine = 0;
CPGeckoBrowserEngine = 1 << 0; CPGeckoBrowserEngine = 1;
CPInternetExplorerBrowserEngine = 1 << 1; CPInternetExplorerBrowserEngine = 2;
CPKHTMLBrowserEngine = 1 << 2; CPKHTMLBrowserEngine = 3;
CPOperaBrowserEngine = 1 << 3; CPOperaBrowserEngine = 4;
CPWebKitBrowserEngine = 1 << 4; // Safari + Chrome CPWebKitBrowserEngine = 5;
CPBlinkBrowserEngine = 1 << 5; // Recent Chrome
// Operating Systems // Operating Systems
CPMacOperatingSystem = 0; CPMacOperatingSystem = 0;
@@ -39,80 +38,42 @@ CPWindowsOperatingSystem = 1;
CPOtherOperatingSystem = 2; CPOtherOperatingSystem = 2;
// Features // Features
CPCSSRGBAFeature = 5; CPCSSRGBAFeature = 1 << 5;
CPHTMLCanvasFeature = 6; CPHTMLCanvasFeature = 1 << 6;
CPHTMLContentEditableFeature = 7; CPHTMLContentEditableFeature = 1 << 7;
CPHTMLDragAndDropFeature = 8; CPHTMLDragAndDropFeature = 1 << 8;
CPJavaScriptInnerTextFeature = 9; CPJavaScriptInnerTextFeature = 1 << 9;
CPJavaScriptTextContentFeature = 10; CPJavaScriptTextContentFeature = 1 << 10;
// In onpaste, oncopy and oncut events, the event has an event.clipboardData from which the current pasteboard contents can be read with event.clipboardData.getData. CPJavaScriptClipboardEventsFeature = 1 << 11;
CPJavaScriptClipboardEventsFeature = 11; CPJavaScriptClipboardAccessFeature = 1 << 12;
// window.clipboardData exists and can be read and written to at any time using window.clipboardData.getData/setData. CPJavaScriptCanvasDrawFeature = 1 << 13;
CPJavaScriptClipboardAccessFeature = 12; CPJavaScriptCanvasTransformFeature = 1 << 14;
CPJavaScriptCanvasDrawFeature = 13;
CPJavaScriptCanvasTransformFeature = 14;
CPVMLFeature = 15; CPVMLFeature = 1 << 15;
CPJavaScriptRemedialKeySupport = 16; CPJavaScriptRemedialKeySupport = 1 << 16;
CPJavaScriptShadowFeature = 20; CPJavaScriptShadowFeature = 1 << 20;
CPJavaScriptNegativeMouseWheelValues = 22; CPJavaScriptNegativeMouseWheelValues = 1 << 22;
CPJavaScriptMouseWheelValues_8_15 = 23; CPJavaScriptMouseWheelValues_8_15 = 1 << 23;
CPOpacityRequiresFilterFeature = 24; CPOpacityRequiresFilterFeature = 1 << 24;
// Internet explorer does not allow dynamically changing the type of an input element //Internet explorer does not allow dynamically changing the type of an input element
CPInputTypeCanBeChangedFeature = 25; CPInputTypeCanBeChangedFeature = 1 << 25;
CPHTML5DragAndDropSourceYOffBy1 = 26; CPHTML5DragAndDropSourceYOffBy1 = 1 << 26;
CPSOPDisabledFromFileURLs = 27;
// element.style.font can be set for an element not in the DOM.
CPInputSetFontOutsideOfDOM = 28;
// Input elements have 1 px of extra padding on the left regardless of padding setting.
CPInput1PxLeftPadding = 29;
CPInputOnInputEventFeature = 30;
CPFileAPIFeature = 31;
CPAltEnterTextAreaFeature = 32;
CPCSSAnimationFeature = 33;
/*
When an absolutely positioned div (CPView) with an absolutely positioned canvas in it (CPView with drawRect:) moves things on top of the canvas (subviews) don't redraw correctly. E.g. if you have a bunch of text fields in a CPBox in a sheet which animates in, some of the text fields might not be visible because the CPBox has a canvas at the bottom and the box moved form offscreen to onscreen. This bug is probably very related: https://bugs.webkit.org/show_bug.cgi?id=67203
*/
CPCanvasParentDrawErrorsOnMovementBug = 1 << 0;
// The paste event is only sent if an input or textarea has focus.
CPJavaScriptPasteRequiresEditableTarget = 1 << 1;
// Redirecting the focus of the browser on keydown to an input for Cmd-V or Ctrl-V makes the paste fail.
CPJavaScriptPasteCantRefocus = 1 << 2;
/*
Safari calculates incorrect text size unless you set the canvas font even if it is already set
You can see the bug after disabling the workaround and opening any panel while typing.
You can use the font panel in the manual test for CPTextView.
Look out for a displaced cursor, i.e. after typing letters of small width, such as the 'i'.
https://bugs.webkit.org/show_bug.cgi?id=150224
*/
CPTextSizingAlwaysNeedsSetFontBug = 1 << 3;
CPSOPDisabledFromFileURLs = 1 << 27;
var USER_AGENT = "", var USER_AGENT = "",
PLATFORM_ENGINE = CPUnknownBrowserEngine, PLATFORM_ENGINE = CPUnknownBrowserEngine,
PLATFORM_FEATURES = [], PLATFORM_FEATURES = 0;
PLATFORM_BUGS = 0,
PLATFORM_STYLE_JS_PROPERTIES = {};
// default these features to true // default these features to true
PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = YES;
PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = YES; PLATFORM_FEATURES |= CPInputTypeCanBeChangedFeature;
PLATFORM_FEATURES[CPAltEnterTextAreaFeature] = YES;
if (typeof window !== "undefined" && typeof window.navigator !== "undefined") if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
USER_AGENT = window.navigator.userAgent; USER_AGENT = window.navigator.userAgent;
@@ -120,44 +81,41 @@ if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
// Opera // Opera
if (typeof window !== "undefined" && window.opera) if (typeof window !== "undefined" && window.opera)
{ {
PLATFORM_ENGINE |= CPOperaBrowserEngine; PLATFORM_ENGINE = CPOperaBrowserEngine;
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES; PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
} }
// Internet Explorer // Internet Explorer
else if (typeof window !== "undefined" && (window.attachEvent || (!(window.ActiveXObject) && "ActiveXObject" in window))) // Must follow Opera check. else if (typeof window !== "undefined" && window.attachEvent) // Must follow Opera check.
{ {
PLATFORM_ENGINE |= CPInternetExplorerBrowserEngine; PLATFORM_ENGINE = CPInternetExplorerBrowserEngine;
// Features we can only be sure of with IE (no known independent tests) // Features we can only be sure of with IE (no known independent tests)
PLATFORM_FEATURES[CPVMLFeature] = YES; PLATFORM_FEATURES |= CPVMLFeature;
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES; PLATFORM_FEATURES |= CPJavaScriptRemedialKeySupport;
PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES; PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = YES; PLATFORM_FEATURES |= CPOpacityRequiresFilterFeature;
PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = NO; PLATFORM_FEATURES &= ~CPInputTypeCanBeChangedFeature;
// Tested in Internet Explore 8 and 9.
PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = NO;
// IE allows free clipboard access.
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = YES;
} }
// Safari + Chrome (WebKit and Blink) // WebKit
else if (USER_AGENT.indexOf("AppleWebKit/") != -1) else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
{ {
PLATFORM_ENGINE |= CPWebKitBrowserEngine; PLATFORM_ENGINE = CPWebKitBrowserEngine;
// Features we can only be sure of with WebKit (no known independent tests) // Features we can only be sure of with WebKit (no known independent tests)
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES; PLATFORM_FEATURES |= CPCSSRGBAFeature;
PLATFORM_FEATURES[CPHTMLContentEditableFeature] = YES; PLATFORM_FEATURES |= CPHTMLContentEditableFeature;
PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES; if (USER_AGENT.indexOf("Chrome") === -1)
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = NO; PLATFORM_FEATURES |= CPHTMLDragAndDropFeature;
PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
PLATFORM_FEATURES |= CPJavaScriptClipboardEventsFeature;
PLATFORM_FEATURES |= CPJavaScriptClipboardAccessFeature;
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length, var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
versionEnd = USER_AGENT.indexOf(" ", versionStart), versionEnd = USER_AGENT.indexOf(" ", versionStart),
@@ -167,139 +125,76 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
minorVersion = parseInt(versionString.substr(versionDivision + 1)); minorVersion = parseInt(versionString.substr(versionDivision + 1));
if ((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound) if ((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES; PLATFORM_FEATURES |= CPJavaScriptRemedialKeySupport;
// FIXME this is a terrible hack to get around this bug: // FIXME this is a terrible hack to get around this bug:
// https://bugs.webkit.org/show_bug.cgi?id=21548 // https://bugs.webkit.org/show_bug.cgi?id=21548
if (![CPPlatform isBrowser]) if (![CPPlatform isBrowser])
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES; PLATFORM_FEATURES |= CPJavaScriptRemedialKeySupport;
if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6)) if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
PLATFORM_FEATURES[CPHTML5DragAndDropSourceYOffBy1] = YES; PLATFORM_FEATURES |= CPHTML5DragAndDropSourceYOffBy1;
// This is supposedly fixed in webkit r123603. Seems to work in Chrome 21 but not Safari 6.0.
if (majorVersion < 537)
PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
if (USER_AGENT.indexOf("Chrome") === CPNotFound) if (USER_AGENT.indexOf("Chrome") === CPNotFound)
{ PLATFORM_FEATURES |= CPSOPDisabledFromFileURLs;
PLATFORM_FEATURES[CPSOPDisabledFromFileURLs] = YES;
PLATFORM_FEATURES[CPHTMLDragAndDropFeature] = YES;
// https://bugs.webkit.org/show_bug.cgi?id=75891
PLATFORM_BUGS |= CPJavaScriptPasteRequiresEditableTarget;
// https://bugs.webkit.org/show_bug.cgi?id=39689
PLATFORM_BUGS |= CPJavaScriptPasteCantRefocus;
PLATFORM_BUGS |= CPTextSizingAlwaysNeedsSetFontBug;
}
else if ((window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window)
PLATFORM_ENGINE |= CPBlinkBrowserEngine;
// Assume this bug was introduced around Safari 5.1/Chrome 16. This could probably be tighter.
if (majorVersion > 533)
PLATFORM_BUGS |= CPCanvasParentDrawErrorsOnMovementBug;
} }
// KHTML // KHTML
else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check. else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
{ {
PLATFORM_ENGINE |= CPKHTMLBrowserEngine; PLATFORM_ENGINE = CPKHTMLBrowserEngine;
} }
// Gecko // Gecko
else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check. else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
{ {
PLATFORM_ENGINE |= CPGeckoBrowserEngine; PLATFORM_ENGINE = CPGeckoBrowserEngine;
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES; PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
var index = USER_AGENT.indexOf("Firefox"), var index = USER_AGENT.indexOf("Firefox"),
version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1)); version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
if (version >= 3.0) if (version >= 3.0)
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES; PLATFORM_FEATURES |= CPCSSRGBAFeature;
if (version < 3.0) if (version < 3.0)
PLATFORM_FEATURES[CPJavaScriptMouseWheelValues_8_15] = YES; PLATFORM_FEATURES |= CPJavaScriptMouseWheelValues_8_15;
// Some day this might be fixed and should be version prefixed. No known fixed version yet.
PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
PLATFORM_FEATURES[CPAltEnterTextAreaFeature] = NO;
// This was supposed to be added in Firefox 22, but when testing with the latest beta as of 2013-06-14
// it does not seem to work. It seems to exhibit the CPJavaScriptPasteRequiresEditableTarget problem,
// and in addition doesn't seem to work with our native copy code either.
/*if (version >= 22.0)
{
PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
// TODO File a bug at https://bugzilla.mozilla.org/. In other browsers, one can return "false" from the
// beforepaste event to indicate a paste should be enabled even that the DOMEvent.target is not editable.
PLATFORM_BUGS |= CPJavaScriptPasteRequiresEditableTarget;
}*/
} }
// Feature-specific checks // Feature Specific Checks
if (typeof document != "undefined") if (typeof document != "undefined")
{ {
var canvasElement = document.createElement("canvas"); var canvasElement = document.createElement("canvas");
// Detect Canvas Support
// Detect canvas support
if (canvasElement && canvasElement.getContext) if (canvasElement && canvasElement.getContext)
{ {
PLATFORM_FEATURES[CPHTMLCanvasFeature] = YES; PLATFORM_FEATURES |= CPHTMLCanvasFeature;
// Any browser that supports canvas supports CSS opacity // Detect Canvas setTransform/transform support
PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = NO;
// Detect canvas setTransform/transform support
var context = document.createElement("canvas").getContext("2d"); var context = document.createElement("canvas").getContext("2d");
if (context && context.setTransform && context.transform) if (context && context.setTransform && context.transform)
PLATFORM_FEATURES[CPJavaScriptCanvasTransformFeature] = YES; PLATFORM_FEATURES |= CPJavaScriptCanvasTransformFeature;
} }
var DOMElement = document.createElement("div"); var DOMElement = document.createElement("div");
// Detect whether we have innerText or textContent (or neither) // Detect whether we have innerText or textContent (or neither)
if (DOMElement.innerText != undefined) if (DOMElement.innerText != undefined)
PLATFORM_FEATURES[CPJavaScriptInnerTextFeature] = YES; PLATFORM_FEATURES |= CPJavaScriptInnerTextFeature;
else if (DOMElement.textContent != undefined) else if (DOMElement.textContent != undefined)
PLATFORM_FEATURES[CPJavaScriptTextContentFeature] = YES; PLATFORM_FEATURES |= CPJavaScriptTextContentFeature;
var DOMInputElement = document.createElement("input");
if ("oninput" in DOMInputElement)
PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
else if (typeof DOMInputElement.setAttribute === "function")
{
DOMInputElement.setAttribute("oninput", "return;");
if (typeof DOMInputElement.oninput === "function")
PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
}
// Detect FileAPI support
if (typeof DOMInputElement.setAttribute === "function")
{
DOMInputElement.setAttribute("type", "file");
PLATFORM_FEATURES[CPFileAPIFeature] = !!DOMInputElement["files"];
}
else
PLATFORM_FEATURES[CPFileAPIFeature] = NO;
} }
function CPFeatureIsCompatible(aFeature) function CPFeatureIsCompatible(aFeature)
{ {
return !!PLATFORM_FEATURES[aFeature]; return PLATFORM_FEATURES & aFeature;
}
function CPPlatformHasBug(aBug)
{
return PLATFORM_BUGS & aBug;
} }
function CPBrowserIsEngine(anEngine) function CPBrowserIsEngine(anEngine)
{ {
return PLATFORM_ENGINE & anEngine; return PLATFORM_ENGINE === anEngine;
} }
function CPBrowserIsOperatingSystem(anOperatingSystem) function CPBrowserIsOperatingSystem(anOperatingSystem)
@@ -334,149 +229,3 @@ else
CPUndoKeyEquivalentModifierMask = CPControlKeyMask; CPUndoKeyEquivalentModifierMask = CPControlKeyMask;
CPRedoKeyEquivalentModifierMask = CPControlKeyMask; CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
} }
/*!
Sets a feature with the given value.
*/
function CPSetPlatformFeature(aFeature, aBool)
{
PLATFORM_FEATURES[aFeature] = aBool;
}
/*!
Return the properly prefixed JS property for the given name. E.g. in a webkit browser,
CPBrowserStyleProperty('transition') -> WebkitTransition
While technically not a style property, style related event handler names are also supported.
CPBrowserStyleProperty('transitionend') -> 'webkitTransitionEnd'
CSS is only available in platform(dom), so don't rely too heavily on it.
*/
function CPBrowserStyleProperty(aProperty)
{
var lowerProperty = aProperty.toLowerCase();
if (PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] === undefined)
{
var r = nil;
#if PLATFORM(DOM)
var testElement = document.createElement('div');
switch (lowerProperty)
{
case 'transitionend':
var candidates = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
};
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transition']] || nil;
break;
case 'transformorigin':
var candidates = {
'WebkitTransform' : 'WebkitTransformOrigin',
'MozTransform' : 'MozTransformOrigin',
'OTransform' : 'OTransformOrigin',
'msTransform' : 'MSTransformOrigin',
'transform' : 'transformOrigin'
};
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transform']] || nil;
break;
case 'animationend':
var candidates = {
'WebkitAnimation' : 'webkitAnimationEnd',
'MozAnimation' : 'animationend',
'OAnimation' : 'oAnimationEnd',
'msAnimation' : 'MSAnimationEnd',
'animation' : 'animationend'
};
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['animation']] || nil;
break;
default:
var prefixes = ["Webkit", "Moz", "O", "ms"],
strippedProperty = aProperty.split('-').join(' '),
capProperty = [strippedProperty capitalizedString].split(' ').join('');
for (var i = 0; i < prefixes.length; i++)
{
// First check if the property is already valid without being formatted, otherwise try the capitalized property
if (prefixes[i] + aProperty in testElement.style)
{
r = prefixes[i] + aProperty;
break;
}
else if (prefixes[i] + capProperty in testElement.style)
{
r = prefixes[i] + capProperty;
break;
}
}
if (!r && lowerProperty in testElement.style)
r = lowerProperty;
break;
}
#endif
PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] = r;
}
return PLATFORM_STYLE_JS_PROPERTIES[lowerProperty];
}
function CPBrowserCSSProperty(aProperty)
{
var browserProperty = CPBrowserStyleProperty(aProperty);
if (!browserProperty)
return nil;
var prefixes = {
'Webkit': '-webkit-',
'Moz': '-moz-',
'O': '-o-',
'ms': '-ms-'
};
for (var prefix in prefixes)
{
if (browserProperty.substring(0, prefix.length) == prefix)
{
var browserPropertyWithoutPrefix = browserProperty.substring(prefix.length),
parts = browserPropertyWithoutPrefix.match(/[A-Z][a-z]+/g);
// If there were any capitalized words in the browserProperty, insert a "-" between each one
if (parts && parts.length > 0)
browserPropertyWithoutPrefix = parts.join("-");
return prefixes[prefix] + browserPropertyWithoutPrefix.toLowerCase();
}
}
var parts = browserProperty.match(/[A-Z][a-z]+/g);
if (parts && parts.length > 0)
browserProperty = parts.join("-");
return browserProperty.toLowerCase();
}
function CPBrowserBackingStorePixelRatio(context)
{
return context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
}
+96 -363
View File
@@ -20,70 +20,47 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import <Foundation/CPFormatter.j>
@import <Foundation/CPTimer.j>
@import "CPFont.j" @import "CPFont.j"
@import "CPShadow.j" @import "CPShadow.j"
@import "CPText.j" @import "CPView.j"
@import "CPKeyValueBinding.j" @import "CPKeyValueBinding.j"
@import "CPTrackingArea.j"
@global CPApp CPLeftTextAlignment = 0;
CPRightTextAlignment = 1;
CPCenterTextAlignment = 2;
CPJustifiedTextAlignment = 3;
CPNaturalTextAlignment = 4;
@protocol CPControlTextEditingDelegate <CPObject> CPRegularControlSize = 0;
CPSmallControlSize = 1;
CPMiniControlSize = 2;
@optional CPLineBreakByWordWrapping = 0;
- (void)controlTextDidBeginEditing:(CPNotification)aNotification; CPLineBreakByCharWrapping = 1;
- (void)controlTextDidChange:(CPNotification)aNotification; CPLineBreakByClipping = 2;
- (void)controlTextDidEndEditing:(CPNotification)aNotification; CPLineBreakByTruncatingHead = 3;
- (void)controlTextDidFocus:(CPNotification)aNotification; CPLineBreakByTruncatingTail = 4;
- (void)controlTextDidBlur:(CPNotification)aNotification; CPLineBreakByTruncatingMiddle = 5;
- (BOOL)control:(CPControl)control didFailToFormatString:(CPString)string errorDescription:(CPString)error;
@end CPTopVerticalTextAlignment = 1;
CPCenterVerticalTextAlignment = 2;
CPBottomVerticalTextAlignment = 3;
@typedef CPControlSize CPScaleProportionally = 0;
CPRegularControlSize = 0; CPScaleToFit = 1;
CPSmallControlSize = 1; CPScaleNone = 2;
CPMiniControlSize = 2;
@typedef CPLineBreakMode CPNoImage = 0;
CPLineBreakByWordWrapping = 0; CPImageOnly = 1;
CPLineBreakByCharWrapping = 1; CPImageLeft = 2;
CPLineBreakByClipping = 2; CPImageRight = 3;
CPLineBreakByTruncatingHead = 3; CPImageBelow = 4;
CPLineBreakByTruncatingTail = 4; CPImageAbove = 5;
CPLineBreakByTruncatingMiddle = 5; CPImageOverlaps = 6;
@typedef CPVerticalTextAlignment CPOnState = 1;
CPTopVerticalTextAlignment = 1; CPOffState = 0;
CPCenterVerticalTextAlignment = 2; CPMixedState = -1;
CPBottomVerticalTextAlignment = 3;
// Deprecated for use with images, use the CPImageScale constants
@typedef CPImageScaling
CPScaleProportionally = 0;
CPScaleToFit = 1;
CPScaleNone = 2;
CPImageScaleProportionallyDown = 0;
CPImageScaleAxesIndependently = 1;
CPImageScaleNone = 2;
CPImageScaleProportionallyUpOrDown = 3;
@typedef CPCellImagePosition
CPNoImage = 0;
CPImageOnly = 1;
CPImageLeft = 2;
CPImageRight = 3;
CPImageBelow = 4;
CPImageAbove = 5;
CPImageOverlaps = 6;
CPOnState = 1;
CPOffState = 0;
CPMixedState = -1;
CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor"; CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor"; CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
@@ -105,7 +82,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;
@@ -118,58 +94,60 @@ var CPControlBlackColor = [CPColor blackColor];
BOOL _trackingWasWithinFrame; BOOL _trackingWasWithinFrame;
unsigned _trackingMouseDownFlags; unsigned _trackingMouseDownFlags;
CGPoint _previousTrackingLocation; CGPoint _previousTrackingLocation;
CPControlSize _controlSize;
CPWritingDirection _baseWritingDirection @accessors(property=baseWritingDirection);
} }
+ (CPDictionary)themeAttributes + (CPDictionary)themeAttributes
{ {
return @{ return [CPDictionary dictionaryWithObjects:[CPLeftTextAlignment,
@"alignment": CPLeftTextAlignment, CPTopVerticalTextAlignment,
@"vertical-alignment": CPTopVerticalTextAlignment, CPLineBreakByClipping,
@"line-break-mode": CPLineBreakByClipping, [CPColor blackColor],
@"text-color": [CPColor blackColor], [CPFont systemFontOfSize:12.0],
@"font": [CPFont systemFontOfSize:CPFontCurrentSystemSize], [CPNull null],
@"text-shadow-color": [CPNull null], _CGSizeMakeZero(),
@"text-shadow-offset": CGSizeMakeZero(), CPImageLeft,
@"image-position": CPImageLeft, CPScaleToFit,
@"image-scaling": CPScaleToFit, _CGSizeMakeZero(),
@"min-size": CGSizeMakeZero(), _CGSizeMake(-1.0, -1.0)]
@"max-size": CGSizeMake(-1.0, -1.0), forKeys:[@"alignment",
@"nib2cib-adjustment-frame": CGRectMakeZero() @"vertical-alignment",
}; @"line-break-mode",
@"text-color",
@"font",
@"text-shadow-color",
@"text-shadow-offset",
@"image-position",
@"image-scaling",
@"min-size",
@"max-size"]];
} }
+ (void)initialize + (void)initialize
{ {
if (self !== [CPControl class]) if (self === [CPControl class])
return; {
[self exposeBinding:@"value"];
[self exposeBinding:@"objectValue"];
[self exposeBinding:@"stringValue"];
[self exposeBinding:@"integerValue"];
[self exposeBinding:@"intValue"];
[self exposeBinding:@"doubleValue"];
[self exposeBinding:@"floatValue"];
[self exposeBinding:@"value"]; [self exposeBinding:@"enabled"];
[self exposeBinding:@"objectValue"]; }
[self exposeBinding:@"stringValue"];
[self exposeBinding:@"integerValue"];
[self exposeBinding:@"intValue"];
[self exposeBinding:@"doubleValue"];
[self exposeBinding:@"floatValue"];
[self exposeBinding:@"enabled"];
} }
+ (Class)_binderClassForBinding:(CPString)aBinding + (Class)_binderClassForBinding:(CPString)theBinding
{ {
if (aBinding === CPValueBinding) if (theBinding === CPValueBinding)
return [_CPValueBinder class]; return [_CPValueBinder class];
else if ([aBinding hasPrefix:CPEnabledBinding])
return [CPMultipleValueAndBinding class];
return [super _binderClassForBinding:aBinding]; return [super _binderClassForBinding:theBinding];
} }
/*! /*!
Reverse set the binding if the CPContinuouslyUpdatesValueBindingOption is set. Reverse set the binding iff the CPContinuouslyUpdatesValueBindingOption is set.
*/ */
- (void)_continuouslyReverseSetBinding - (void)_continuouslyReverseSetBinding
{ {
@@ -194,94 +172,13 @@ var CPControlBlackColor = [CPColor blackColor];
if (self) if (self)
{ {
_sendActionOn = CPLeftMouseUpMask; _sendActionOn = CPLeftMouseUpMask;
_trackingMouseDownFlags = 0; _trackingMouseDownFlags = 0;
} }
return self; return self;
} }
#pragma mark -
#pragma mark Control Size
/*!
Returns the control's control size
*/
- (CPControlSize)controlSize
{
return _controlSize;
}
/*!
Sets the control's size.
@param aControlSize the control's size
*/
- (void)setControlSize:(CPControlSize)aControlSize
{
if (_controlSize === aControlSize)
return;
[self unsetThemeState:[self _controlSizeThemeState]];
_controlSize = aControlSize;
[self setThemeState:[self _controlSizeThemeState]];
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
/*!
Gets the current theme state according to the current controlSize.
@return a CPThemeState
*/
- (ThemeState)_controlSizeThemeState
{
switch (_controlSize)
{
case CPSmallControlSize:
return CPThemeStateControlSizeSmall;
case CPMiniControlSize:
return CPThemeStateControlSizeMini;
case CPRegularControlSize:
default:
return CPThemeStateControlSizeRegular;
}
}
/*!
@ignore
Change frame size according to the theme control size theme constraints
Basically for height to min-size.
*/
- (void)_sizeToControlSize
{
var frameSize = [self frameSize],
minSize = [self currentValueForThemeAttribute:@"min-size"],
maxSize = [self currentValueForThemeAttribute:@"max-size"];
if (minSize.width > 0)
{
frameSize.width = MAX(minSize.width, frameSize.width);
if (maxSize.width > 0)
frameSize.width = MIN(maxSize.width, frameSize.width);
}
if (minSize.height > 0)
{
frameSize.height = MAX(minSize.height, frameSize.height);
if (maxSize.height > 0)
frameSize.height = MIN(maxSize.height, frameSize.height);
}
[self setFrameSize:frameSize];
}
#pragma mark -
/*! /*!
Sets the receiver's target action. Sets the receiver's target action.
@@ -324,14 +221,10 @@ var CPControlBlackColor = [CPColor blackColor];
@param anAction the action to send @param anAction the action to send
@param anObject the object to which the action will be sent @param anObject the object to which the action will be sent
*/ */
- (BOOL)sendAction:(SEL)anAction to:(id)anObject - (void)sendAction:(SEL)anAction to:(id)anObject
{ {
[self _reverseSetBinding]; [self _reverseSetBinding];
[CPApp sendAction:anAction to:anObject from:self];
var binding = [CPBinder getBinding:CPTargetBinding forObject:self];
[binding invokeAction];
return [CPApp sendAction:anAction to:anObject from:self];
} }
- (int)sendActionOn:(int)mask - (int)sendActionOn:(int)mask
@@ -420,11 +313,11 @@ var CPControlBlackColor = [CPColor blackColor];
_previousTrackingLocation = currentLocation; _previousTrackingLocation = currentLocation;
} }
- (void)setState:(CPInteger)state - (void)setState:(int)state
{ {
} }
- (CPInteger)nextState - (int)nextState
{ {
return 0; return 0;
} }
@@ -487,18 +380,7 @@ var CPControlBlackColor = [CPColor blackColor];
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
{ {
if (mouseIsUp) [self highlight:NO];
[self highlight:NO];
else
[self highlight:YES];
}
/*!
Enabled controls accept first mouse by default.
*/
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
{
return [self isEnabled];
} }
- (void)mouseDown:(CPEvent)anEvent - (void)mouseDown:(CPEvent)anEvent
@@ -620,46 +502,15 @@ var CPControlBlackColor = [CPColor blackColor];
*/ */
- (CPString)stringValue - (CPString)stringValue
{ {
if (_formatter && _value !== undefined) return (_value === undefined || _value === nil) ? "" : String(_value);
{
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);
} }
/*! /*!
Sets the receiver's string value. Sets the receiver's string value.
*/ */
- (void)setStringValue:(CPString)aString - (void)setStringValue:(CPString)anObject
{ {
// Cocoa raises an invalid parameter assertion and returns if you pass nil. [self setObjectValue:anObject];
if (aString === nil || aString === undefined)
{
CPLog.warn("nil or undefined sent to CPControl -setStringValue");
return;
}
var value;
if (_formatter)
{
value = nil;
if ([_formatter getObjectValue:@ref(value) forString:aString errorDescription:nil] === NO)
{
// If the given string is non-empty and doesn't work, Cocoa tries an empty string.
if (!aString || [_formatter getObjectValue:@ref(value) forString:@"" errorDescription:nil] === NO)
value = undefined; // Means the value is invalid
}
}
else
value = aString;
[self setObjectValue:value];
} }
- (void)takeDoubleValueFrom:(id)sender - (void)takeDoubleValueFrom:(id)sender
@@ -675,18 +526,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)])
@@ -705,7 +559,7 @@ var CPControlBlackColor = [CPColor blackColor];
if ([note object] != self) if ([note object] != self)
return; return;
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}]; [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
} }
- (void)textDidChange:(CPNotification)note - (void)textDidChange:(CPNotification)note
@@ -714,7 +568,7 @@ var CPControlBlackColor = [CPColor blackColor];
if ([note object] != self) if ([note object] != self)
return; return;
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:@{"CPFieldEditor": [note object]}]; [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
} }
- (void)textDidEndEditing:(CPNotification)note - (void)textDidEndEditing:(CPNotification)note
@@ -725,49 +579,7 @@ var CPControlBlackColor = [CPColor blackColor];
[self _reverseSetBinding]; [self _reverseSetBinding];
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[note userInfo]]; [[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
}
/*!
@ignore
Return the currentTextMovement needed by the delegate textDidEndEditing
This is going to check the currentEvent of the CPApp
*/
- (unsigned)_currentTextMovement
{
var currentEvent = [CPApp currentEvent],
keyCode = [currentEvent keyCode],
modifierFlags = [currentEvent modifierFlags];
switch (keyCode)
{
case CPEscapeKeyCode:
return CPCancelTextMovement;
case CPLeftArrowKeyCode:
return CPLeftTextMovement;
case CPRightArrowKeyCode:
return CPRightTextMovement;
case CPUpArrowKeyCode:
return CPUpTextMovement;
case CPDownArrowKeyCode:
return CPDownTextMovement;
case CPReturnKeyCode:
return CPReturnTextMovement;
case CPTabKeyCode:
if (modifierFlags & CPShiftKeyMask)
return CPBacktabTextMovement;
return CPTabTextMovement;
default:
return CPOtherTextMovement;
}
} }
/*! /*!
@@ -939,10 +751,9 @@ var CPControlBlackColor = [CPColor blackColor];
Sets the image scaling of the control. Sets the image scaling of the control.
<pre> <pre>
CPImageScaleProportionallyDown CPScaleProportionally
CPImageScaleAxesIndependently CPScaleToFit
CPImageScaleNone CPScaleNone
CPImageScaleProportionallyUpOrDown
</pre> </pre>
*/ */
- (void)setImageScaling:(CPImageScaling)scaling - (void)setImageScaling:(CPImageScaling)scaling
@@ -953,7 +764,7 @@ var CPControlBlackColor = [CPColor blackColor];
/*! /*!
Returns the image scaling of the control. Returns the image scaling of the control.
*/ */
- (CPUInteger)imageScaling - (CPImageScaling)imageScaling
{ {
return [self valueForThemeAttribute:@"image-scaling"]; return [self valueForThemeAttribute:@"image-scaling"];
} }
@@ -1011,83 +822,19 @@ var CPControlBlackColor = [CPColor blackColor];
return [self hasThemeState:CPThemeStateHighlighted]; return [self hasThemeState:CPThemeStateHighlighted];
} }
#pragma mark -
#pragma mark Base writing direction
/*!
Sets the initial writing direction of the receiver
@param writingDirection - It could be CPWritingDirectionNatural, CPWritingDirectionLeftToRight, CPWritingDirectionRightToLeft
*/
- (void)setBaseWritingDirection:(CPWritingDirection)writingDirection
{
if (writingDirection == _baseWritingDirection)
return;
[self willChangeValueForKey:@"baseWritingDirection"];
_baseWritingDirection = writingDirection;
[self didChangeValueForKey:@"baseWritingDirection"];
#if PLATFORM(DOM)
var style;
switch (_baseWritingDirection)
{
case CPWritingDirectionNatural:
style = "initial";
break;
case CPWritingDirectionLeftToRight:
style = "ltr";
break;
case CPWritingDirectionRightToLeft:
style = "rtl";
break;
default:
style = "initial";
}
_DOMElement.style.direction = style;
#endif
}
@end @end
@implementation CPControl (CPTrackingArea) var CPControlValueKey = "CPControlValueKey",
{ CPControlControlStateKey = @"CPControlControlStateKey",
CPTrackingArea _controlTrackingArea; CPControlIsEnabledKey = "CPControlIsEnabledKey",
}
- (void)updateTrackingAreas CPControlTargetKey = "CPControlTargetKey",
{ CPControlActionKey = "CPControlActionKey",
if (_controlTrackingArea) CPControlSendActionOnKey = "CPControlSendActionOnKey",
[self removeTrackingArea:_controlTrackingArea];
_controlTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect
owner:self
userInfo:nil];
[self addTrackingArea:_controlTrackingArea];
[super updateTrackingAreas];
}
@end CPControlSendsActionOnEndEditingKey = "CPControlSendsActionOnEndEditingKey";
var CPControlActionKey = @"CPControlActionKey", var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
CPControlControlSizeKey = @"CPControlControlSizeKey",
CPControlControlStateKey = @"CPControlControlStateKey",
CPControlFormatterKey = @"CPControlFormatterKey",
CPControlIsEnabledKey = @"CPControlIsEnabledKey",
CPControlSendActionOnKey = @"CPControlSendActionOnKey",
CPControlSendsActionOnEndEditingKey = @"CPControlSendsActionOnEndEditingKey",
CPControlTargetKey = @"CPControlTargetKey",
CPControlValueKey = @"CPControlValueKey",
CPControlBaseWrittingDirectionKey = @"CPControlBaseWrittingDirectionKey";
__Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
@implementation CPControl (CPCoding) @implementation CPControl (CPCoding)
@@ -1110,12 +857,6 @@ var CPControlActionKey = @"CPControlActionKey",
[self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]]; [self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
[self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]]; [self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]];
[self setFormatter:[aCoder decodeObjectForKey:CPControlFormatterKey]];
[self setControlSize:[aCoder decodeIntForKey:CPControlControlSizeKey]];
[self setBaseWritingDirection:[aCoder decodeIntForKey:CPControlBaseWrittingDirectionKey]];
} }
return self; return self;
@@ -1141,18 +882,10 @@ var CPControlActionKey = @"CPControlActionKey",
if (_target !== nil) if (_target !== nil)
[aCoder encodeConditionalObject:_target forKey:CPControlTargetKey]; [aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
if (_action !== nil) if (_action !== NULL)
[aCoder encodeObject:_action forKey:CPControlActionKey]; [aCoder encodeObject:_action forKey:CPControlActionKey];
[aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey]; [aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey];
if (_formatter !== nil)
[aCoder encodeObject:_formatter forKey:CPControlFormatterKey];
[aCoder encodeInt:_controlSize forKey:CPControlControlSizeKey];
[aCoder encodeInt:_baseWritingDirection forKey:CPControlBaseWrittingDirectionKey];
} }
@end @end
+1 -1
View File
@@ -64,7 +64,7 @@ var CPControllerDeclaredKeysKey = @"CPControllerDeclaredKeysKey";
count = _editors.length; count = _editors.length;
for (; index < count; ++index) for (; index < count; ++index)
if (![[_editors objectAtIndex:index] commitEditing]) if (![[_editors objectAtIndex:i] commitEditing])
return NO; return NO;
return YES; return YES;
+3 -7
View File
@@ -94,7 +94,7 @@
domain = ""; domain = "";
#if PLATFORM(DOM) #if PLATFORM(DOM)
document.cookie = _cookieName + "=" + value + expires + "; path=/" + domain; document.cookie = _cookieName+"="+value+expires+"; path=/"+domain;
#else #else
_cookieValue = value; _cookieValue = value;
_expires = expires; _expires = expires;
@@ -111,12 +111,8 @@
for (var i = 0; i < ca.length; i++) for (var i = 0; i < ca.length; i++)
{ {
var c = ca[i]; var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
while (c.charAt(0) == ' ') if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0)
return c.substring(nameEQ.length, c.length);
} }
#endif #endif
return ""; return "";
+12 -100
View File
@@ -22,14 +22,10 @@ Cursor support by browser:
*/ */
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import "CPImage.j"
@global CPApp
var currentCursor = nil, var currentCursor = nil,
cursorStack = [], cursorStack = [],
cursors = {}, cursors = {};
ieCursorMap = {};
@implementation CPCursor : CPObject @implementation CPCursor : CPObject
{ {
@@ -40,20 +36,6 @@ var currentCursor = nil,
BOOL _isSetOnMouseExited @accessors(readwrite, getter=isSetOnMouseExited, setter=setOnMouseExited:); BOOL _isSetOnMouseExited @accessors(readwrite, getter=isSetOnMouseExited, setter=setOnMouseExited:);
} }
+ (void)initialize
{
if (self !== CPCursor)
return;
// IE < 9 does not support some CSS cursors, we map them to supported ones
ieCursorMap = {
"ew-resize": "e-resize",
"ns-resize": "n-resize",
"nesw-resize": "ne-resize",
"nwse-resize": "nw-resize"
};
}
- (id)initWithCSSString:(CPString)aString - (id)initWithCSSString:(CPString)aString
{ {
if (self = [super init]) if (self = [super init])
@@ -62,30 +44,23 @@ var currentCursor = nil,
return self; return self;
} }
/*! // hotspot is supported in CSS3 (but not IE).
Init a cursor with the given image and hotspot. - (id)initWithImage:(CPImage)image hotSpot:(CPPoint)hotSpot
hotspot is supported in CSS3 (but not IE).
*/
- (id)initWithImage:(CPImage)image hotSpot:(CGPoint)hotSpot
{ {
_hotSpot = hotSpot; _hotSpot = hotSpot;
_image = image; _image = image;
return [self initWithCSSString:"url(" + [_image filename] + ")" + hotSpot.x + " " + hotSpot.y + ", auto"]; return [self initWithCSSString:"url(" + [_image filename] + ")" + hotSpot.x + " " + hotSpot.y + ", auto"];
} }
/*! // 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
Init a cursor with the given image and hotspot. This is provided - (id)initWithImage:(CPImage)image foregroundColorHint:(CPColor)foregroundColor backgroundColorHint:(CPColor)backgroundColor hotSpot:(CPPoint)aHotSpot
for compliance with Cocoa. Note that foregroundColor and backgroundColor are ignored
(as they are in Cocoa). See http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSCursor_Class/Reference/Reference.html
*/
- (id)initWithImage:(CPImage)image foregroundColorHint:(CPColor)foregroundColor backgroundColorHint:(CPColor)backgroundColor hotSpot:(CGPoint)aHotSpot
{ {
return [self initWithImage:image hotSpot:aHotSpot]; return [self initWithImage:image hotSpot:hotSpot];
} }
+ (void)hide + (void)hide
{ {
[self _setCursorCSS:@"none"]; // Not supported in IE < 9 [self _setCursorCSS:"none"]; // Not supported in IE
} }
+ (void)unhide + (void)unhide
@@ -117,15 +92,11 @@ var currentCursor = nil,
- (void)push - (void)push
{ {
cursorStack.push(self); currentCursor = cursorStack.push(self);
currentCursor = self;
} }
- (void)set - (void)set
{ {
if (currentCursor === self)
return;
currentCursor = self; currentCursor = self;
#if PLATFORM(DOM) #if PLATFORM(DOM)
@@ -150,7 +121,6 @@ var currentCursor = nil,
{ {
#if PLATFORM(DOM) #if PLATFORM(DOM)
var platformWindows = [[CPPlatformWindow visiblePlatformWindows] allObjects]; var platformWindows = [[CPPlatformWindow visiblePlatformWindows] allObjects];
for (var i = 0, count = [platformWindows count]; i < count; i++) for (var i = 0, count = [platformWindows count]; i < count; i++)
platformWindows[i]._DOMBodyElement.style.cursor = aString; platformWindows[i]._DOMBodyElement.style.cursor = aString;
#endif #endif
@@ -160,40 +130,22 @@ var currentCursor = nil,
+ (CPCursor)_systemCursorWithName:(CPString)cursorName cssString:(CPString)aString hasImage:(BOOL)doesHaveImage + (CPCursor)_systemCursorWithName:(CPString)cursorName cssString:(CPString)aString hasImage:(BOOL)doesHaveImage
{ {
var cursor = cursors[cursorName]; var cursor = cursors[cursorName];
if (typeof cursor === 'undefined')
if (typeof cursor === "undefined")
{ {
var cssString; var cssString;
if (doesHaveImage) if (doesHaveImage)
{ cssString = @"url(" + [[CPBundle bundleForClass:self] resourcePath] + @"/CPCursor/" + cursorName + ".cur), " + aString;
var themeResourcePath = [[[CPApp themeBlend] bundle] resourcePath],
extension = CPBrowserIsEngine(CPInternetExplorerBrowserEngine) ? @"cur" : @"png";
cssString = [CPString stringWithFormat:@"url(%@cursors/%@.%@), %@", themeResourcePath, cursorName, extension, aString];
}
else else
{ cssString = aString
// IE <= 8 does not support some cursors, map them to supported cursors
var ieLessThan9 = CPBrowserIsEngine(CPInternetExplorerBrowserEngine) && !CPFeatureIsCompatible(CPHTMLCanvasFeature);
if (ieLessThan9)
cssString = ieCursorMap[aString] || aString;
else
cssString = aString;
}
cursor = [[CPCursor alloc] initWithCSSString:cssString]; cursor = [[CPCursor alloc] initWithCSSString:cssString];
cursors[cursorName] = cursor; cursors[cursorName] = cursor;
} }
return cursor; return cursor;
} }
+ (CPCursor)arrowCursor + (CPCursor)arrowCursor
{ {
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"default" hasImage:NO]; return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:"default" hasImage:NO];
} }
+ (CPCursor)crosshairCursor + (CPCursor)crosshairCursor
@@ -211,36 +163,6 @@ var currentCursor = nil,
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"pointer" hasImage:NO]; return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"pointer" hasImage:NO];
} }
+ (CPCursor)resizeNorthwestCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nw-resize" hasImage:NO];
}
+ (CPCursor)resizeNorthwestSoutheastCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nwse-resize" hasImage:NO];
}
+ (CPCursor)resizeNortheastCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ne-resize" hasImage:NO];
}
+ (CPCursor)resizeNortheastSouthwestCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nesw-resize" hasImage:NO];
}
+ (CPCursor)resizeSouthwestCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"sw-resize" hasImage:NO];
}
+ (CPCursor)resizeSoutheastCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"se-resize" hasImage:NO];
}
+ (CPCursor)resizeDownCursor + (CPCursor)resizeDownCursor
{ {
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"s-resize" hasImage:NO]; return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"s-resize" hasImage:NO];
@@ -266,21 +188,11 @@ var currentCursor = nil,
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"col-resize" hasImage:NO]; return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"col-resize" hasImage:NO];
} }
+ (CPCursor)resizeEastWestCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ew-resize" hasImage:NO];
}
+ (CPCursor)resizeUpDownCursor + (CPCursor)resizeUpDownCursor
{ {
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"row-resize" hasImage:NO]; return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"row-resize" hasImage:NO];
} }
+ (CPCursor)resizeNorthSouthCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ns-resize" hasImage:NO];
}
+ (CPCursor)operationNotAllowedCursor + (CPCursor)operationNotAllowedCursor
{ {
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"not-allowed" hasImage:NO]; return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"not-allowed" hasImage:NO];
-738
View File
@@ -1,738 +0,0 @@
/* CPDatePicker.j
* AppKit
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPControl.j"
@import "CPFont.j"
@import "CPTextField.j"
@import "_CPDatePickerTextField.j"
@import "_CPDatePickerCalendar.j"
@import <Foundation/CPArray.j>
@import <Foundation/CPObject.j>
@import <Foundation/CPDate.j>
@import <Foundation/CPLocale.j>
@import <Foundation/CPTimeZone.j>
@class CPStepper
@class CPApp
@global CPLocaleLanguageCode
@global CPDateFormatterShortStyle
var CPDatePicker_validateProposedDateValue_timeInterval = 1 << 1;
CPSingleDateMode = 0;
CPRangeDateMode = 1;
CPTextFieldAndStepperDatePickerStyle = 0;
CPClockAndCalendarDatePickerStyle = 1;
CPTextFieldDatePickerStyle = 2;
CPHourMinuteDatePickerElementFlag = 0x000c;
CPHourMinuteSecondDatePickerElementFlag = 0x000e;
CPTimeZoneDatePickerElementFlag = 0x0010;
CPYearMonthDatePickerElementFlag = 0x00c0;
CPYearMonthDayDatePickerElementFlag = 0x00e0;
CPEraDatePickerElementFlag = 0x0100;
/*!
@ingroup appkit
This control displays a datepicker in a Cappuccino application
*/
@implementation CPDatePicker : CPControl
{
BOOL _isBordered @accessors(getter=isBordered, setter=setBordered:);
BOOL _isBezeled @accessors(getter=isBezeled, setter=setBezeled:);
BOOL _drawsBackground @accessors(property=drawsBackground);
CPDate _dateValue @accessors(property=dateValue);
CPDate _minDate @accessors(property=minDate);
CPDate _maxDate @accessors(property=maxDate);
CPFont _textFont @accessors(property=textFont);
CPLocale _locale @accessors(property=locale);
//CPCalendar _calendar @accessors(property=calendar);
CPTimeZone _timeZone @accessors(property=timeZone);
id _delegate @accessors(property=delegate);
CPInteger _datePickerElements @accessors(property=datePickerElements);
CPInteger _datePickerMode @accessors(property=datePickerMode);
CPInteger _datePickerStyle @accessors(property=datePickerStyle);
CPInteger _timeInterval @accessors(property=timeInterval);
BOOL _invokedByUserEvent;
_CPDatePickerTextField _datePickerTextfield;
_CPDatePickerCalendar _datePickerCalendar;
unsigned _implementedCDatePickerDelegateMethods;
}
#pragma mark -
#pragma mark Theme methods
+ (CPString)defaultThemeClass
{
return @"datePicker";
}
+ (CPDictionary)themeAttributes
{
return @{
@"bezel-color": [CPColor clearColor],
@"border-width" : 1.0,
@"border-color": [CPColor clearColor],
@"content-inset": CGInsetMakeZero(),
@"bezel-inset": CGInsetMakeZero(),
@"datepicker-textfield-bezel-color": [CPColor clearColor],
@"min-size-datepicker-textfield": CGSizeMakeZero(),
@"content-inset-datepicker-textfield": CGInsetMakeZero(),
@"content-inset-datepicker-textfield-separator": CGInsetMakeZero(),
@"separator-content-inset": CGInsetMakeZero(),
@"date-hour-margin": 5.0,
@"stepper-margin": 5.0,
@"bezel-color-calendar": [CPColor clearColor],
@"title-text-color": [CPColor blackColor],
@"title-text-shadow-color": [CPColor clearColor],
@"title-text-shadow-offset": CGSizeMakeZero(),
@"title-font": [CPNull null],
@"weekday-text-color": [CPColor blackColor],
@"weekday-text-shadow-color": [CPColor clearColor],
@"weekday-text-shadow-offset": CGSizeMakeZero(),
@"weekday-font": [CPNull null],
@"arrow-image-left": [CPNull null],
@"arrow-image-right": [CPNull null],
@"arrow-image-left-highlighted": [CPNull null],
@"arrow-image-right-highlighted": [CPNull null],
@"arrow-inset": CGInsetMakeZero(),
@"circle-image": [CPNull null],
@"circle-image-highlighted": [CPNull null],
@"tile-text-color": [CPColor blackColor],
@"tile-text-shadow-color": [CPColor clearColor],
@"tile-text-shadow-offset": CGSizeMakeZero(),
@"tile-font": [CPNull null],
@"size-tile": CGSizeMakeZero(),
@"size-calendar": CGSizeMakeZero(),
@"size-header": CGSizeMakeZero(),
@"min-size-calendar": CGSizeMakeZero(),
@"max-size-calendar": CGSizeMakeZero(),
@"bezel-color-clock": [CPColor clearColor],
@"clock-text-color": [CPColor blackColor],
@"clock-text-shadow-color": [CPColor clearColor],
@"clock-text-shadow-offset": CGSizeMakeZero(),
@"clock-font": [CPNull null],
@"second-hand-image": [CPNull null],
@"hour-hand-image": [CPNull null],
@"middle-hand-image": [CPNull null],
@"minute-hand-image": [CPNull null],
@"size-clock": CGSizeMakeZero(),
@"second-hand-size": CGSizeMakeZero(),
@"hour-hand-size": CGSizeMakeZero(),
@"middle-hand-size": CGSizeMakeZero(),
@"minute-hand-size": CGSizeMakeZero(),
};
}
#pragma mark -
#pragma mark Binding methods
+ (Class)_binderClassForBinding:(CPString)theBinding
{
if (theBinding == CPValueBinding || theBinding == CPMinValueBinding || theBinding == CPMaxValueBinding)
return [_CPDatePickerValueBinder class];
return [super _binderClassForBinding:theBinding];
}
- (CPString)_replacementKeyPathForBinding:(CPString)aBinding
{
if (aBinding == CPValueBinding)
return @"dateValue";
if (aBinding == CPMinValueBinding)
return @"minDate";
if (aBinding == CPMaxValueBinding)
return @"maxDate";
return [super _replacementKeyPathForBinding:aBinding];
}
#pragma mark -
#pragma mark Init methods
- (id)initWithFrame:(CGRect)aFrame
{
if (self = [super initWithFrame:aFrame])
{
_drawsBackground = YES;
_datePickerStyle = CPTextFieldAndStepperDatePickerStyle;
_datePickerMode = CPSingleDateMode;
_datePickerElements = CPYearMonthDayDatePickerElementFlag | CPHourMinuteSecondDatePickerElementFlag;
_timeInterval = 0;
_implementedCDatePickerDelegateMethods = 0;
[self setObjectValue:[CPDate date]];
_minDate = [CPDate distantPast];
_maxDate = [CPDate distantFuture];
[self setBezeled:YES];
[self setBordered:YES];
[self _init];
}
return self
}
- (void)_init
{
if (!_locale)
_locale = [CPLocale currentLocale];
_datePickerTextfield = [[_CPDatePickerTextField alloc] initWithFrame:[self bounds] withDatePicker:self];
[_datePickerTextfield setDateValue:_dateValue];
_datePickerCalendar = [[_CPDatePickerCalendar alloc] initWithFrame:[self bounds] withDatePicker:self];
[_datePickerCalendar setDateValue:_dateValue];
// We might have been unarchived in a disabled state.
[_datePickerTextfield setEnabled:[self isEnabled]];
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
#pragma mark -
#pragma mark Control Size
- (void)setControlSize:(CPControlSize)aControlSize
{
[super setControlSize:aControlSize];
if ([self datePickerStyle] == CPTextFieldAndStepperDatePickerStyle || [self datePickerStyle] == CPTextFieldDatePickerStyle)
[self _sizeToControlSize];
}
#pragma mark -
#pragma mark Delegate methods
/*! Set the delegate of the datePicker
@param aDelegate delegate of the datePicker
*/
- (void)setDelegate:(id)aDelegate
{
_delegate = aDelegate;
_implementedCDatePickerDelegateMethods = 0;
// Look if the delegate implements or not the delegate methods
if ([_delegate respondsToSelector:@selector(datePicker:validateProposedDateValue:timeInterval:)])
_implementedCDatePickerDelegateMethods |= CPDatePicker_validateProposedDateValue_timeInterval;
}
#pragma mark -
#pragma mark Layout method
/*! Layout the subviews
*/
- (void)layoutSubviews
{
[super layoutSubviews];
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
{
if (![_datePickerTextfield superview])
[self addSubview:_datePickerTextfield];
if ([_datePickerCalendar superview])
[_datePickerCalendar removeFromSuperview];
[_datePickerTextfield setControlSize:[self controlSize]];
[_datePickerTextfield setNeedsLayout];
[_datePickerTextfield setNeedsDisplay:YES];
}
else
{
if (![_datePickerCalendar superview])
[self addSubview:_datePickerCalendar];
if ([_datePickerTextfield superview])
[_datePickerTextfield removeFromSuperview];
[_datePickerCalendar setNeedsLayout];
[_datePickerCalendar setNeedsDisplay:YES];
}
}
#pragma mark -
#pragma mark Setter
/*! Return the objectValue of the datePicker. The objectValue should take the timeZoneEffect
*/
- (id)objectValue
{
// TODO : add timeZone effect. How to do it because js ???
return _dateValue
}
/*! Set the objectValue of the datePicker. It has to be a CPDate
@param aDateValue the dateValue
*/
- (void)setObjectValue:(CPDate)aValue
{
if (![aValue isKindOfClass:[CPDate class]])
return;
[self setDateValue:aValue];
}
/* Set the dateValue of the datePicker
@param aDateValue the dateValue
*/
- (void)setDateValue:(CPDate)aDateValue
{
if (aDateValue == nil)
return;
_invokedByUserEvent = NO;
[self _setDateValue:aDateValue timeInterval:_timeInterval];
}
/*! Set the dateValue and the timeInterval. This method checks the min and max date of the datePicker also. It will call the delegate if possible.
@param aDateValue the dateValue
@param aTimeInterval the timeInterval
*/
- (void)_setDateValue:(CPDate)aDateValue timeInterval:(CPTimeInterval)aTimeInterval
{
// Make sure to have a valid date and avoid NaN values
if (!isFinite(aDateValue))
{
[CPException raise:CPInvalidArgumentException
reason:@"aDateValue is not valid"];
return;
}
if (_minDate)
aDateValue = new Date (MAX(aDateValue, _minDate));
if (_maxDate)
aDateValue = new Date (MIN(aDateValue, _maxDate));
aTimeInterval = MAX(MIN(aTimeInterval, [_maxDate timeIntervalSinceDate:aDateValue]), [_minDate timeIntervalSinceDate:aDateValue]);
if ([aDateValue isEqualToDate:_dateValue] && aTimeInterval == _timeInterval)
{
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield setDateValue:_dateValue];
else
[_datePickerCalendar setDateValue:_dateValue];
return;
}
if (_implementedCDatePickerDelegateMethods & CPDatePicker_validateProposedDateValue_timeInterval)
{
// constrain timeInterval also
var aStartDateRef = function(x){if (typeof x == 'undefined') return aDateValue; aDateValue = x;};
var aTimeIntervalRef = function(x){if (typeof x == 'undefined') return aTimeInterval; aTimeInterval = x;};
[_delegate datePicker:self validateProposedDateValue:aStartDateRef timeInterval:aTimeIntervalRef];
}
[self willChangeValueForKey:@"objectValue"];
[self willChangeValueForKey:@"dateValue"];
_dateValue = aDateValue;
[super setObjectValue:_dateValue];
[self didChangeValueForKey:@"objectValue"];
[self didChangeValueForKey:@"dateValue"];
[self willChangeValueForKey:@"timeInterval"];
_timeInterval = (_datePickerMode == CPSingleDateMode)? 0 : aTimeInterval;
[self didChangeValueForKey:@"timeInterval"];
if (_invokedByUserEvent)
[self sendAction:[self action] to:[self target]];
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield setDateValue:_dateValue];
else
[_datePickerCalendar setDateValue:_dateValue];
}
/*! Set the minDate of the datePicker
@param aMinDate the minDate
*/
- (void)setMinDate:(CPDate)aMinDate
{
[self willChangeValueForKey:@"minDate"];
_minDate = aMinDate;
[self didChangeValueForKey:@"minDate"];
[self _setDateValue:_dateValue timeInterval:_timeInterval];
}
/*! Set the maxDate of the datePicker
@param aMaxDate the maxDate
*/
- (void)setMaxDate:(CPDate)aMaxDate
{
[self willChangeValueForKey:@"maxDate"];
_maxDate = aMaxDate;
[self didChangeValueForKey:@"maxDate"];
[self _setDateValue:_dateValue timeInterval:_timeInterval];
}
/*! Set the syle of the datePicker
@param aDatePickerStyle the datePicker style
*/
- (void)setDatePickerStyle:(CPInteger)aDatePickerStyle
{
_datePickerStyle = aDatePickerStyle;
[self setControlSize:[self controlSize]];
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
/*! Set the elements of the datePicker
@param aDatePickerElements the datePicker elements
*/
- (void)setDatePickerElements:(CPInteger)aDatePickerElements
{
_datePickerElements = aDatePickerElements;
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
/*! Set the mode of the datePicker
@param aDatePickerMode the datePicker mode
*/
- (void)setDatePickerMode:(CPInteger)aDatePickerMode
{
_datePickerMode = aDatePickerMode;
if (_datePickerMode == CPSingleDateMode)
[self _setDateValue:[self dateValue] timeInterval:0];
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
/*! Set the timeInterval of the datePicker
@param aTimeInterval the timeInterval of the datePicker
*/
- (void)setTimeInterval:(CPInteger)aTimeInterval
{
if (_datePickerMode == CPSingleDateMode)
return;
[self _setDateValue:[self dateValue] timeInterval:aTimeInterval];
}
/*! Set the locale of the datePicker. This update laso the locale of the formatter.
@param aLocale the locale
*/
- (void)setLocale:(CPLocale)aLocale
{
_locale = aLocale;
if (_formatter)
{
[self willChangeValueForKey:@"locale"];
[_formatter setLocale:_locale];
[self didChangeValueForKey:@"locale"];
}
// This will update the textFields (usefull when changing with a date with pm and am)
[_datePickerTextfield setDateValue:_dateValue];
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
/*!
Sets whether the datepicker will have a bezeled border.
@param shouldBeBezeled \c YES means the datepicker will draw a bezeled border
*/
- (void)setBezeled:(BOOL)shouldBeBezeled
{
_isBezeled = shouldBeBezeled;
if (shouldBeBezeled)
[self setThemeState:CPThemeStateBezeled];
else
[self unsetThemeState:CPThemeStateBezeled];
}
/*!
Sets whether the datepicker will have a border drawn. (actually it does nothing)
@param shouldBeBordered \c YES makes the datepicker draw a border
*/
- (void)setBordered:(BOOL)shouldBeBordered
{
_isBordered = shouldBeBordered;
if (shouldBeBordered)
[self setThemeState:CPThemeStateBordered];
else
[self unsetThemeState:CPThemeStateBordered];
}
/*!
Sets the font of the control.
@param aFont
*/
- (void)setTextFont:(CPFont)aFont
{
[self setFont:aFont];
}
/*! Sets the enabled status of the control. Controls that are not enabled can not be used by the user and obtain the CPThemeStateDisabled theme state.
@param a boolean. YES if the control should be enabled, otherwise NO.
*/
- (void)setEnabled:(BOOL)aBoolean
{
[super setEnabled:aBoolean];
[_datePickerTextfield setEnabled:aBoolean];
[_datePickerCalendar setEnabled:aBoolean];
}
/*! Set the background color of the datePicker
@param aColor
*/
- (void)setBackgroundColor:(CPColor)aColor
{
_backgroundColor = aColor;
[self setNeedsLayout];
}
/*! Set the boolean drawsBackgroundColor
@param aBoolean
*/
- (void)setDrawsBackground:(BOOL)aBoolean
{
[self willChangeValueForKey:@"drawsBackground"];
_drawsBackground = aBoolean;
[self didChangeValueForKey:@"drawsBackground"];
[self setNeedsLayout];
}
/*! Set the timeZone
@param aTimeZone
*/
- (void)setTimeZone:(CPTimeZone)aTimeZone
{
[self willChangeValueForKey:@"timeZone"];
_timeZone = aTimeZone;
[self didChangeValueForKey:@"timeZone"];
[self setNeedsLayout];
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield setDateValue:_dateValue];
else
[_datePickerCalendar setDateValue:_dateValue];
}
#pragma mark -
#pragma mark First responder methods
/*! Return YES if style is set to CPTextFieldAndStepperDatePickerStyle or CPTextFieldDatePickerStyle
*/
- (BOOL)becomeFirstResponder
{
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
{
if (![super becomeFirstResponder])
return NO;
[_datePickerTextfield _selectTextFieldWithFlags:[[CPApp currentEvent] modifierFlags]];
return YES;
}
return NO;
}
/*! Return YES
*/
- (BOOL)acceptsFirstResponder
{
return YES;
}
/*! Return YES
*/
- (BOOL)resignFirstResponder
{
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield resignFirstResponder];
return YES;
}
#pragma mark -
#pragma mark getter
/*!
Returns \c YES if the textfield is bezeled.
*/
- (BOOL)isBezeled
{
return [self hasThemeState:CPThemeStateBezeled];
}
/*!
Returns \c YES if the textfield has a border.
*/
- (BOOL)isBordered
{
return [self hasThemeState:CPThemeStateBordered];
}
/*!
Returns the font of the control.
*/
- (CPFont)textFont
{
return [self font];
}
/*! Check if we are in the american format or not. Depending on the locale
*/
- (BOOL)_isAmericanFormat
{
return [[_locale objectForKey:CPLocaleCountryCode] isEqualToString:@"US"];
}
#pragma mark -
#pragma mark Key event
/*! Key down event
@param anEvent
*/
- (void)keyDown:(CPEvent)anEvent
{
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield keyDown:anEvent];
}
@end
var CPDatePickerModeKey = @"CPDatePickerModeKey",
CPIntervalKey = @"CPIntervalKey",
CPMinDateKey = @"CPMinDateKey",
CPMaxDateKey = @"CPMaxDateKey",
CPBackgroundColorKey = @"CPBackgroundColorKey",
CPDrawsBackgroundKey = @"CPDrawsBackgroundKey",
CPTextFontKey = @"CPTextFontKey",
CPDatePickerElementsKey = @"CPDatePickerElementsKey",
CPDatePickerStyleKey = @"CPDatePickerStyleKey",
CPLocaleKey = @"CPLocaleKey",
CPBorderedKey = @"CPBorderedKey",
CPDateValueKey = @"CPDateValueKey";
@implementation CPDatePicker (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_textFont = [aCoder decodeObjectForKey:CPTextFontKey];
_minDate = [aCoder decodeObjectForKey:CPMinDateKey] || [CPDate distantPast];
_maxDate = [aCoder decodeObjectForKey:CPMaxDateKey] || [CPDate distantFuture];
_timeInterval = [aCoder decodeDoubleForKey:CPIntervalKey];
_datePickerMode = [aCoder decodeIntForKey:CPDatePickerModeKey];
_datePickerElements = [aCoder decodeIntForKey:CPDatePickerElementsKey];
[self setDatePickerStyle:[aCoder decodeIntForKey:CPDatePickerStyleKey]];
_locale = [aCoder decodeObjectForKey:CPLocaleKey];
_dateValue = [aCoder decodeObjectForKey:CPDateValueKey];
_backgroundColor = [aCoder decodeObjectForKey:CPBackgroundColorKey];
_drawsBackground = [aCoder decodeBoolForKey:CPDrawsBackgroundKey];
_isBordered = [aCoder decodeBoolForKey:CPBorderedKey];
[self _init];
}
return self
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
// FIXME Do we need to encode _datePickerTextfield and _datePickerCalendar? As subviews they'll be encoded, but when we decode we recreate them anyhow.
[super encodeWithCoder:aCoder];
[aCoder encodeDouble:_timeInterval forKey:CPIntervalKey];
[aCoder encodeInt:_datePickerMode forKey:CPDatePickerModeKey];
[aCoder encodeInt:_datePickerStyle forKey:CPDatePickerStyleKey];
[aCoder encodeInt:_datePickerElements forKey:CPDatePickerElementsKey];
[aCoder encodeObject:_minDate forKey:CPMinDateKey];
[aCoder encodeObject:_maxDate forKey:CPMaxDateKey];
[aCoder encodeObject:_dateValue forKey:CPDateValueKey];;
[aCoder encodeObject:_textFont forKey:CPTextFontKey];
[aCoder encodeObject:_locale forKey:CPLocaleKey];
[aCoder encodeObject:_backgroundColor forKey:CPBackgroundColorKey];
[aCoder encodeObject:_drawsBackground forKey:CPDrawsBackgroundKey];
[aCoder encodeObject:_isBordered forKey:CPBorderedKey];
}
@end
@implementation _CPDatePickerValueBinder : CPBinder
{
}
@end
@implementation CPDate (CPDatePickerAdditions)
- (int)_daysInMonth
{
return 32 - new Date(self.getFullYear(), self.getMonth(), 32).getDate();
}
- (void)_resetToMidnight
{
self.setHours(0);
self.setMinutes(0);
self.setSeconds(0);
self.setMilliseconds(0);
}
- (void)_resetToLastSeconds
{
self.setHours(23);
self.setMinutes(59);
self.setSeconds(59);
self.setMilliseconds(99);
}
@end
File diff suppressed because it is too large Load Diff
-313
View File
@@ -1,313 +0,0 @@
/*
* _CPDatePickerClock.j
* AppKit
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPView.j"
@import "CPTextField.j"
@import "CPImageView.j"
@import "CALayer.j"
@class _CPCibCustomResource
@class CPDatePicker
@global CPHourMinuteSecondDatePickerElementFlag
@global CPTextFieldAndStepperDatePickerStyle
@global CPTextFieldDatePickerStyle
var RADIANS = Math.PI / 180;
@implementation _CPDatePickerClock : CPView
{
BOOL _isEnabled;
CALayer _rootLayer;
CALayer _hourHandLayer;
CALayer _minuteHandLayer;
CALayer _secondHandLayer;
CALayer _middleHandLayer;
CPDatePicker _datePicker;
CPTextField _PMAMTextField;
}
#pragma mark -
#pragma mark Init methods
- (id)initWithFrame:(CGRect)aFrame datePicker:(CPDatePicker)aDatePicker
{
if (self = [super initWithFrame:aFrame])
{
_datePicker = aDatePicker;
_PMAMTextField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-font" inState:CPThemeStateNormal] forThemeAttribute:@"font" inState:CPThemeStateNormal];
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-color" inState:CPThemeStateNormal];
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-shadow-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateNormal];
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-shadow-offset" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateNormal];
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-font" inState:CPThemeStateDisabled] forThemeAttribute:@"font" inState:CPThemeStateDisabled];
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-shadow-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateDisabled];
[_PMAMTextField setValue:[_datePicker valueForThemeAttribute:@"clock-text-shadow-offset" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateDisabled];
[self addSubview:_PMAMTextField];
var middleHandSize = [_datePicker valueForThemeAttribute:@"middle-hand-size"],
minuteHandSize = [_datePicker valueForThemeAttribute:@"minute-hand-size"],
hourHandSize = [_datePicker valueForThemeAttribute:@"hour-hand-size"],
secondHandSize = [_datePicker valueForThemeAttribute:@"second-hand-size"];
// We use layer to make the rotation possible
_hourHandLayer = [[HandLayer alloc] initWithSize:hourHandSize];
[_hourHandLayer setBounds:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height)];
[_hourHandLayer setAnchorPoint:CGPointMakeZero()];
[_hourHandLayer setPosition:CGPointMake(0.0, 0.0)];
_minuteHandLayer = [[HandLayer alloc] initWithSize:minuteHandSize];
[_minuteHandLayer setBounds:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height)];
[_minuteHandLayer setAnchorPoint:CGPointMakeZero()];
[_minuteHandLayer setPosition:CGPointMake(0.0, 0.0)];
_secondHandLayer = [[HandLayer alloc] initWithSize:secondHandSize];
[_secondHandLayer setBounds:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height)];
[_secondHandLayer setAnchorPoint:CGPointMakeZero()];
[_secondHandLayer setPosition:CGPointMake(0.0, 0.0)];
_middleHandLayer = [[HandLayer alloc] initWithSize:middleHandSize];
[_middleHandLayer setBounds:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height)];
[_middleHandLayer setAnchorPoint:CGPointMakeZero()];
[_middleHandLayer setPosition:CGPointMake(0.0, 0.0)];
_rootLayer = [CALayer layer];
[self setWantsLayer:YES];
[self setLayer:_rootLayer];
[_hourHandLayer setNeedsDisplay];
[_middleHandLayer setNeedsDisplay];
[_secondHandLayer setNeedsDisplay];
[_minuteHandLayer setNeedsDisplay];
[_rootLayer addSublayer:_hourHandLayer];
[_rootLayer addSublayer:_minuteHandLayer];
[_rootLayer addSublayer:_secondHandLayer];
[_rootLayer addSublayer:_middleHandLayer];
[_rootLayer setNeedsDisplay];
}
return self;
}
#pragma mark -
#pragma mark Layout methods
- (void)layoutSubviews
{
if ([_datePicker datePickerStyle] == CPTextFieldAndStepperDatePickerStyle || [_datePicker datePickerStyle] == CPTextFieldDatePickerStyle)
return;
[super layoutSubviews];
var bounds = [self bounds],
dateValue = [[_datePicker dateValue] copy];
[dateValue _dateWithTimeZone:[_datePicker timeZone]];
[self setBackgroundColor:[_datePicker currentValueForThemeAttribute:@"bezel-color-clock"]];
[_middleHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"middle-hand-image"]];
[_hourHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"hour-hand-image"]];
[_minuteHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"minute-hand-image"]];
[_secondHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"second-hand-image"]];
if ([_datePicker _isAmericanFormat])
{
if (dateValue.getHours() > 11)
[_PMAMTextField setStringValue:@"PM"];
else
[_PMAMTextField setStringValue:@"AM"];
[_PMAMTextField sizeToFit];
[_PMAMTextField setFrameOrigin:CGPointMake(bounds.size.width / 2 - [_PMAMTextField frameSize].width / 2, bounds.size.height / 2 + 15)];
[_PMAMTextField setHidden:NO];
}
else
{
[_PMAMTextField setHidden:YES];
}
[_hourHandLayer setRotationRadians:[self _hourPositionRadianForDate:dateValue]];
[_minuteHandLayer setRotationRadians:[self _minutePositionRadianForDate:dateValue]];
[_secondHandLayer setRotationRadians:[self _secondPositionRadianForDate:dateValue]];
[_PMAMTextField setEnabled:_isEnabled];
[_hourHandLayer setEnabled:_isEnabled];
[_middleHandLayer setEnabled:_isEnabled];
[_secondHandLayer setEnabled:_isEnabled];
[_minuteHandLayer setEnabled:_isEnabled];
// Check if we have to display the hand second
if (([_datePicker datePickerElements] & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
[_secondHandLayer setHidden:NO];
else
[_secondHandLayer setHidden:YES];
[_rootLayer setNeedsDisplay];
}
#pragma mark -
#pragma mark Accessors
- (float)_hourPositionRadianForDate:(CPDate)aDate
{
var hours = aDate.getHours() + aDate.getMinutes() / 60;
return (360 * hours / 12) * RADIANS;
}
- (float)_secondPositionRadianForDate:(CPDate)aDate
{
return (360 * aDate.getSeconds() / 60) * RADIANS;
}
- (float)_minutePositionRadianForDate:(CPDate)aDate
{
var minutes = aDate.getMinutes() + aDate.getSeconds() / 60;
return (360 * minutes / 60) * RADIANS;
}
- (void)setEnabled:(BOOL)shouldEnable
{
shouldEnable = !!shouldEnable;
if (shouldEnable === _isEnabled)
return;
_isEnabled = shouldEnable;
[self setNeedsLayout];
// FIXME: This is a workaround for an apparent bug in CALayer.
// Without pumping the event loop, the sublayers of _rootLayer
// (the hands) are not redrawn until an event occurs.
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
}
@end
@implementation HandLayer : CALayer
{
BOOL _isEnabled @accessors(setter=setEnabled:, getter=isEnabled);
CPImage _image;
CALayer _imageLayer;
float _rotationRadians;
}
#pragma mark -
#pragma mark Init methods
- (id)initWithSize:(CGSize)aSize
{
if (self = [super init])
{
_isEnabled = YES;
_imageLayer = [CALayer layer];
_rotationRadians = 0;
[_imageLayer setDelegate:self];
[_imageLayer setBounds:CGRectMake(0.0, 0.0, aSize.width, aSize.height)];
[self addSublayer:_imageLayer];
}
return self;
}
#pragma mark -
#pragma mark Setter Getter methods
/*!
Set the bounds of the layer. The imageLayer will be at the center of this bounds.
*/
- (void)setBounds:(CGRect)aRect
{
[super setBounds:aRect];
[_imageLayer setPosition:CGPointMake(CGRectGetMidX(aRect), CGRectGetMidY(aRect))];
}
- (void)setImage:(CPImage)anImage
{
if (_image === anImage)
return;
if ([anImage isKindOfClass:[_CPCibCustomResource class]])
_image = [anImage imageFromCoder:nil];
else
_image = anImage;
[_imageLayer setNeedsDisplay];
}
- (void)setRotationRadians:(float)radians
{
if (_rotationRadians === radians)
return;
_rotationRadians = radians;
[_imageLayer setAffineTransform:CGAffineTransformScale(
CGAffineTransformMakeRotation(_rotationRadians),
1.0, 1.0)];
}
- (void)setEnabled:(BOOL)shouldEnable
{
shouldEnable = !!shouldEnable;
if (_isEnabled === shouldEnable)
return;
_isEnabled = shouldEnable;
[self setNeedsDisplay];
[_imageLayer setNeedsDisplay];
}
- (void)imageDidLoad:(CPImage)anImage
{
[_imageLayer setNeedsDisplay];
}
- (void)drawLayer:(CALayer)aLayer inContext:(CGContext)aContext
{
if ([_image loadStatus] != CPImageLoadStatusCompleted)
[_image setDelegate:self];
else
CGContextDrawImage(aContext, [aLayer bounds], _image);
}
@end
File diff suppressed because it is too large Load Diff
-168
View File
@@ -1,168 +0,0 @@
/*
* CPDictionaryController.j
* AppKit
*
* Adapted from Cocotron, by Johannes Fortmann
*
* Created by Blair Duncan
* Copyright 2013, SGL Studio, BBDO Toronto All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPArrayController.j"
@implementation CPDictionaryController : CPArrayController
{
CPDictionary _contentDictionary;
CPArray _includedKeys @accessors(property=includedKeys);
CPArray _excludedKeys @accessors(property=excludedKeys);
CPString _initialKey @accessors(property=initialKey);
id _initialValue @accessors(property=initialValue);
}
- (id)init
{
self = [super init];
if (self)
{
_initialKey = @"key";
_initialValue = @"value";
}
return self;
}
- (id)newObject
{
var keys = [_contentDictionary allKeys],
newKey = _initialKey,
count = 0;
if ([keys containsObject:newKey])
while ([keys containsObject:newKey])
newKey = [CPString stringWithFormat:@"%@%i", _initialKey, ++count];
return [self _newObjectWithKey:newKey value:_initialValue];
}
- (id)_newObjectWithKey:(CPString)aKey value:(id)aValue
{
var aNewObject = [_CPDictionaryControllerKeyValuePair new];
aNewObject._dictionary = _contentDictionary;
aNewObject._controller = self;
aNewObject._key = aKey;
if (aValue !== nil)
[aNewObject setValue:aValue];
return aNewObject;
}
- (CPDictionary)contentDictionary
{
return _contentDictionary;
}
- (void)setContentDictionary:(CPDictionary)aDictionary
{
if (aDictionary == _contentDictionary)
return;
if ([aDictionary isKindOfClass:[CPDictionary class]])
_contentDictionary = aDictionary;
else
_contentDictionary = nil;
var array = [CPArray array],
allKeys = [_contentDictionary allKeys];
[allKeys addObjectsFromArray:_includedKeys];
var iter = [[CPSet setWithArray:allKeys] objectEnumerator],
obj;
while ((obj = [iter nextObject]) !== nil)
if (![_excludedKeys containsObject:obj])
[array addObject:[self _newObjectWithKey:obj value:nil]];
[super setContent:array];
}
@end
var CPIncludedKeys = @"CPIncludedKeys",
CPExcludedKeys = @"CPExcludedKeys";
@implementation CPDictionaryController (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_includedKeys = [aCoder decodeObjectForKey:CPIncludedKeys];
_excludedKeys = [aCoder decodeObjectForKey:CPExcludedKeys];
_initialKey = @"key";
_initialValue = @"value";
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:_includedKeys forKey:CPIncludedKeys];
[aCoder encodeObject:_excludedKeys forKey:CPExcludedKeys];
}
@end
@implementation _CPDictionaryControllerKeyValuePair : CPObject
{
CPString _key @accessors(property=key);
CPDictionary _dictionary @accessors(property=dictionary);
CPDictionaryController _controller @accessors(property=controller);
}
- (id)value
{
return [_dictionary objectForKey:_key];
}
- (void)setValue:(id)aValue
{
[_dictionary setObject:aValue forKey:_key];
}
- (BOOL)isExplicitlyIncluded
{
return [[_controller _includedKeys] containsObject:_key];
}
@end
+44 -43
View File
@@ -23,16 +23,12 @@
@import <Foundation/CPString.j> @import <Foundation/CPString.j>
@import <Foundation/CPArray.j> @import <Foundation/CPArray.j>
@import "CPAlert.j" @import "CPApplication.j"
@import "CPResponder.j" @import "CPResponder.j"
@import "CPSavePanel.j" @import "CPSavePanel.j"
@import "CPViewController.j" @import "CPViewController.j"
@import "CPWindowController.j" @import "CPWindowController.j"
@class CPDocumentController
@global CPApp
/* /*
@global @global
@@ -129,7 +125,7 @@ var CPDocumentUntitledCount = 0;
if (self) if (self)
{ {
_windowControllers = []; _windowControllers = [];
_viewControllersForWindowControllers = @{}; _viewControllersForWindowControllers = [CPDictionary dictionary];
_hasUndoManager = YES; _hasUndoManager = YES;
_changeCount = 0; _changeCount = 0;
@@ -146,7 +142,7 @@ var CPDocumentUntitledCount = 0;
@param anError not used @param anError not used
@return the initialized document @return the initialized document
*/ */
- (id)initWithType:(CPString)aType error:(/*{*/CPError/*}*/)anError - (id)initWithType:(CPString)aType error:({CPError})anError
{ {
self = [self init]; self = [self init];
@@ -215,7 +211,7 @@ var CPDocumentUntitledCount = 0;
@throws CPUnsupportedMethodException if this method hasn't been overridden by the subclass @throws CPUnsupportedMethodException if this method hasn't been overridden by the subclass
@return the document data @return the document data
*/ */
- (CPData)dataOfType:(CPString)aType error:(/*{*/CPError/*}*/)anError - (CPData)dataOfType:(CPString)aType error:({CPError})anError
{ {
[CPException raise:CPUnsupportedMethodException [CPException raise:CPUnsupportedMethodException
reason:"dataOfType:error: must be overridden by the document subclass."]; reason:"dataOfType:error: must be overridden by the document subclass."];
@@ -244,6 +240,11 @@ var CPDocumentUntitledCount = 0;
{ {
} }
- (CPWindowController)firstEligibleExistingWindowController
{
return nil;
}
// Creating and managing window controllers // Creating and managing window controllers
/*! /*!
Creates the window controller for this document. Creates the window controller for this document.
@@ -256,7 +257,6 @@ var CPDocumentUntitledCount = 0;
- (void)makeViewAndWindowControllers - (void)makeViewAndWindowControllers
{ {
var viewCibName = [self viewCibName], var viewCibName = [self viewCibName],
windowCibName = [self windowCibName],
viewController = nil, viewController = nil,
windowController = nil; windowController = nil;
@@ -264,21 +264,31 @@ var CPDocumentUntitledCount = 0;
if ([viewCibName length]) if ([viewCibName length])
viewController = [[CPViewController alloc] initWithCibName:viewCibName bundle:nil owner:self]; viewController = [[CPViewController alloc] initWithCibName:viewCibName bundle:nil owner:self];
// From a cib if we have one. // If we have a view controller, check if we have a free window for it.
if ([windowCibName length]) if (viewController)
windowController = [[CPWindowController alloc] initWithWindowCibName:windowCibName owner:self]; windowController = [self firstEligibleExistingWindowController];
// If not you get a standard window capable of displaying multiple documents and view // If not, create one.
else if (viewController) if (!windowController)
{ {
var view = [viewController view], var windowCibName = [self windowCibName];
viewFrame = [view frame];
viewFrame.origin = CGPointMake(50, 50); // From a cib if we have one.
if ([windowCibName length])
windowController = [[CPWindowController alloc] initWithWindowCibName:windowCibName owner:self];
var theWindow = [[CPWindow alloc] initWithContentRect:viewFrame styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask]; // If not you get a standard window capable of displaying multiple documents and view
else if (viewController)
{
var view = [viewController view],
viewFrame = [view frame];
windowController = [[CPWindowController alloc] initWithWindow:theWindow]; viewFrame.origin = CGPointMake(50, 50);
var theWindow = [[CPWindow alloc] initWithContentRect:viewFrame styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask];
windowController = [[CPWindowController alloc] initWithWindow:theWindow];
}
} }
if (windowController && viewController) if (windowController && viewController)
@@ -515,9 +525,7 @@ var CPDocumentUntitledCount = 0;
alert("There was an error retrieving the document."); alert("There was an error retrieving the document.");
var theDelegate = session.delegate; objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo);
} }
else else
{ {
@@ -542,9 +550,7 @@ var CPDocumentUntitledCount = 0;
_writeRequest = nil; _writeRequest = nil;
var theDelegate = session.delegate; objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo);
[self _sendDocumentSavedNotification:NO]; [self _sendDocumentSavedNotification:NO];
} }
} }
@@ -557,15 +563,14 @@ var CPDocumentUntitledCount = 0;
*/ */
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData - (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
{ {
var session = aConnection.session, var session = aConnection.session;
theDelegate = session.delegate;
// READ // READ
if (aConnection == _readConnection) if (aConnection == _readConnection)
{ {
[self readFromData:[CPData dataWithRawString:aData] ofType:session.fileType error:nil]; [self readFromData:[CPData dataWithRawString:aData] ofType:session.fileType error:nil];
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, YES, session.contextInfo); objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
} }
else else
{ {
@@ -574,7 +579,7 @@ var CPDocumentUntitledCount = 0;
_writeRequest = nil; _writeRequest = nil;
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, YES, session.contextInfo); objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
[self _sendDocumentSavedNotification:YES]; [self _sendDocumentSavedNotification:YES];
} }
} }
@@ -585,11 +590,10 @@ var CPDocumentUntitledCount = 0;
*/ */
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError - (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
{ {
var session = aConnection.session, var session = aConnection.session;
theDelegate = session.delegate;
if (_readConnection == aConnection) if (_readConnection == aConnection)
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo); objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
else else
{ {
@@ -603,7 +607,7 @@ var CPDocumentUntitledCount = 0;
alert("There was an error saving the document."); alert("There was an error saving the document.");
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo); objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
[self _sendDocumentSavedNotification:NO]; [self _sendDocumentSavedNotification:NO];
} }
} }
@@ -864,24 +868,21 @@ var CPDocumentUntitledCount = 0;
[self canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_document:shouldClose:context:) contextInfo:{delegate:delegate, selector:selector, context:info}]; [self canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_document:shouldClose:context:) contextInfo:{delegate:delegate, selector:selector, context:info}];
else if ([delegate respondsToSelector:selector]) else if ([delegate respondsToSelector:selector])
delegate.isa.objj_msgSend3(delegate, selector, self, YES, info); objj_msgSend(delegate, selector, self, YES, info);
} }
- (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context - (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
{ {
var theDelegate = context.delegate;
if (aDocument === self && shouldClose) if (aDocument === self && shouldClose)
[self close]; [self close];
if (theDelegate != null) objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.context);
theDelegate.isa.objj_msgSend3(theDelegate, context.selector, aDocument, shouldClose, context.context);
} }
- (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context - (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context
{ {
if (![self isDocumentEdited]) if (![self isDocumentEdited])
return [aDelegate respondsToSelector:aSelector] && aDelegate.isa.objj_msgSend3(aDelegate, aSelector, self, YES, context); return [aDelegate respondsToSelector:aSelector] && objj_msgSend(aDelegate, aSelector, self, YES, context);
_canCloseAlert = [[CPAlert alloc] init]; _canCloseAlert = [[CPAlert alloc] init];
@@ -910,8 +911,8 @@ var CPDocumentUntitledCount = 0;
if (returnCode === 0) if (returnCode === 0)
[self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context]; [self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context];
else if (delegate != null) else
delegate.isa.objj_msgSend3(delegate, selector, self, returnCode === 2, context); objj_msgSend(delegate, selector, self, returnCode === 2, context);
_canCloseAlert = nil; _canCloseAlert = nil;
} }
@@ -921,9 +922,9 @@ var CPDocumentUntitledCount = 0;
var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo) var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo)
{ {
return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo }; return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo };
}; }
var _CPSaveSessionMake = function(anAbsoluteURL, aSaveOperation, aChangeCount, aDelegate, aDidSaveSelector, aContextInfo, aConnection) var _CPSaveSessionMake = function(anAbsoluteURL, aSaveOperation, aChangeCount, aDelegate, aDidSaveSelector, aContextInfo, aConnection)
{ {
return { absoluteURL:anAbsoluteURL, saveOperation:aSaveOperation, changeCount:aChangeCount, delegate:aDelegate, didSaveSelector:aDidSaveSelector, contextInfo:aContextInfo, connection:aConnection }; return { absoluteURL:anAbsoluteURL, saveOperation:aSaveOperation, changeCount:aChangeCount, delegate:aDelegate, didSaveSelector:aDidSaveSelector, contextInfo:aContextInfo, connection:aConnection };
}; }
+8 -49
View File
@@ -25,10 +25,6 @@
@import "CPDocument.j" @import "CPDocument.j"
@import "CPOpenPanel.j" @import "CPOpenPanel.j"
@import "CPMenuItem.j"
@import "CPWindowController.j"
@global CPApp
var CPSharedDocumentController = nil; var CPSharedDocumentController = nil;
@@ -128,7 +124,7 @@ var CPSharedDocumentController = nil;
@param anError not used @param anError not used
@return the created document @return the created document
*/ */
- (CPDocument)makeUntitledDocumentOfType:(CPString)aType error:(/*{*/CPError/*}*/)anError - (CPDocument)makeUntitledDocumentOfType:(CPString)aType error:({CPError})anError
{ {
return [[[self documentClassForType:aType] alloc] initWithType:aType error:anError]; return [[[self documentClassForType:aType] alloc] initWithType:aType error:anError];
} }
@@ -148,7 +144,7 @@ var CPSharedDocumentController = nil;
{ {
var type = [self typeForContentsOfURL:anAbsoluteURL error:anError]; var type = [self typeForContentsOfURL:anAbsoluteURL error:anError];
result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:@{ @"shouldDisplay": shouldDisplay }]; result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:[CPDictionary dictionaryWithObject:shouldDisplay forKey:@"shouldDisplay"]];
[self addDocument:result]; [self addDocument:result];
@@ -253,14 +249,6 @@ var CPSharedDocumentController = nil;
return _documents; return _documents;
} }
/*!
Returns the CPDocument object associated with the main window.
*/
- (CPDocument)currentDocument
{
return [[[CPApp mainWindow] windowController] document];
}
/*! /*!
Adds \c aDocument under the control of the receiver. Adds \c aDocument under the control of the receiver.
@param aDocument the document to add @param aDocument the document to add
@@ -279,33 +267,6 @@ var CPSharedDocumentController = nil;
[_documents removeObjectIdenticalTo:aDocument]; [_documents removeObjectIdenticalTo:aDocument];
} }
/*!
Returns the document object whose window controller
owns a specified window.
*/
- (CPDocument)documentForWindow:(CPWindow)aWindow
{
return [[aWindow windowController] document];
}
/*!
Returns a Boolean value that indicates whether the receiver
has any documents with unsaved changes.
*/
- (BOOL)hasEditedDocuments
{
var iter = [_documents objectEnumerator],
obj;
while ((obj = [iter nextObject]) !== nil)
{
if ([obj isDocumentEdited])
return YES;
}
return NO;
}
- (CPString)defaultType - (CPString)defaultType
{ {
return [_documentTypes[0] objectForKey:@"CPBundleTypeName"]; return [_documentTypes[0] objectForKey:@"CPBundleTypeName"];
@@ -378,10 +339,10 @@ var CPSharedDocumentController = nil;
- (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info - (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info
{ {
var context = { var context = {
delegate: aDelegate, delegate: aDelegate,
selector: didCloseSelector, selector: didCloseSelector,
context: info context: info
}; };
[self _closeDocumentsStartingWith:nil shouldClose:YES context:context]; [self _closeDocumentsStartingWith:nil shouldClose:YES context:context];
} }
@@ -402,10 +363,8 @@ var CPSharedDocumentController = nil;
} }
} }
var theDelegate = context.delegate; if ([context.delegate respondsToSelector:context.selector])
objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context);
if ([theDelegate respondsToSelector:context.selector])
theDelegate.isa.objj_msgSend3(theDelegate, context.selector, self, [[self documents] count] === 0, context.context);
} }
@end @end
+64 -37
View File
@@ -20,17 +20,22 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "CPDragServer_Constants.j" @import "CPApplication.j"
@import "CPEvent.j" @import "CPEvent.j"
@import "CPImageView.j" @import "CPImageView.j"
@import "CPPasteboard.j" @import "CPPasteboard.j"
@import "CPView.j" @import "CPView.j"
@import "CPWindow_Constants.j" @import "CPWindow.j"
@class CPWindow // This file is imported by CPWindow.j
@class _CPDOMDataTransferPasteboard
@global CPApp CPDragOperationNone = 0;
CPDragOperationCopy = 1 << 1;
CPDragOperationLink = 1 << 1;
CPDragOperationGeneric = 1 << 2;
CPDragOperationPrivate = 1 << 3;
CPDragOperationMove = 1 << 4;
CPDragOperationDelete = 1 << 5;
CPDragOperationEvery = -1;
#define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window]) #define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window])
@@ -67,7 +72,7 @@ var CPDragServerSource = nil,
- (unsigned)draggingSourceOperationMask - (unsigned)draggingSourceOperationMask
*/ */
- (CGPoint)draggingLocation - (CPPoint)draggingLocation
{ {
return [[CPDragServer sharedDragServer] draggingLocation]; return [[CPDragServer sharedDragServer] draggingLocation];
} }
@@ -163,7 +168,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
if (self) if (self)
{ {
_draggedWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessWindowMask]; _draggedWindow = [[CPWindow alloc] initWithContentRect:_CGRectMakeZero() styleMask:CPBorderlessWindowMask];
[_draggedWindow setLevel:CPDraggingWindowLevel]; [_draggedWindow setLevel:CPDraggingWindowLevel];
} }
@@ -201,12 +206,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
- (void)draggingSourceUpdatedWithGlobalLocation:(CGPoint)aGlobalLocation - (void)draggingSourceUpdatedWithGlobalLocation:(CGPoint)aGlobalLocation
{ {
if (![CPPlatform supportsDragAndDrop]) if (![CPPlatform supportsDragAndDrop])
{ [_draggedWindow setFrameOrigin:_CGPointMake(aGlobalLocation.x - _draggingOffset.width, aGlobalLocation.y - _draggingOffset.height)];
var frame = [_draggedWindow frame];
frame.origin.x = aGlobalLocation.x - _draggingOffset.width;
frame.origin.y = aGlobalLocation.y - _draggingOffset.height;
[_draggedWindow _setFrame:frame display:YES animate:NO constrainWidth:NO constrainHeight:NO];
}
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_movedTo_) if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_movedTo_)
[_draggingSource draggedImage:[_draggedView image] movedTo:aGlobalLocation]; [_draggingSource draggedImage:[_draggedView image] movedTo:aGlobalLocation];
@@ -253,7 +253,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
_draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval _draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval
target:self target:self
selector:@selector(_sendPeriodicDraggingUpdate:) selector:@selector(_sendPeriodicDraggingUpdate:)
userInfo:@{ "platformWindow":aPlatformWindow, "location":aLocation } userInfo:[CPDictionary dictionaryWithJSObject:{platformWindow:aPlatformWindow, location:aLocation}]
repeats:NO]; repeats:NO];
var scrollView = [_draggingDestination isKindOfClass:[CPView class]] ? [_draggingDestination enclosingScrollView] : nil; var scrollView = [_draggingDestination isKindOfClass:[CPView class]] ? [_draggingDestination enclosingScrollView] : nil;
@@ -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,17 +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); var scrollPoint = _CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY);
[contentView scrollToPoint:scrollPoint]; [contentView scrollToPoint:scrollPoint];
[[scrollView _headerView] scrollPoint:scrollPoint]; [[scrollView _headerView] scrollPoint:scrollPoint];
@@ -370,26 +370,22 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
if (mouseDownWindow) if (mouseDownWindow)
mouseDownEventLocation = [mouseDownWindow convertBaseToGlobal:mouseDownEventLocation]; mouseDownEventLocation = [mouseDownWindow convertBaseToGlobal:mouseDownEventLocation];
_draggingOffset = CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y); _draggingOffset = _CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y);
} }
else else
_draggingOffset = CGSizeMakeZero(); _draggingOffset = _CGSizeMakeZero();
if ([CPPlatform isBrowser]) if ([CPPlatform isBrowser])
[_draggedWindow setPlatformWindow:[aWindow platformWindow]]; [_draggedWindow setPlatformWindow:[aWindow platformWindow]];
[aView setFrameOrigin:CGPointMakeZero()]; [aView setFrameOrigin:_CGPointMakeZero()];
var mouseLocation = [CPEvent mouseLocation], var mouseLocation = [CPEvent mouseLocation];
viewSize = [aView frameSize],
startDragLocationX = mouseLocation.x - _draggingOffset.width,
startDragLocationY = mouseLocation.y - _draggingOffset.height,
draggedWindowFrame = CGRectMake(startDragLocationX, startDragLocationY, viewSize.width, viewSize.height);
// Place it where the mouse pointer is. // Place it where the mouse pointer is.
_startDragLocation = CGPointMake(startDragLocationX, startDragLocationY); _startDragLocation = _CGPointMake(mouseLocation.x - _draggingOffset.width, mouseLocation.y - _draggingOffset.height);
[_draggedWindow setFrameOrigin:_startDragLocation];
[_draggedWindow _setFrame:draggedWindowFrame display:YES animate:NO constrainWidth:NO constrainHeight:NO]; [_draggedWindow setFrameSize:[aView frame].size];
[[_draggedWindow contentView] addSubview:aView]; [[_draggedWindow contentView] addSubview:aView];
@@ -438,7 +434,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
var imageSize = [anImage size]; var imageSize = [anImage size];
if (!_imageView) if (!_imageView)
_imageView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, imageSize.width, imageSize.height)]; _imageView = [[CPImageView alloc] initWithFrame:_CGRectMake(0.0, 0.0, imageSize.width, imageSize.height)];
[_imageView setImage:anImage]; [_imageView setImage:anImage];
@@ -465,7 +461,6 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
else if (type === CPKeyDown) else if (type === CPKeyDown)
{ {
var characters = [anEvent characters]; var characters = [anEvent characters];
if (characters === CPEscapeFunctionKey) if (characters === CPEscapeFunctionKey)
{ {
_dragOperation = CPDragOperationNone; _dragOperation = CPDragOperationNone;
@@ -482,7 +477,39 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
// If we're not a mouse up, then we're going to want to grab the next event. // If we're not a mouse up, then we're going to want to grab the next event.
[CPApp setTarget:self selector:@selector(trackDragging:) [CPApp setTarget:self selector:@selector(trackDragging:)
forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPKeyDownMask forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPKeyDownMask
untilDate:nil inMode:0 dequeue:YES]; untilDate:nil inMode:0 dequeue:NO];
}
@end
@implementation CPWindow (CPDraggingAdditions)
/* @ignore */
- (id)_dragHitTest:(CGPoint)aPoint pasteboard:(CPPasteboard)aPasteboard
{
// If none of our views or ourselves has registered for drag events...
if (!_inclusiveRegisteredDraggedTypes)
return nil;
// We don't need to do this because the only place this gets called
// -_dragHitTest: in CPPlatformWindow does this already. Perhaps to
// be safe?
// if (![self containsPoint:aPoint])
// return nil;
var adjustedPoint = [self convertPlatformWindowToBase:aPoint],
hitView = [_windowView hitTest:adjustedPoint];
while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]])
hitView = [hitView superview];
if (hitView)
return hitView;
if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]])
return self;
return nil;
} }
@end @end
-31
View File
@@ -1,31 +0,0 @@
/*
* CPDragServer_Constants.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2008, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@typedef CPDragOperation
CPDragOperationNone = 0;
CPDragOperationCopy = 1 << 1;
CPDragOperationLink = 1 << 1;
CPDragOperationGeneric = 1 << 2;
CPDragOperationPrivate = 1 << 3;
CPDragOperationMove = 1 << 4;
CPDragOperationDelete = 1 << 5;
CPDragOperationEvery = -1;
+181 -202
View File
@@ -20,33 +20,166 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "CPEvent_Constants.j"
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPObjJRuntime.j> @import "CPText.j"
@import <Foundation/CPString.j>
@import "CPCompatibility.j"
@import "CGGeometry.j"
@import "CPTrackingArea.j"
@class CPTextField CPLeftMouseDown = 1;
@class CPWindow CPLeftMouseUp = 2;
@class CPGraphicsContext CPRightMouseDown = 3;
CPRightMouseUp = 4;
CPMouseMoved = 5;
CPLeftMouseDragged = 6;
CPRightMouseDragged = 7;
CPMouseEntered = 8;
CPMouseExited = 9;
CPKeyDown = 10;
CPKeyUp = 11;
CPFlagsChanged = 12;
CPAppKitDefined = 13;
CPSystemDefined = 14;
CPApplicationDefined = 15;
CPPeriodic = 16;
CPCursorUpdate = 17;
CPScrollWheel = 22;
CPOtherMouseDown = 25;
CPOtherMouseUp = 26;
CPOtherMouseDragged = 27;
@global CPApp // iPhone Event Types
@global CPNewlineCharacter CPTouchStart = 28;
@global CPCarriageReturnCharacter CPTouchMove = 29;
@global CPEnterCharacter CPTouchEnd = 30;
CPTouchCancel = 31;
@typedef DOMEvent CPAlphaShiftKeyMask = 1 << 16;
@typedef CPEventType CPShiftKeyMask = 1 << 17;
CPControlKeyMask = 1 << 18;
CPAlternateKeyMask = 1 << 19;
CPCommandKeyMask = 1 << 20;
CPNumericPadKeyMask = 1 << 21;
CPHelpKeyMask = 1 << 22;
CPFunctionKeyMask = 1 << 23;
CPDeviceIndependentModifierFlagsMask = 0xffff0000;
CPLeftMouseDownMask = 1 << CPLeftMouseDown;
CPLeftMouseUpMask = 1 << CPLeftMouseUp;
CPRightMouseDownMask = 1 << CPRightMouseDown;
CPRightMouseUpMask = 1 << CPRightMouseUp;
CPOtherMouseDownMask = 1 << CPOtherMouseDown;
CPOtherMouseUpMask = 1 << CPOtherMouseUp;
CPMouseMovedMask = 1 << CPMouseMoved;
CPLeftMouseDraggedMask = 1 << CPLeftMouseDragged;
CPRightMouseDraggedMask = 1 << CPRightMouseDragged;
CPOtherMouseDragged = 1 << CPOtherMouseDragged;
CPMouseEnteredMask = 1 << CPMouseEntered;
CPMouseExitedMask = 1 << CPMouseExited;
CPCursorUpdateMask = 1 << CPCursorUpdate;
CPKeyDownMask = 1 << CPKeyDown;
CPKeyUpMask = 1 << CPKeyUp;
CPFlagsChangedMask = 1 << CPFlagsChanged;
CPAppKitDefinedMask = 1 << CPAppKitDefined;
CPSystemDefinedMask = 1 << CPSystemDefined;
CPApplicationDefinedMask = 1 << CPApplicationDefined;
CPPeriodicMask = 1 << CPPeriodic;
CPScrollWheelMask = 1 << CPScrollWheel;
CPAnyEventMask = 0xffffffff;
CPUpArrowFunctionKey = "\uF700";
CPDownArrowFunctionKey = "\uF701";
CPLeftArrowFunctionKey = "\uF702";
CPRightArrowFunctionKey = "\uF703";
CPF1FunctionKey = "\uF704";
CPF2FunctionKey = "\uF705";
CPF3FunctionKey = "\uF706";
CPF4FunctionKey = "\uF707";
CPF5FunctionKey = "\uF708";
CPF6FunctionKey = "\uF709";
CPF7FunctionKey = "\uF70A";
CPF8FunctionKey = "\uF70B";
CPF9FunctionKey = "\uF70C";
CPF10FunctionKey = "\uF70D";
CPF11FunctionKey = "\uF70E";
CPF12FunctionKey = "\uF70F";
CPF13FunctionKey = "\uF710";
CPF14FunctionKey = "\uF711";
CPF15FunctionKey = "\uF712";
CPF16FunctionKey = "\uF713";
CPF17FunctionKey = "\uF714";
CPF18FunctionKey = "\uF715";
CPF19FunctionKey = "\uF716";
CPF20FunctionKey = "\uF717";
CPF21FunctionKey = "\uF718";
CPF22FunctionKey = "\uF719";
CPF23FunctionKey = "\uF71A";
CPF24FunctionKey = "\uF71B";
CPF25FunctionKey = "\uF71C";
CPF26FunctionKey = "\uF71D";
CPF27FunctionKey = "\uF71E";
CPF28FunctionKey = "\uF71F";
CPF29FunctionKey = "\uF720";
CPF30FunctionKey = "\uF721";
CPF31FunctionKey = "\uF722";
CPF32FunctionKey = "\uF723";
CPF33FunctionKey = "\uF724";
CPF34FunctionKey = "\uF725";
CPF35FunctionKey = "\uF726";
CPInsertFunctionKey = "\uF727";
CPDeleteFunctionKey = "\uF728";
CPHomeFunctionKey = "\uF729";
CPBeginFunctionKey = "\uF72A";
CPEndFunctionKey = "\uF72B";
CPPageUpFunctionKey = "\uF72C";
CPPageDownFunctionKey = "\uF72D";
CPPrintScreenFunctionKey = "\uF72E";
CPScrollLockFunctionKey = "\uF72F";
CPPauseFunctionKey = "\uF730";
CPSysReqFunctionKey = "\uF731";
CPBreakFunctionKey = "\uF732";
CPResetFunctionKey = "\uF733";
CPStopFunctionKey = "\uF734";
CPMenuFunctionKey = "\uF735";
CPUserFunctionKey = "\uF736";
CPSystemFunctionKey = "\uF737";
CPPrintFunctionKey = "\uF738";
CPClearLineFunctionKey = "\uF739";
CPClearDisplayFunctionKey = "\uF73A";
CPInsertLineFunctionKey = "\uF73B";
CPDeleteLineFunctionKey = "\uF73C";
CPInsertCharFunctionKey = "\uF73D";
CPDeleteCharFunctionKey = "\uF73E";
CPPrevFunctionKey = "\uF73F";
CPNextFunctionKey = "\uF740";
CPSelectFunctionKey = "\uF741";
CPExecuteFunctionKey = "\uF742";
CPUndoFunctionKey = "\uF743";
CPRedoFunctionKey = "\uF744";
CPFindFunctionKey = "\uF745";
CPHelpFunctionKey = "\uF746";
CPModeSwitchFunctionKey = "\uF747";
CPEscapeFunctionKey = "\u001B";
CPSpaceFunctionKey = "\u0020";
CPDOMEventDoubleClick = "dblclick";
CPDOMEventMouseDown = "mousedown";
CPDOMEventMouseUp = "mouseup";
CPDOMEventMouseMoved = "mousemove";
CPDOMEventMouseDragged = "mousedrag";
CPDOMEventKeyUp = "keyup";
CPDOMEventKeyDown = "keydown";
CPDOMEventKeyPress = "keypress";
CPDOMEventCopy = "copy";
CPDOMEventPaste = "paste";
CPDOMEventScrollWheel = "mousewheel";
CPDOMEventTouchStart = "touchstart";
CPDOMEventTouchMove = "touchmove";
CPDOMEventTouchEnd = "touchend";
CPDOMEventTouchCancel = "touchcancel";
var _CPEventPeriodicEventPeriod = 0, var _CPEventPeriodicEventPeriod = 0,
_CPEventPeriodicEventTimer = nil, _CPEventPeriodicEventTimer = nil,
_CPEventUpperCaseRegex = new RegExp("[A-Z]"), _CPEventUpperCaseRegex = new RegExp("[A-Z]");
_CPEventStartupMilliseconds = new Date().getTime();
/*! /*!
@ingroup appkit @ingroup appkit
@@ -56,7 +189,7 @@ var _CPEventPeriodicEventPeriod = 0,
@implementation CPEvent : CPObject @implementation CPEvent : CPObject
{ {
CPEventType _type; CPEventType _type;
CGPoint _location; CPPoint _location;
unsigned _modifierFlags; unsigned _modifierFlags;
CPTimeInterval _timestamp; CPTimeInterval _timestamp;
CPGraphicsContext _context; CPGraphicsContext _context;
@@ -66,35 +199,14 @@ var _CPEventPeriodicEventPeriod = 0,
CPWindow _window; CPWindow _window;
Number _windowNumber; Number _windowNumber;
CPString _characters; CPString _characters;
CPString _charactersIgnoringModifiers; CPString _charactersIgnoringModifiers
BOOL _isARepeat; BOOL _isARepeat;
unsigned _keyCode; unsigned _keyCode;
DOMEvent _DOMEvent; DOMEvent _DOMEvent;
int _data1;
int _data2;
short _subtype;
float _deltaX; float _deltaX;
float _deltaY; float _deltaY;
float _deltaZ; float _deltaZ;
float _scrollingDeltaX;
float _scrollingDeltaY;
BOOL _hasPreciseScrollingDeltas;
#if PLATFORM(DOM)
BOOL _suppressCappuccinoCut;
BOOL _suppressCappuccinoPaste;
#endif
CPTrackingArea _trackingArea;
}
/*!
Returns the current time in fractional seconds since startup.
*/
+ (CPTimeInterval)currentTimestamp
{
return (new Date().getTime() - _CPEventStartupMilliseconds) / 1000;
} }
/*! /*!
@@ -146,27 +258,6 @@ var _CPEventPeriodicEventPeriod = 0,
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber clickCount:aClickCount pressure:aPressure]; timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber clickCount:aClickCount pressure:aPressure];
} }
/*!
Creates a new mouse tracking event.
@param anEventType the event type
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
@param aTimestamp the time the event occurred
@param aWindowNumber the number of the CPWindow where the event occurred
@param aGraphicsContext the graphics context where the event occurred
@param anEventNumber a number for this event
@param aTrackingArea the tracking area that triggered the event
@throws CPInternalInconsistencyException if an invalid event type is provided
@return the new mouse event
*/
+ (id)enterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
{
return [[self alloc] _initEnterExitEventWithType:anEventType location:aPoint modifierFlags:modifierFlags timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber trackingArea:aTrackingArea];
}
/*! /*!
Creates a new custom event. Creates a new custom event.
@@ -198,9 +289,7 @@ var _CPEventPeriodicEventPeriod = 0,
// Make sure these are 0 rather than nil. // Make sure these are 0 rather than nil.
_deltaX = 0; _deltaX = 0;
_scrollingDeltaX = 0;
_deltaY = 0; _deltaY = 0;
_scrollingDeltaY = 0;
_deltaZ = 0; _deltaZ = 0;
} }
@@ -208,13 +297,13 @@ var _CPEventPeriodicEventPeriod = 0,
} }
/* @ignore */ /* @ignore */
- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags - (id)_initMouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
{ {
if (self = [self _initWithType:anEventType]) if (self = [self _initWithType:anEventType])
{ {
_location = CGPointCreateCopy(aPoint); _location = CPPointCreateCopy(aPoint);
_modifierFlags = modifierFlags; _modifierFlags = modifierFlags;
_timestamp = aTimestamp; _timestamp = aTimestamp;
_context = aGraphicsContext; _context = aGraphicsContext;
@@ -228,35 +317,13 @@ var _CPEventPeriodicEventPeriod = 0,
} }
/* @ignore */ /* @ignore */
- (id)_initEnterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags - (id)_initKeyEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned int)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
{
if ((anEventType != CPMouseEntered) && (anEventType != CPMouseExited) && (anEventType != CPCursorUpdate))
[CPException raise:CPInternalInconsistencyException reason:"Invalid event type"];
if (self = [self _initWithType:anEventType])
{
_location = CGPointCreateCopy(aPoint);
_modifierFlags = modifierFlags;
_timestamp = aTimestamp;
_context = aGraphicsContext;
_eventNumber = anEventNumber;
_trackingArea = aTrackingArea;
_window = [CPApp windowWithWindowNumber:aWindowNumber];
}
return self;
}
/* @ignore */
- (id)_initKeyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code
{ {
if (self = [self _initWithType:anEventType]) if (self = [self _initWithType:anEventType])
{ {
_location = CGPointCreateCopy(aPoint); _location = CPPointCreateCopy(aPoint);
_modifierFlags = modifierFlags; _modifierFlags = modifierFlags;
_timestamp = aTimestamp; _timestamp = aTimestamp;
_context = aGraphicsContext; _context = aGraphicsContext;
@@ -277,14 +344,13 @@ var _CPEventPeriodicEventPeriod = 0,
{ {
if (self = [self _initWithType:anEventType]) if (self = [self _initWithType:anEventType])
{ {
_location = CGPointCreateCopy(aPoint); _location = CPPointCreateCopy(aPoint);
_modifierFlags = modifierFlags; _modifierFlags = modifierFlags;
_timestamp = aTimestamp; _timestamp = aTimestamp;
_context = aGraphicsContext; _context = aGraphicsContext;
_subtype = aSubtype; _subtype = aSubtype;
_data1 = aData1; _data1 = aData1;
_data2 = aData2; _data2 = aData2;
_windowNumber = aWindowNumber;
} }
return self; return self;
@@ -300,7 +366,7 @@ var _CPEventPeriodicEventPeriod = 0,
*/ */
- (CGPoint)locationInWindow - (CGPoint)locationInWindow
{ {
return CGPointMakeCopy(_location); return _CGPointMakeCopy(_location);
} }
- (CGPoint)globalLocation - (CGPoint)globalLocation
@@ -323,7 +389,7 @@ var _CPEventPeriodicEventPeriod = 0,
} }
/*! /*!
Returns the time the event occurred in seconds since startup. Returns the time the event occurred.
*/ */
- (CPTimeInterval)timestamp - (CPTimeInterval)timestamp
{ {
@@ -338,14 +404,6 @@ var _CPEventPeriodicEventPeriod = 0,
return _type; return _type;
} }
/*!
Returns the subtype of the event.
*/
- (CPEventType)subtype
{
return _subtype;
}
/*! /*!
Returns the event's associated window. Returns the event's associated window.
*/ */
@@ -450,16 +508,6 @@ var _CPEventPeriodicEventPeriod = 0,
return _DOMEvent; return _DOMEvent;
} }
- (int)data1
{
return _data1;
}
- (int)data2
{
return _data2;
}
// Getting Scroll Wheel Event Information // Getting Scroll Wheel Event Information
/*! /*!
Returns the change in the x-axis for a mouse event. Returns the change in the x-axis for a mouse event.
@@ -485,31 +533,6 @@ var _CPEventPeriodicEventPeriod = 0,
return _deltaZ; return _deltaZ;
} }
- (BOOL)hasPreciseScrollingDeltas
{
return !!_hasPreciseScrollingDeltas;
}
/*!
Returns the change in points on the x-axis for a mouse movement, unless hasPreciseScrollingDeltas is NO,
in which case the change is measured in number of "lines" or "rows" and needs to be multiplied as appropriate
to get change in pixels.
*/
- (float)scrollingDeltaX
{
return _scrollingDeltaX;
}
/*!
Returns the change in points on the y-axis for a mouse movement, unless hasPreciseScrollingDeltas is NO,
in which case the change is measured in number of "lines" or "columns" and needs to be multiplied as appropriate
to get change in pixels.
*/
- (float)scrollingDeltaY
{
return _scrollingDeltaY;
}
- (BOOL)_triggersKeyEquivalent:(CPString)aKeyEquivalent withModifierMask:aKeyEquivalentModifierMask - (BOOL)_triggersKeyEquivalent:(CPString)aKeyEquivalent withModifierMask:aKeyEquivalentModifierMask
{ {
if (!aKeyEquivalent) if (!aKeyEquivalent)
@@ -548,47 +571,33 @@ 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
// 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++) 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 CPSpaceFunctionKey:
case CPEscapeFunctionKey:
case CPPageUpFunctionKey:
case CPPageDownFunctionKey:
case CPLeftArrowFunctionKey:
case CPUpArrowFunctionKey:
case CPRightArrowFunctionKey:
case CPDownArrowFunctionKey:
case CPEndFunctionKey:
case CPHomeFunctionKey:
return YES;
} }
} }
// FIXME: More cases?
return !firstResponderIsText;
}
/*!
Return YES if this event is a part of processing a browser controlled cut or paste event
where the browser will go ahead and do the work of cutting or pasting within the input
element after processing of this event. The implication is that it should not be done by
the CPTextField (or whatever else is controlling the input) since this would result in
nothing being cut (because the field already cut the text out), or a double paste
(because the field pasted as well as the browser).
*/
- (BOOL)_platformIsEffectingCutOrPaste
{
#if PLATFORM(DOM)
return _suppressCappuccinoCut || _suppressCappuccinoPaste;
#else
return NO; return NO;
#endif
} }
/*! /*!
Generates periodic events every \c aPeriod seconds. Generates periodic events every \c aPeriod seconds.
@@ -616,42 +625,11 @@ var _CPEventPeriodicEventPeriod = 0,
_CPEventPeriodicEventTimer = nil; _CPEventPeriodicEventTimer = nil;
} }
- (CPString)description
{
switch (_type)
{
case CPKeyDown:
case CPKeyUp:
case CPFlagsChanged:
return [CPString stringWithFormat:@"CPEvent: type=%d loc=%@ time=%.1f flags=0x%X win=%@ winNum=%d ctxt=%@ chars=\"%@\" unmodchars=\"%@\" repeat=%d keyCode=%d", _type, CGStringFromPoint(_location), _timestamp, _modifierFlags, _window, _windowNumber, _context, _characters, _charactersIgnoringModifiers, _isARepeat, _keyCode];
case CPLeftMouseDown:
case CPLeftMouseUp:
case CPRightMouseDown:
case CPRightMouseUp:
case CPMouseMoved:
case CPLeftMouseDragged:
case CPRightMouseDragged:
case CPMouseEntered:
case CPMouseExited:
return [CPString stringWithFormat:@"CPEvent: type=%d loc=%@ time=%.1f flags=0x%X win=%@ winNum=%d ctxt=%@ evNum=%d click=%d buttonNumber=%d pressure=%f", _type, CGStringFromPoint(_location), _timestamp, _modifierFlags, _window, _windowNumber, _context, _eventNumber, _clickCount, [self buttonNumber], _pressure];
default:
return [CPString stringWithFormat:@"CPEvent: type=%d loc=%@ time=%.1f flags=0x%X win=%@ winNum=%d ctxt=%@ subtype=%d data1=%d data2=%d", _type, CGStringFromPoint(_location), _timestamp, _modifierFlags, _window, _windowNumber, _context, _subtype, _data1, _data2];
}
}
- (CPTrackingArea)trackingArea
{
if ((_type !== CPMouseEntered) && (_type !== CPMouseExited) && (_type !== CPCursorUpdate))
[CPException raise:CPInternalInconsistencyException format:@"You can't call trackingArea for events of type %#x", _type];
return _trackingArea;
}
@end @end
function _CPEventFirePeriodEvent() function _CPEventFirePeriodEvent()
{ {
[CPApp sendEvent:[CPEvent otherEventWithType:CPPeriodic location:CGPointMakeZero() modifierFlags:0 timestamp:[CPEvent currentTimestamp] windowNumber:0 context:nil subtype:0 data1:0 data2:0]]; [CPApp sendEvent:[CPEvent otherEventWithType:CPPeriodic location:_CGPointMakeZero() modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]];
} }
var CPEventClass = [CPEvent class]; var CPEventClass = [CPEvent class];
@@ -673,3 +651,4 @@ function _CPEventFromNativeMouseEvent(aNativeEvent, anEventType, aPoint, modifie
return aNativeEvent; return aNativeEvent;
} }
-174
View File
@@ -1,174 +0,0 @@
/*
* CPEvent_Constants.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2008, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
CPLeftMouseDown = 1;
CPLeftMouseUp = 2;
CPRightMouseDown = 3;
CPRightMouseUp = 4;
CPMouseMoved = 5;
CPLeftMouseDragged = 6;
CPRightMouseDragged = 7;
CPMouseEntered = 8;
CPMouseExited = 9;
CPKeyDown = 10;
CPKeyUp = 11;
CPFlagsChanged = 12;
CPAppKitDefined = 13;
CPSystemDefined = 14;
CPApplicationDefined = 15;
CPPeriodic = 16;
CPCursorUpdate = 17;
CPScrollWheel = 22;
CPOtherMouseDown = 25;
CPOtherMouseUp = 26;
CPOtherMouseDragged = 27;
// iPhone Event Types
CPTouchStart = 28;
CPTouchMove = 29;
CPTouchEnd = 30;
CPTouchCancel = 31;
CPAlphaShiftKeyMask = 1 << 16;
CPShiftKeyMask = 1 << 17;
CPControlKeyMask = 1 << 18;
CPAlternateKeyMask = 1 << 19;
CPCommandKeyMask = 1 << 20;
CPNumericPadKeyMask = 1 << 21;
CPHelpKeyMask = 1 << 22;
CPFunctionKeyMask = 1 << 23;
CPDeviceIndependentModifierFlagsMask = 0xffff0000;
CPLeftMouseDownMask = 1 << CPLeftMouseDown;
CPLeftMouseUpMask = 1 << CPLeftMouseUp;
CPRightMouseDownMask = 1 << CPRightMouseDown;
CPRightMouseUpMask = 1 << CPRightMouseUp;
CPOtherMouseDownMask = 1 << CPOtherMouseDown;
CPOtherMouseUpMask = 1 << CPOtherMouseUp;
CPMouseMovedMask = 1 << CPMouseMoved;
CPLeftMouseDraggedMask = 1 << CPLeftMouseDragged;
CPRightMouseDraggedMask = 1 << CPRightMouseDragged;
CPOtherMouseDragged = 1 << CPOtherMouseDragged;
CPMouseEnteredMask = 1 << CPMouseEntered;
CPMouseExitedMask = 1 << CPMouseExited;
CPCursorUpdateMask = 1 << CPCursorUpdate;
CPKeyDownMask = 1 << CPKeyDown;
CPKeyUpMask = 1 << CPKeyUp;
CPFlagsChangedMask = 1 << CPFlagsChanged;
CPAppKitDefinedMask = 1 << CPAppKitDefined;
CPSystemDefinedMask = 1 << CPSystemDefined;
CPApplicationDefinedMask = 1 << CPApplicationDefined;
CPPeriodicMask = 1 << CPPeriodic;
CPScrollWheelMask = 1 << CPScrollWheel;
CPAnyEventMask = 0xffffffff;
CPUpArrowFunctionKey = "\uF700";
CPDownArrowFunctionKey = "\uF701";
CPLeftArrowFunctionKey = "\uF702";
CPRightArrowFunctionKey = "\uF703";
CPF1FunctionKey = "\uF704";
CPF2FunctionKey = "\uF705";
CPF3FunctionKey = "\uF706";
CPF4FunctionKey = "\uF707";
CPF5FunctionKey = "\uF708";
CPF6FunctionKey = "\uF709";
CPF7FunctionKey = "\uF70A";
CPF8FunctionKey = "\uF70B";
CPF9FunctionKey = "\uF70C";
CPF10FunctionKey = "\uF70D";
CPF11FunctionKey = "\uF70E";
CPF12FunctionKey = "\uF70F";
CPF13FunctionKey = "\uF710";
CPF14FunctionKey = "\uF711";
CPF15FunctionKey = "\uF712";
CPF16FunctionKey = "\uF713";
CPF17FunctionKey = "\uF714";
CPF18FunctionKey = "\uF715";
CPF19FunctionKey = "\uF716";
CPF20FunctionKey = "\uF717";
CPF21FunctionKey = "\uF718";
CPF22FunctionKey = "\uF719";
CPF23FunctionKey = "\uF71A";
CPF24FunctionKey = "\uF71B";
CPF25FunctionKey = "\uF71C";
CPF26FunctionKey = "\uF71D";
CPF27FunctionKey = "\uF71E";
CPF28FunctionKey = "\uF71F";
CPF29FunctionKey = "\uF720";
CPF30FunctionKey = "\uF721";
CPF31FunctionKey = "\uF722";
CPF32FunctionKey = "\uF723";
CPF33FunctionKey = "\uF724";
CPF34FunctionKey = "\uF725";
CPF35FunctionKey = "\uF726";
CPInsertFunctionKey = "\uF727";
CPDeleteFunctionKey = "\uF728";
CPHomeFunctionKey = "\uF729";
CPBeginFunctionKey = "\uF72A";
CPEndFunctionKey = "\uF72B";
CPPageUpFunctionKey = "\uF72C";
CPPageDownFunctionKey = "\uF72D";
CPPrintScreenFunctionKey = "\uF72E";
CPScrollLockFunctionKey = "\uF72F";
CPPauseFunctionKey = "\uF730";
CPSysReqFunctionKey = "\uF731";
CPBreakFunctionKey = "\uF732";
CPResetFunctionKey = "\uF733";
CPStopFunctionKey = "\uF734";
CPMenuFunctionKey = "\uF735";
CPUserFunctionKey = "\uF736";
CPSystemFunctionKey = "\uF737";
CPPrintFunctionKey = "\uF738";
CPClearLineFunctionKey = "\uF739";
CPClearDisplayFunctionKey = "\uF73A";
CPInsertLineFunctionKey = "\uF73B";
CPDeleteLineFunctionKey = "\uF73C";
CPInsertCharFunctionKey = "\uF73D";
CPDeleteCharFunctionKey = "\uF73E";
CPPrevFunctionKey = "\uF73F";
CPNextFunctionKey = "\uF740";
CPSelectFunctionKey = "\uF741";
CPExecuteFunctionKey = "\uF742";
CPUndoFunctionKey = "\uF743";
CPRedoFunctionKey = "\uF744";
CPFindFunctionKey = "\uF745";
CPHelpFunctionKey = "\uF746";
CPModeSwitchFunctionKey = "\uF747";
CPEscapeFunctionKey = "\u001B";
CPSpaceFunctionKey = "\u0020";
CPDOMEventDoubleClick = "dblclick";
CPDOMEventMouseDown = "mousedown";
CPDOMEventMouseUp = "mouseup";
CPDOMEventMouseMoved = "mousemove";
CPDOMEventMouseDragged = "mousedrag";
CPDOMEventKeyUp = "keyup";
CPDOMEventKeyDown = "keydown";
CPDOMEventKeyPress = "keypress";
CPDOMEventCopy = "copy";
CPDOMEventPaste = "paste";
CPDOMEventScrollWheel = "mousewheel";
CPDOMEventTouchStart = "touchstart";
CPDOMEventTouchMove = "touchmove";
CPDOMEventTouchEnd = "touchend";
CPDOMEventTouchCancel = "touchcancel";
+16 -25
View File
@@ -38,14 +38,12 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
#if PLATFORM(DOM) #if PLATFORM(DOM)
DOMElement _DOMParamElement; DOMElement _DOMParamElement;
DOMElement _DOMObjectElement; DOMElement _DOMObjectElement;
DOMElement _DOMInnerObjectElement;
#endif #endif
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
{ {
CPLog.warn("CPFlashView is not supported by Cappuccino, this is now deprecated and it will be removed in the version 1.1 of Cappuccino");
self = [super initWithFrame:aFrame]; self = [super initWithFrame:aFrame];
if (self) if (self)
@@ -54,16 +52,22 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine)) if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
{ {
_DOMObjectElement = document.createElement(@"object"); _DOMObjectElement = document.createElement(@"object");
_DOMObjectElement.id = [self elementID];
_DOMObjectElement.width = @"100%"; _DOMObjectElement.width = @"100%";
_DOMObjectElement.height = @"100%"; _DOMObjectElement.height = @"100%";
_DOMObjectElement.style.top = @"0px"; _DOMObjectElement.style.top = @"0px";
_DOMObjectElement.style.left = @"0px"; _DOMObjectElement.style.left = @"0px";
_DOMObjectElement.type = @"application/x-shockwave-flash"; _DOMObjectElement.type = @"application/x-shockwave-flash";
_DOMObjectElement.setAttribute(@"classid", IEFlashCLSID);
_DOMParamElement = document.createElement(@"param"); _DOMParamElement = document.createElement(@"param");
_DOMParamElement.name = @"movie"; _DOMParamElement.name = @"movie";
_DOMInnerObjectElement = document.createElement(@"object");
_DOMInnerObjectElement.width = @"100%";
_DOMInnerObjectElement.height = @"100%";
_DOMObjectElement.appendChild(_DOMParamElement); _DOMObjectElement.appendChild(_DOMParamElement);
_DOMObjectElement.appendChild(_DOMInnerObjectElement);
_DOMElement.appendChild(_DOMObjectElement); _DOMElement.appendChild(_DOMObjectElement);
} }
@@ -85,7 +89,7 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine)) if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
{ {
_DOMParamElement.value = [aFlashMovie filename]; _DOMParamElement.value = [aFlashMovie filename];
_DOMObjectElement.data = [aFlashMovie filename]; _DOMInnerObjectElement.data = [aFlashMovie filename];
} }
else else
[self _rebuildIEObjects]; [self _rebuildIEObjects];
@@ -100,17 +104,14 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
- (void)setFlashVars:(CPDictionary)aDictionary - (void)setFlashVars:(CPDictionary)aDictionary
{ {
var varString = @"", var varString = @"",
enumerator = [aDictionary keyEnumerator], enumerator = [aDictionary keyEnumerator];
key;
if (key = [enumerator nextObject])
varString = [varString stringByAppendingFormat:@"%@=%@", key, [aDictionary objectForKey:key]];
var key;
while (key = [enumerator nextObject]) while (key = [enumerator nextObject])
varString = [varString stringByAppendingFormat:@"&%@=%@", key, [aDictionary objectForKey:key]]; varString = [varString stringByAppendingFormat:@"&%@=%@", key, [aDictionary objectForKey:key]];
if (!_params) if (!_params)
_params = @{}; _params = [CPDictionary dictionary];
[_params setObject:varString forKey:@"flashvars"]; [_params setObject:varString forKey:@"flashvars"];
[self setParameters:_params]; [self setParameters:_params];
@@ -140,12 +141,12 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
#if PLATFORM(DOM) #if PLATFORM(DOM)
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine)) if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
{ {
_paramElements = @{}; _paramElements = [CPDictionary dictionary];
var enumerator = [_params keyEnumerator], var enumerator = [_params keyEnumerator],
key; key;
while (_DOMObjectElement && (key = [enumerator nextObject]) !== nil) while (_DOMObjectElement && (key = [enumerator nextObject]))
{ {
var param = document.createElement(@"param"); var param = document.createElement(@"param");
param.name = key; param.name = key;
@@ -177,26 +178,16 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
paramEnumerator = [_params keyEnumerator], paramEnumerator = [_params keyEnumerator],
key; key;
while ((key = [paramEnumerator nextObject]) !== nil) while (key = [paramEnumerator nextObject])
paramString = [paramString stringByAppendingFormat:@"<param name='%@' value='%@' />", key, [_params objectForKey:key]]; paramString = [paramString stringByAppendingFormat:@"<param name='%@' value='%@' />", key, [_params objectForKey:key]];
_DOMObjectElement = document.createElement(@"object"); _DOMObjectElement = document.createElement(@"object");
_DOMElement.appendChild(_DOMObjectElement); _DOMElement.appendChild(_DOMObjectElement);
_DOMObjectElement.outerHTML = [CPString stringWithFormat:@"<object id=%@ classid=%@ width=%@ height=%@>%@</object>", [self elementID], IEFlashCLSID, CGRectGetWidth([self bounds]), CGRectGetHeight([self bounds]), paramString]; _DOMObjectElement.outerHTML = [CPString stringWithFormat:@"<object classid=%@ width=%@ height=%@>%@</object>", IEFlashCLSID, CGRectGetWidth([self bounds]), CGRectGetHeight([self bounds]), paramString];
} }
#endif #endif
- (CPString)elementID
{
return @"CPFV_" + [self UID];
}
- (void)mouseMoved:(CPEvent)sommit
{
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
}
- (void)mouseDragged:(CPEvent)anEvent - (void)mouseDragged:(CPEvent)anEvent
{ {
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES]; [[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
+74 -232
View File
@@ -24,90 +24,50 @@
@import <Foundation/CPBundle.j> @import <Foundation/CPBundle.j>
@import "CPView.j" @import "CPView.j"
@import "CPFontDescriptor.j"
CPFontDefaultSystemFontFace = @"Arial, sans-serif"; CPFontDefaultSystemFontFace = @"Arial, sans-serif";
CPFontDefaultSystemFontSize = 12; CPFontDefaultSystemFontSize = 12;
/*! var _CPFonts = {},
To create a font of a size that will dynamically reflect the current _CPFontSystemFontFace = CPFontDefaultSystemFontFace,
system font size, use this for the size argument. _CPFontSystemFontSize = 12,
*/ _CPFontFallbackFaces = CPFontDefaultSystemFontFace.split(", "),
CPFontCurrentSystemSize = -1; _CPFontStripRegExp = new RegExp("(^\\s*[\"']?|[\"']?\\s*$)", "g");
// For internal use only by this class and subclasses
_CPFontSystemFacePlaceholder = "_CPFontSystemFacePlaceholder";
var _CPFontCache = {},
_CPSystemFontCache = {},
_CPFontSystemFontFace = CPFontDefaultSystemFontFace,
_CPFontSystemFontSize = 12,
_CPFontFallbackFaces = CPFontDefaultSystemFontFace.split(", "),
_CPFontStripRegExp = new RegExp("(^\\s*[\"']?|[\"']?\\s*$)", "g");
#define _CPRealFontSize(aSize) (aSize <= 0 ? _CPFontSystemFontSize : aSize)
#define _CPFontNormalizedNames(aName) _CPFontNormalizedNameArray(aName).join(", ") #define _CPFontNormalizedNames(aName) _CPFontNormalizedNameArray(aName).join(", ")
#define _CPCachedFont(aName, aSize, isBold, isItalic) _CPFontCache[_CPFontCreateCSSString(_CPFontNormalizedNames(aName), aSize, isBold, isItalic)] #define _CPCachedFont(aName, aSize, isBold, isItalic) _CPFonts[_CPFontCreateCSSString(_CPFontNormalizedNames(aName), aSize, isBold, isItalic)]
#define _CPUserFont(aName, aSize, isBold, isItalic) _CPCachedFont(aName, aSize, isBold, isItalic) || [[CPFont alloc] _initWithName:aName size:aSize bold:isBold italic:isItalic system:NO]
#define _CPSystemFontCacheKey(aSize, isBold) (String(aSize) + (isBold ? "b" : ""))
#define _CPCachedSystemFont(aSize, isBold) _CPSystemFontCache[_CPSystemFontCacheKey(aSize, isBold)]
#define _CPSystemFont(aSize, isBold) (_CPCachedSystemFont(aSize, isBold) || [[CPFont alloc] _initWithName:_CPFontSystemFacePlaceholder size:aSize bold:isBold italic:NO system:YES])
/*! /*!
@ingroup appkit @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 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. a name and/or size as arguments, and return the appropriate CPFont.
### System fonts By default the system font face/size is Arial 12px, with a fallback to sans-serif. You may
When you create a font using \c -systemFontOfSize: or \c -boldSystemFontOfSize:, a proxy font is configure this at runtime in two ways:
created that always refers to the current system font face and size. By default the system
font face/size is Arial 12px, with a fallback to sans-serif 12px. You may configure this at
runtime in two ways:
- By sending [CPFont setSystemFontFace:<face>] and/or [CPFont setSystemFontSize:<size>]. - By sending [CPFont @link CPFont::setSystemFontFace: setSystemFontFace:@endlink] and/or [CPFont @link CPFont::setSystemFontSize: setSystemFontSize:@endlink].
Note that if you change the system font face or size during runtime, the next time - By configuring Info.plist for your application or for AppKit. You can set the font face
any view using a system font is redrawn, it will show the new font. by adding a CPSystemFontFace string item to the Info.plist, and you can set the font size
- By configuring Info.plist for your application or for AppKit. You can set the font face by adding a CPSystemFontSize integer item to the Info.plist.
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. Note that in either case, you can specify a comma-delimited list of fonts as the font face.
Do not quote enclose font faces that contain spaces, that is done automatically when a CSS The browser will use the first available font in the list. CPFont always ensures that Arial
representation of a font is requested. and sans-serif are in the generated CSS string, so there is no need to add them to the end
of your font list.
The browser will use the first available font in the list you supply. CPFont always ensures If you are using nib2cib, you should use the second method (using Info.plist),
that Arial and sans-serif are in the CSS representation of a font as a fallback, so there is and be sure to run nib2cib again any time you modify the CPSystemFontFace or CPSystemFontSize items.
no need to add them to the end of your font list. 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:
### nib2cib conversion @code
Fonts are converted by nib2cib according to the following algorithm:
- If the font family is Lucida Grande, then a system font will be created.
- If the font is Lucida Grande 13 (plain), the "default" font will be used at runtime.
The default font is taken from the "font" theme attribute if one exists,
otherwise from the current system font face and size.
- Lucida Grande of any size other than 13 will retain its size.
- Fonts using any family other than Lucida Grande will be used as is, including the size.
### Using custom web fonts
The configurability of CPFont makes it easy to use a custom web font as the system font.
For example, if you want to use the google font Asap as the system font, you would do the
following:
- Add a <link> to the <head> of index-debug.html and index.html:
@code
<link href='http://fonts.googleapis.com/css?family=Asap:400,700' rel='stylesheet' type='text/css'>
@endcode
- Specify Asap as the system font in Info.plist by adding the following item:
@code
<key>CPSystemFontFace</key> <key>CPSystemFontFace</key>
<string>Asap</string> <string>Lucida Grande</string>
@endcode @endcode
*/ */
@implementation CPFont : CPObject @implementation CPFont : CPObject
{ {
@@ -118,23 +78,19 @@ following:
float _lineHeight; float _lineHeight;
BOOL _isBold @accessors(readonly, getter=isBold); BOOL _isBold @accessors(readonly, getter=isBold);
BOOL _isItalic @accessors(readonly, getter=isItalic); BOOL _isItalic @accessors(readonly, getter=isItalic);
BOOL _isSystem @accessors(readonly, getter=isSystem);
CPString _cssString; CPString _cssString;
} }
+ (void)initialize + (void)initialize
{ {
if (self !== [CPFont class])
return;
var systemFontFace = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontFace"]; var systemFontFace = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontFace"];
if (!systemFontFace) if (!systemFontFace)
systemFontFace = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontFace"]; systemFontFace = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontFace"];
if (systemFontFace) if (systemFontFace)
_CPFontSystemFontFace = _CPFontNormalizedNames(systemFontFace); [self setSystemFontFace:systemFontFace];
var systemFontSize = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontSize"]; var systemFontSize = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontSize"];
@@ -158,13 +114,7 @@ following:
*/ */
+ (CPString)setSystemFontFace:(CPString)aFace + (CPString)setSystemFontFace:(CPString)aFace
{ {
var normalizedFaces = _CPFontNormalizedNames(aFace); _CPFontSystemFontFace = _CPFontNormalizedNames(aFace);
if (normalizedFaces === _CPFontSystemFontFace)
return;
[self _invalidateSystemFontCache];
_CPFontSystemFontFace = aFace;
} }
/*! /*!
@@ -175,158 +125,106 @@ following:
return _CPFontSystemFontSize; return _CPFontSystemFontSize;
} }
+ (CPFont)systemFontForControlSize:(CPControlSize)aSize
{
// TODO These sizes should be themable or made less arbitrary in some other way.
switch (aSize)
{
case CPSmallControlSize:
return [self systemFontOfSize:_CPFontSystemFontSize - 1];
case CPMiniControlSize:
return [self systemFontOfSize:_CPFontSystemFontSize - 2];
case CPRegularControlSize:
default:
return [self systemFontOfSize:_CPFontSystemFontSize];
}
}
/*! /*!
Sets the default system font size. Sets the default system font size.
*/ */
+ (float)setSystemFontSize:(float)size + (float)setSystemFontSize:(float)size
{ {
if (size > 0 && size !== _CPFontSystemFontSize) if (size > 0)
{
[self _invalidateSystemFontCache];
_CPFontSystemFontSize = size; _CPFontSystemFontSize = size;
}
}
+ (void)_invalidateSystemFontCache
{
var systemSize = String(_CPFontSystemFontSize),
currentSize = String(CPFontCurrentSystemSize);
for (var key in _CPSystemFontCache)
{
if (_CPSystemFontCache.hasOwnProperty(key) &&
(key.indexOf(systemSize) === 0 || key.indexOf(currentSize) === 0))
{
delete _CPSystemFontCache[key];
}
}
} }
/*! /*!
Returns a font with the specified name and size. Returns a font with the specified name and size.
@param aName the name of the font @param aName the name of the font
@param aSize the size of the font (in px). 0 or negative will create a font @param aSize the size of the font (in px)
in the current system font size.
@return the requested font @return the requested font
*/ */
+ (CPFont)fontWithName:(CPString)aName size:(float)aSize + (CPFont)fontWithName:(CPString)aName size:(float)aSize
{ {
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, NO, NO); return _CPCachedFont(aName, aSize, NO, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:NO italic:NO];
} }
/*! /*!
Returns a font with the specified name, size and style. Returns a font with the specified name, size and style.
@param aName the name of the font @param aName the name of the font
@param aSize the size of the font (in px). 0 or negative will create a font @param aSize the size of the font (in px)
in the current system font size.
@param italic whether the font should be italicized @param italic whether the font should be italicized
@return the requested font @return the requested font
*/ */
+ (CPFont)fontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic + (CPFont)fontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic
{ {
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, NO, 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). 0 or negative will create a font @param aSize the size of the font (in px)
in the current system font size.
@return the requested bold font @return the requested bold font
*/ */
+ (CPFont)boldFontWithName:(CPString)aName size:(float)aSize + (CPFont)boldFontWithName:(CPString)aName size:(float)aSize
{ {
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, YES, NO); return _CPCachedFont(aName, aSize, YES, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:YES italic:NO];
} }
/*! /*!
Returns a bold font with the specified name, size and style. Returns a bold font with the specified name, size and style.
@param aName the name of the font @param aName the name of the font
@param aSize the size of the font (in px). 0 or negative will create a font @param aSize the size of the font (in px)
in the current system font size.
@param italic whether the font should be italicized @param italic whether the font should be italicized
@return the requested font @return the requested font
*/ */
+ (CPFont)boldFontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic + (CPFont)boldFontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic
{ {
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, YES, italic); return _CPCachedFont(aName, aSize, NO, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:YES italic:italic];
}
/*!
Internal font getter like fontWithName:size:italic: with a bold selector.
*/
+ (CPFont)_fontWithName:(CPString)aName size:(float)aSize bold:(BOOL)bold italic:(BOOL)italic
{
return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, bold, italic);
} }
/*! /*!
Returns the system font scaled to the specified size Returns the system font scaled to the specified size
@param aSize the size of the font (in px). 0 creates a static font @param aSize the size of the font (in px)
in the current system font size. Negative creates a font
that dynamically tracks the current system font size.
@return the requested system font @return the requested system font
*/ */
+ (CPFont)systemFontOfSize:(CGSize)aSize + (CPFont)systemFontOfSize:(CPSize)aSize
{ {
return _CPSystemFont(aSize === 0 ? _CPFontSystemFontSize : aSize, NO); return _CPCachedFont(_CPFontSystemFontFace, aSize, NO, NO) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:NO italic:NO];
} }
/*! /*!
Returns the bold system font scaled to the specified size Returns the bold system font scaled to the specified size
@param aSize the size of the font (in px). 0 creates a static font @param aSize the size of the font (in px)
in the current system font size. Negative creates a font
that dynamically tracks the current system font size.
@return the requested bold system font @return the requested bold system font
*/ */
+ (CPFont)boldSystemFontOfSize:(CGSize)aSize + (CPFont)boldSystemFontOfSize:(CPSize)aSize
{ {
return _CPSystemFont(aSize === 0 ? _CPFontSystemFontSize : aSize, YES); return _CPCachedFont(_CPFontSystemFontFace, aSize, YES, NO) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:YES italic:NO];
} }
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold italic:(BOOL)isItalic system:(BOOL)isSystem /* FIXME Font Descriptor
@ignore
*/
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold
{
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);
_size = aSize; _size = aSize;
_ascender = 0; _ascender = 0;
_descender = 0; _descender = 0;
_lineHeight = 0; _lineHeight = 0;
_isBold = isBold; _isBold = isBold;
_isItalic = isItalic; _isItalic = isItalic;
_isSystem = isSystem;
if (isSystem) _cssString = _CPFontCreateCSSString(_name, _size, _isBold, _isItalic);
{
_name = aName; _CPFonts[_cssString] = self;
_cssString = _CPFontCreateCSSString(_CPFontSystemFontFace, _size, _isBold, _isItalic);
_CPSystemFontCache[_CPSystemFontCacheKey(_size, _isBold)] = self;
}
else
{
_name = _CPFontNormalizedNames(aName);
_cssString = _CPFontCreateCSSString(_name, _size, _isBold, _isItalic);
_CPFontCache[_cssString] = self;
}
} }
return self; return self;
@@ -337,12 +235,10 @@ following:
*/ */
- (float)ascender - (float)ascender
{ {
var font = _isSystem ? _CPSystemFont(_size, _isBold) : self; if (!_ascender)
[self _getMetrics];
if (!font._ascender) return _ascender;
[font _getMetrics];
return font._ascender;
} }
/*! /*!
@@ -351,12 +247,10 @@ following:
*/ */
- (float)descender - (float)descender
{ {
var font = _isSystem ? _CPSystemFont(_size, _isBold) : self; if (!_descender)
[self _getMetrics];
if (!font._descender) return _descender;
[font _getMetrics];
return font._descender;
} }
/*! /*!
@@ -366,12 +260,10 @@ following:
*/ */
- (float)defaultLineHeightForFont - (float)defaultLineHeightForFont
{ {
var font = _isSystem ? _CPSystemFont(_size, _isBold) : self; if (!_lineHeight)
[self _getMetrics];
if (!font._lineHeight) return _lineHeight;
[font _getMetrics];
return font._lineHeight;
} }
/*! /*!
@@ -379,7 +271,7 @@ following:
*/ */
- (float)size - (float)size
{ {
return _CPRealFontSize(_size); return _size;
} }
/*! /*!
@@ -387,9 +279,7 @@ following:
*/ */
- (CPString)cssString - (CPString)cssString
{ {
var font = _isSystem ? _CPSystemFont(_size, _isBold) : self; return _cssString;
return font._cssString;
} }
/*! /*!
@@ -397,17 +287,9 @@ following:
*/ */
- (CPString)familyName - (CPString)familyName
{ {
if (_isSystem)
return _CPFontSystemFontFace;
return _name; return _name;
} }
- (BOOL)isSystemSize
{
return _size <= 0;
}
- (BOOL)isEqual:(id)anObject - (BOOL)isEqual:(id)anObject
{ {
return [anObject isKindOfClass:[CPFont class]] && [anObject cssString] === _cssString; return [anObject isKindOfClass:[CPFont class]] && [anObject cssString] === _cssString;
@@ -420,7 +302,7 @@ following:
- (id)copy - (id)copy
{ {
return [[CPFont alloc] _initWithName:_name size:_size bold:_isBold italic:_isItalic system:_isSystem]; return [[CPFont alloc] _initWithName:_name size:_size bold:_isBold italic:_isItalic];
} }
- (void)_getMetrics - (void)_getMetrics
@@ -434,48 +316,10 @@ following:
@end @end
@implementation CPFont(DescriptorAdditions)
- (id)_initWithFontDescriptor:(CPFontDescriptor)fontDescriptor
{
var aName = [fontDescriptor objectForKey: CPFontNameAttribute] ,
aSize = [fontDescriptor pointSize],
isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
return [self _initWithName:aName size:aSize bold:isBold italic:isItalic system:NO];
}
+ (CPFont)fontWithDescriptor:(CPFontDescriptor)fontDescriptor size:(float)aSize
{
var aName = [fontDescriptor objectForKey: CPFontNameAttribute],
isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
return [self _fontWithName:aName size:aSize || [fontDescriptor pointSize] bold:isBold italic:isItalic];
}
- (CPFontDescriptor)fontDescriptor
{
var traits = 0;
if ([self isBold])
traits |= CPFontBoldTrait;
if ([self isItalic])
traits |= CPFontItalicTrait;
return [[CPFontDescriptor fontDescriptorWithName:_name size:_size] fontDescriptorWithSymbolicTraits:traits];
}
@end
var CPFontNameKey = @"CPFontNameKey", var CPFontNameKey = @"CPFontNameKey",
CPFontSizeKey = @"CPFontSizeKey", CPFontSizeKey = @"CPFontSizeKey",
CPFontIsBoldKey = @"CPFontIsBoldKey", CPFontIsBoldKey = @"CPFontIsBoldKey",
CPFontIsItalicKey = @"CPFontIsItalicKey", CPFontIsItalicKey = @"CPFontIsItalicKey";
CPFontIsSystemKey = @"CPFontIsSystemKey";
@implementation CPFont (CPCoding) @implementation CPFont (CPCoding)
@@ -489,10 +333,9 @@ var CPFontNameKey = @"CPFontNameKey",
var fontName = [aCoder decodeObjectForKey:CPFontNameKey], var fontName = [aCoder decodeObjectForKey:CPFontNameKey],
size = [aCoder decodeFloatForKey:CPFontSizeKey], size = [aCoder decodeFloatForKey:CPFontSizeKey],
isBold = [aCoder decodeBoolForKey:CPFontIsBoldKey], isBold = [aCoder decodeBoolForKey:CPFontIsBoldKey],
isItalic = [aCoder decodeBoolForKey:CPFontIsItalicKey], isItalic = [aCoder decodeBoolForKey:CPFontIsItalicKey];
isSystem = [aCoder decodeBoolForKey:CPFontIsSystemKey];
return [self _initWithName:fontName size:size bold:isBold italic:isItalic system:isSystem]; return [self _initWithName:fontName size:size bold:isBold italic:isItalic];
} }
/*! /*!
@@ -505,7 +348,6 @@ var CPFontNameKey = @"CPFontNameKey",
[aCoder encodeFloat:_size forKey:CPFontSizeKey]; [aCoder encodeFloat:_size forKey:CPFontSizeKey];
[aCoder encodeBool:_isBold forKey:CPFontIsBoldKey]; [aCoder encodeBool:_isBold forKey:CPFontIsBoldKey];
[aCoder encodeBool:_isItalic forKey:CPFontIsItalicKey]; [aCoder encodeBool:_isItalic forKey:CPFontIsItalicKey];
[aCoder encodeBool:_isSystem forKey:CPFontIsSystemKey];
} }
@end @end
@@ -514,7 +356,7 @@ var CPFontNameKey = @"CPFontNameKey",
// aName must be normalized // aName must be normalized
var _CPFontCreateCSSString = function(aName, aSize, isBold, isItalic) var _CPFontCreateCSSString = function(aName, aSize, isBold, isItalic)
{ {
var properties = (isItalic ? "italic " : "") + (isBold ? "bold " : "") + _CPRealFontSize(aSize) + "px "; var properties = (isItalic ? "italic " : "") + (isBold ? "bold " : "") + aSize + "px ";
return properties + _CPFontConcatNameWithFallback(aName); return properties + _CPFontConcatNameWithFallback(aName);
}; };
+32 -361
View File
@@ -22,61 +22,18 @@
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import "CPControl.j"
@import "CPFont.j" @import "CPFont.j"
@import "CPFontDescriptor.j"
@global CPApp
@class CPFontPanel
CPItalicFontMask = 1 << 0;
CPBoldFontMask = 1 << 1;
CPUnboldFontMask = 1 << 2;
CPNonStandardCharacterSetFontMask = 1 << 3;
CPNarrowFontMask = 1 << 4;
CPExpandedFontMask = 1 << 5;
CPCondensedFontMask = 1 << 6;
CPSmallCapsFontMask = 1 << 7;
CPPosterFontMask = 1 << 8;
CPCompressedFontMask = 1 << 9;
CPFixedPitchFontMask = 1 << 10;
CPUnitalicFontMask = 1 << 24;
var CPSharedFontManager = nil, var CPSharedFontManager = nil,
CPFontManagerFactory = nil, CPFontManagerFactory = Nil;
CPFontPanelFactory = nil;
/*
modifyFont: sender's tag
*/
CPNoFontChangeAction = 0;
CPViaPanelFontAction = 1;
CPAddTraitFontAction = 2;
CPSizeUpFontAction = 3;
CPSizeDownFontAction = 4;
CPHeavierFontAction = 5;
CPLighterFontAction = 6;
CPRemoveTraitFontAction = 7;
/*! /*!
@ingroup appkit @ingroup appkit
*/ */
@implementation CPFontManager : CPObject @implementation CPFontManager : CPObject
{ {
CPArray _availableFonts; CPArray _availableFonts;
id _target @accessors(property=target);
SEL _action @accessors(property=action);
id _delegate @accessors(property=delegate);
CPFont _selectedFont;
BOOL _multiple @accessors(getter=isMultiple, setter=setMultiple:);
CPDictionary _activeChange;
unsigned _fontAction;
} }
// Getting the Shared Font Manager // Getting the Shared Font Manager
@@ -101,25 +58,6 @@ CPRemoveTraitFontAction = 7;
{ {
CPFontManagerFactory = aClass; CPFontManagerFactory = aClass;
} }
/*!
Sets the class that will be used to create the application's
Font panel.
*/
+ (void)setFontPanelFactory:(Class)aClass
{
CPFontPanelFactory = aClass;
}
- (id)init
{
if (self = [super init])
{
_action = @selector(changeFont:);
}
return self;
}
/*! /*!
Returns an array of the available fonts Returns an array of the available fonts
@@ -128,9 +66,6 @@ CPRemoveTraitFontAction = 7;
{ {
if (!_availableFonts) if (!_availableFonts)
{ {
_availableFonts = [];
#if PLATFORM(DOM)
_CPFontDetectSpan = document.createElement("span"); _CPFontDetectSpan = document.createElement("span");
_CPFontDetectSpan.fontSize = "24px"; _CPFontDetectSpan.fontSize = "24px";
_CPFontDetectSpan.appendChild(document.createTextNode("mmmmmmmmmml")); _CPFontDetectSpan.appendChild(document.createTextNode("mmmmmmmmmml"));
@@ -142,16 +77,13 @@ CPRemoveTraitFontAction = 7;
_CPFontDetectReferenceFonts = _CPFontDetectPickTwoDifferentFonts(["monospace", "serif", "sans-serif", "cursive"]); _CPFontDetectReferenceFonts = _CPFontDetectPickTwoDifferentFonts(["monospace", "serif", "sans-serif", "cursive"]);
_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]);
} }
#else
// If there's no font detection, just assume all fonts are available.
_availableFonts = _CPFontDetectAllFonts;
#endif
} }
return _availableFonts; return _availableFonts;
} }
@@ -165,304 +97,43 @@ CPRemoveTraitFontAction = 7;
return _CPFontDetectFontAvailable(aFontName); return _CPFontDetectFontAvailable(aFontName);
} }
- (void)setSelectedFont:(CPFont)aFont isMultiple:(BOOL)aFlag
{
_selectedFont = aFont;
_multiple = aFlag;
// TODO Notify CPFontPanel when it exists.
}
- (CPFont)selectedFont
{
return _selectedFont;
}
- (int)weightOfFont:(CPFont)aFont
{
// TODO Weight 5 is a normal of book weight and 9 and above is bold, but it would be nice to be more
// precise than that.
return [aFont isBold] ? 9 : 5;
}
- (CPFontTraitMask)traitsOfFont:(CPFont)aFont
{
return ([aFont isBold] ? CPBoldFontMask : 0) | ([aFont isItalic] ? CPItalicFontMask : 0);
}
- (CPFont)convertFont:(CPFont)aFont
{
if (!_activeChange)
return aFont;
var addTraits = [_activeChange valueForKey:@"addTraits"];
if (addTraits)
aFont = [self convertFont:aFont toHaveTrait:addTraits];
return aFont;
}
- (CPFont)convertFont:(CPFont)aFont toHaveTrait:(CPFontTraitMask)addTraits
{
if (!aFont)
return nil;
var shouldBeBold = ([aFont isBold] || (addTraits & CPBoldFontMask)) && !(addTraits & CPUnboldFontMask),
shouldBeItalic = ([aFont isItalic] || (addTraits & CPItalicFontMask)) && !(addTraits & CPUnitalicFontMask),
shouldBeSize = [aFont size];
// XXX On the current platform there will always be a bold/italic version of each font, but still leave
// || aFont in here for future platforms.
aFont = [CPFont _fontWithName:[aFont familyName] size:shouldBeSize bold:shouldBeBold italic:shouldBeItalic] || aFont;
return aFont;
}
- (CPFont)convertFont:(CPFont)aFont toFace:(CPString)aTypeface
{
if (!aFont)
return nil;
var shouldBeBold = [aFont isBold],
shouldBeItalic = [aFont isItalic],
shouldBeSize = [aFont size];
aFont = [CPFont _fontWithName:aTypeface size:shouldBeSize bold:shouldBeBold italic:shouldBeItalic] || aFont;
return aFont;
}
- (@action)addFontTrait:(id)sender
{
var tag = [sender tag];
_activeChange = tag === nil ? @{} : @{ @"addTraits": tag };
_fontAction = CPAddTraitFontAction;
[self sendAction];
}
- (BOOL)sendAction
{
return [CPApp sendAction:_action to:_target from:self];
}
/*!
This method open the font panel, create it if necessary.
@param sender The object that sent the message.
*/
- (CPFontPanel)fontPanel:(BOOL)createIt
{
var panel = nil,
panelExists = [CPFontPanelFactory sharedFontPanelExists];
if ((panelExists) || (!panelExists && createIt))
panel = [CPFontPanelFactory sharedFontPanel];
return panel;
}
/*!
Convert a font to have the specified Font traits. The font is unchanged expect for the specified Font traits.
Using CPUnboldFontMask or CPUnitalicFontMask will respectively remove Bold and Italic traits.
@param aFont The font to convert.
@param fontTrait The new font traits mask.
@result The converted font or \c aFont if the conversion failed.
*/
- (CPFont)convertFont:(CPFont)aFont toHaveTrait:(CPFontTraitMask)fontTrait
{
var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
if (fontTrait & CPBoldFontMask)
symbolicTrait |= CPFontBoldTrait;
if (fontTrait & CPItalicFontMask)
symbolicTrait |= CPFontItalicTrait;
if (fontTrait & CPUnboldFontMask) /* FIXME: this only change CPFontSymbolicTrait what about CPFontWeightTrait */
symbolicTrait &= ~CPFontBoldTrait;
if (fontTrait & CPUnitalicFontMask)
symbolicTrait &= ~CPFontItalicTrait;
if (fontTrait & CPExpandedFontMask)
symbolicTrait |= CPFontExpandedTrait;
if (fontTrait & CPSmallCapsFontMask)
symbolicTrait |= CPFontSmallCapsTrait;
if (![attributes containsKey:CPFontTraitsAttribute])
[attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
forKey:CPFontSymbolicTrait]
forKey:CPFontTraitsAttribute];
else
[[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
forKey:CPFontSymbolicTrait];
return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
}
/*!
Convert a font to not have the specified Font traits. The font is unchanged expect for the specified Font traits.
@param aFont The font to convert.
@param fontTrait The font traits mask to remove.
@result The converted font or \c aFont if the conversion failed.
*/
- (CPFont)convertFont:(CPFont)aFont toNotHaveTrait:(CPFontTraitMask)fontTrait
{
var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
if ((fontTrait & CPBoldFontMask) || (fontTrait & CPUnboldFontMask)) /* FIXME: see convertFont:toHaveTrait: about CPFontWeightTrait */
symbolicTrait &= ~CPFontBoldTrait;
if ((fontTrait & CPItalicFontMask) || (fontTrait & CPUnitalicFontMask))
symbolicTrait &= ~CPFontItalicTrait;
if (fontTrait & CPExpandedFontMask)
symbolicTrait &= ~CPFontExpandedTrait;
if (fontTrait & CPSmallCapsFontMask)
symbolicTrait &= ~CPFontSmallCapsTrait;
if (![attributes containsKey:CPFontTraitsAttribute])
[attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
forKey:CPFontSymbolicTrait]
forKey:CPFontTraitsAttribute];
else
[[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
forKey:CPFontSymbolicTrait];
return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
}
/*!
Convert a font to have specified size. The font is unchanged expect for the specified size.
@param aFont The font to convert.
@param aSize The new font size.
@result The converted font or \c aFont if the conversion failed.
*/
- (CPFont)convertFont:(CPFont)aFont toSize:(float)aSize
{
var descriptor = [aFont fontDescriptor];
return [[aFont class] fontWithDescriptor: descriptor size:aSize]
}
- (void)orderFrontFontPanel:(id)sender
{
[[self fontPanel:YES] orderFront:sender];
}
- (void)modifyFont:(id)sender
{
_fontAction = [sender tag];
[self sendAction];
if (_selectedFont)
[self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
}
/*!
This method causes the receiver to send its action message.
@param sender The object that sent the message. (a Font panel)
*/
- (void)modifyFontViaPanel:(id)sender
{
_fontAction = CPViaPanelFontAction;
if (_selectedFont)
[self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
[self sendAction];
}
/*!
Convert a font according to current font changes, provided by the object that initiated the font change.
@param aFont The font to convert.
@result The converted font or \c aFont if the conversion failed.
*/
- (CPFont)convertFont:(CPFont)aFont
{
var newFont = nil;
switch (_fontAction)
{
case CPNoFontChangeAction:
newFont = aFont;
break;
case CPViaPanelFontAction:
newFont = [[self fontPanel:NO] panelConvertFont:aFont];
break;
case CPAddTraitFontAction:
newFont = aFont;
if (!_activeChange)
break;
var addTraits = [_activeChange valueForKey:@"addTraits"];
if (addTraits)
newFont = [self convertFont:aFont toHaveTrait:addTraits];
break;
case CPSizeUpFontAction:
newFont = [self convertFont:aFont toSize:[aFont size] + 1.0]; /* any limit ? */
break;
case CPSizeDownFontAction:
if ([aFont size] > 1)
newFont = [self convertFont:aFont toSize:[aFont size] - 1.0];
/* else CPBeep() :-p */
break;
default:
CPLog.trace(@"-[" + [self className] + " " + _cmd + "] unsupported font action: " + _fontAction + " aFont unchanged");
newFont = aFont;
break;
}
return newFont;
}
@end @end
var _CPFontDetectSpan, var _CPFontDetectSpan,
_CPFontDetectReferenceFonts, _CPFontDetectReferenceFonts,
_CPFontDetectAllFonts = [ _CPFontDetectAllFonts = [
/* "04b_21", "A Charming Font", "Abadi MT Condensed", "Abadi MT Condensed Extra Bold", "Abadi MT Condensed Light", "Academy Engraved LET", "Agency FB", "Alba", "Alba Matter", "Alba Super", "Algerian",*/ /* "04b_21","A Charming Font","Abadi MT Condensed","Abadi MT Condensed Extra Bold","Abadi MT Condensed Light","Academy Engraved LET","Agency FB","Alba","Alba Matter","Alba Super","Algerian",*/
"American Typewriter", "American Typewriter",
/* "Andale Mono", "Andale Mono IPA", "Andy", */ /* "Andale Mono","Andale Mono IPA","Andy", */
"Apple Chancery", "Arial", "Arial Black", "Arial Narrow", "Arial Rounded MT Bold", "Arial Unicode MS", "Apple Chancery","Arial","Arial Black","Arial Narrow","Arial Rounded MT Bold","Arial Unicode MS",
/* "Avant Garde", "Avantgarde", "Baby Kruffy", "Base 02", "Baskerville", "Baskerville Old Face", "Bauhaus 93", "Beesknees ITC", "Bell MT", "Berlin Sans FB", "Berlin Sans FB Demi", "Bernard MT Condensed", "Bickley Script",*/ /* "Avant Garde","Avantgarde","Baby Kruffy","Base 02","Baskerville","Baskerville Old Face","Bauhaus 93","Beesknees ITC","Bell MT","Berlin Sans FB","Berlin Sans FB Demi","Bernard MT Condensed","Bickley Script",*/
"Big Caslon", "Bitstream Vera Sans", "Bitstream Vera Sans Mono", "Bitstream Vera Serif", "Big Caslon","Bitstream Vera Sans","Bitstream Vera Sans Mono","Bitstream Vera Serif",
/* "Blackadder ITC", "Blackletter686 BT", "Bodoni MT", "Bodoni MT Black", "Bodoni MT Condensed", "Bodoni MT Poster Compressed", "Book Antiqua", "Bookman", "Bookman Old Style", "Bradley Hand ITC", "Braggadocio", "Britannic Bold", "Broadway", "Broadway BT",*/ /* "Blackadder ITC","Blackletter686 BT","Bodoni MT","Bodoni MT Black","Bodoni MT Condensed","Bodoni MT Poster Compressed","Book Antiqua","Bookman","Bookman Old Style","Bradley Hand ITC","Braggadocio","Britannic Bold","Broadway","Broadway BT",*/
"Brush Script MT", "Brush Script MT",
/* "BudHand", "CAMPBELL", "Calibri", "Californian FB", "Calisto MT", "Calligraph421 BT",*/ /* "BudHand","CAMPBELL","Calibri","Californian FB","Calisto MT","Calligraph421 BT",*/
"Cambria", "Cambria",
/* "Candara", "Capitals",*/ /* "Candara","Capitals",*/
"Caslon", "Castellar", "Cataneo BT", "Centaur", "Century Gothic", "Century Schoolbook", "Century Schoolbook L", "Caslon","Castellar","Cataneo BT","Centaur","Century Gothic","Century Schoolbook","Century Schoolbook L",
/* "Champignon", "Charcoal", "Charter", "Charter BT", "Chicago", "Chick", "Chiller", "ClearlyU", "Colonna MT",*/ /* "Champignon","Charcoal","Charter","Charter BT","Chicago","Chick","Chiller","ClearlyU","Colonna MT",*/
"Comic Sans", "Comic Sans MS", "Consolas", "Constantia", "Cooper Black", "Copperplate", "Copperplate Gothic Bold", "Copperplate Gothic Light", "Corbel", "Courier", "Courier New", "Comic Sans", "Comic Sans MS","Consolas","Constantia","Cooper Black","Copperplate","Copperplate Gothic Bold","Copperplate Gothic Light","Corbel","Courier","Courier New",
/* "Croobie", "Curlz MT", "Desdemona", "Didot", "DomBold BT", "Edwardian Script ITC", "Engravers MT", "Eras Bold ITC", "Eras Demi ITC", "Eras Light ITC", "Eras Medium ITC", "Eurostile", "FIRSTHOME", "Fat", "Felix Titling", "Fine Hand", "Fixed", "Footlight MT Light", "Forte", "Franklin Gothic Book", "Franklin Gothic Demi", "Franklin Gothic Demi Cond", "Franklin Gothic Heavy", "Franklin Gothic Medium", "Franklin Gothic Medium Cond", "Freestyle Script", "French Script MT", "Freshbot", "Frosty",*/ /* "Croobie","Curlz MT","Desdemona","Didot","DomBold BT","Edwardian Script ITC","Engravers MT","Eras Bold ITC","Eras Demi ITC","Eras Light ITC","Eras Medium ITC","Eurostile","FIRSTHOME","Fat","Felix Titling","Fine Hand","Fixed","Footlight MT Light","Forte","Franklin Gothic Book","Franklin Gothic Demi","Franklin Gothic Demi Cond","Franklin Gothic Heavy","Franklin Gothic Medium","Franklin Gothic Medium Cond","Freestyle Script","French Script MT","Freshbot","Frosty",*/
"Futura", "Futura",
/* "GENUINE", "Gadget", "Garamond",*/ /* "GENUINE","Gadget","Garamond",*/
"Geneva", "Georgia", "Georgia Ref", "Geeza Pro", "Gigi", "Gill Sans", "Gill Sans MT", "Gill Sans MT Condensed", "Gill Sans MT Ext Condensed Bold", "Gill Sans Ultra Bold", "Gill Sans Ultra Bold Condensed", "Geneva","Georgia","Georgia Ref", "Geeza Pro", "Gigi","Gill Sans","Gill Sans MT","Gill Sans MT Condensed","Gill Sans MT Ext Condensed Bold","Gill Sans Ultra Bold","Gill Sans Ultra Bold Condensed",
/* "GlooGun", "Gloucester MT Extra Condensed", "Goudy Old Style", "Goudy Stout", "Haettenschweiler", "Harlow Solid Italic", "Harrington",*/ /* "GlooGun","Gloucester MT Extra Condensed","Goudy Old Style","Goudy Stout","Haettenschweiler","Harlow Solid Italic","Harrington",*/
"Helvetica", "Helvetica Narrow", "Helvetica Neue", "Herculanum", "High Tower Text", "Highlight LET", "Hoefler Text", "Impact", "Imprint MT Shadow", "Helvetica","Helvetica Narrow","Helvetica Neue","Herculanum","High Tower Text","Highlight LET","Hoefler Text","Impact","Imprint MT Shadow",
/* "Informal Roman", "Jenkins v2.0", "John Handy LET", "Jokerman", "Jokerman LET", "Jokewood", "Juice ITC", "Kabel Ult BT", "Kartika", "Kino MT", "Kristen ITC", "Kunstler Script", "La Bamba LET", */ /* "Informal Roman","Jenkins v2.0","John Handy LET","Jokerman","Jokerman LET","Jokewood","Juice ITC","Kabel Ult BT","Kartika","Kino MT","Kristen ITC","Kunstler Script","La Bamba LET", */
"Lucida", "Lucida Bright", "Lucida Calligraphy", "Lucida Console", "Lucida Fax", "Lucida Grande", "Lucida Handwriting", "Lucida Sans", "Lucida Sans Typewriter", "Lucida Sans Unicode", "Lucida","Lucida Bright","Lucida Calligraphy","Lucida Console","Lucida Fax","Lucida Grande","Lucida Handwriting","Lucida Sans","Lucida Sans Typewriter","Lucida Sans Unicode",
/* "Luxi Mono", "Luxi Sans", "Luxi Serif", "MARKETPRO", "MS Reference Sans Serif", "MS Reference Serif", "Magneto", "Maiandra GD", */ /* "Luxi Mono","Luxi Sans","Luxi Serif","MARKETPRO","MS Reference Sans Serif","MS Reference Serif","Magneto","Maiandra GD", */
"Marker Felt", "Marker Felt",
/* "Matisse ITC", "Matura MT Script Capitals", "Mead Bold", "Mekanik LET", "Mercurius Script MT Bold", */ /* "Matisse ITC","Matura MT Script Capitals","Mead Bold","Mekanik LET","Mercurius Script MT Bold", */
"Microsoft Sans Serif", "Milano LET", "Minion Web", "MisterEarl BT", "Mistral", "Monaco", "Monotype Corsiva", "Monotype.com", "New Century Schoolbook", "New York", "News Gothic MT", "Microsoft Sans Serif","Milano LET","Minion Web","MisterEarl BT","Mistral","Monaco","Monotype Corsiva","Monotype.com","New Century Schoolbook","New York","News Gothic MT",
/* "Niagara Engraved", "Niagara Solid", "Nimbus Mono L", "Nimbus Roman No9 L", "OCR A Extended", "OCRB", "Odessa LET", "Old English Text MT", "OldDreadfulNo7 BT", "One Stroke Script LET", "Onyx", "Optima", "Orange LET", "Palace Script MT", "Palatino", "Palatino Linotype", */ /* "Niagara Engraved","Niagara Solid","Nimbus Mono L","Nimbus Roman No9 L","OCR A Extended","OCRB","Odessa LET","Old English Text MT","OldDreadfulNo7 BT","One Stroke Script LET","Onyx","Optima","Orange LET","Palace Script MT","Palatino","Palatino Linotype", */
"Papyrus", "Papyrus",
/* "ParkAvenue BT", "Pepita MT", "Perpetua", "Perpetua Titling MT", "Placard Condensed", "Playbill", "Poornut", "Pristina", "Pump Demi Bold LET", "Pussycat", "Quixley LET", "Rage Italic", "Rage Italic LET", "Ravie", "Rockwell", "Rockwell Condensed", "Rockwell Extra Bold", "Ruach LET", "Runic MT Condensed", "Sand", "Script MT Bold", "Scruff LET", "Segoe UI", "Showcard Gothic", "Skia", "Smudger LET", "Snap ITC", "Square721 BT", "Staccato222 BT", "Stencil", "Sylfaen", */ /* "ParkAvenue BT","Pepita MT","Perpetua","Perpetua Titling MT","Placard Condensed","Playbill","Poornut","Pristina","Pump Demi Bold LET","Pussycat","Quixley LET","Rage Italic","Rage Italic LET","Ravie","Rockwell","Rockwell Condensed","Rockwell Extra Bold","Ruach LET","Runic MT Condensed","Sand","Script MT Bold","Scruff LET","Segoe UI","Showcard Gothic","Skia","Smudger LET","Snap ITC","Square721 BT","Staccato222 BT","Stencil","Sylfaen", */
"Tahoma", "Techno", "Tempus Sans ITC", "Terminal", "Textile", "Times", "Times New Roman", "Tiranti Solid LET", "Trebuchet MS", "Tahoma","Techno","Tempus Sans ITC","Terminal","Textile","Times","Times New Roman","Tiranti Solid LET","Trebuchet MS",
/* "Tw Cen MT", "Tw Cen MT Condensed", "Tw Cen MT Condensed Extra Bold", "URW Antiqua T", "URW Bookman L", "URW Chancery L", "URW Gothic L", "URW Palladio L", "Univers", "University Roman LET", "Utopia", */ /* "Tw Cen MT","Tw Cen MT Condensed","Tw Cen MT Condensed Extra Bold","URW Antiqua T","URW Bookman L","URW Chancery L","URW Gothic L","URW Palladio L","Univers","University Roman LET","Utopia", */
"Verdana", "Verdana Ref", /* "Victorian LET", "Viner Hand ITC", "Vivaldi", "Vladimir Script", "Vrinda", "Weltron Urban", "Westwood LET", "Wide Latin", "Zapf Chancery", */ "Verdana","Verdana Ref", /* "Victorian LET","Viner Hand ITC","Vivaldi","Vladimir Script","Vrinda","Weltron Urban","Westwood LET","Wide Latin","Zapf Chancery", */
"Zapfino"]; "Zapfino"];
// Compare against the reference fonts. Return true if it produces a different size than at least one of them. // Compare against the reference fonts. Return true if it produces a different size than at least one of them.
@@ -472,7 +143,7 @@ var _CPFontDetectFontAvailable = function(font)
if (_CPFontDetectCompareFonts(_CPFontDetectReferenceFonts[i], font)) if (_CPFontDetectCompareFonts(_CPFontDetectReferenceFonts[i], font))
return true; return true;
return false; return false;
}; }
var _CPFontDetectCache = {}; var _CPFontDetectCache = {};
@@ -494,7 +165,7 @@ var _CPFontDetectCompareFonts = function(fontA, fontB)
bHeight = _CPFontDetectSpan.offsetHeight; 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)
@@ -504,6 +175,6 @@ var _CPFontDetectPickTwoDifferentFonts = function(candidates)
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]];
+254 -123
View File
@@ -1,6 +1,6 @@
/* /*
* CPGeometry.j * CPGeometry.j
* Foundation * AppKit
* *
* Created by Francisco Tolmasky. * Created by Francisco Tolmasky.
* Copyright 2008, 280 North, Inc. * Copyright 2008, 280 North, Inc.
@@ -20,109 +20,34 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "_CGGeometry.j" @import "CGGeometry.j"
@typedef CPRectEdge
CPMinXEdge = 0; CPMinXEdge = 0;
CPMinYEdge = 1; CPMinYEdge = 1;
CPMaxXEdge = 2; CPMaxXEdge = 2;
CPMaxYEdge = 3; CPMaxYEdge = 3;
/*! // FIXME: the rest!
Makes a CGPoint object out of two numbers provided as arguments
@group CGPoint
@param x the x-coordinate of the CGPoint
@param y the y-coordinate of the CGPoint
@return CGPoint a CGPoint with an X and Y coordinate equal to the function arguments
*/
CPMakePoint = CGPointMake; CPMakePoint = CGPointMake;
/*!
Creates and returns a new CGSize object from the provided dimensions.
@group CGSize
@param width the width for the new CGSize
@param height the height for the new CGSize
@return CGSize the new CGSize
*/
CPMakeSize = CGSizeMake; CPMakeSize = CGSizeMake;
/*!
Returns a CGRect made of the specified arguments
@group CGRect
@param x the x-coordinate of the rectangle's origin
@param y the y-coordinate of the rectangle's origin
@param width the width of the new rectangle
@param height the height of the new rectangle
@return CGRect the new rectangle
*/
CPMakeRect = CGRectMake; CPMakeRect = CGRectMake;
/*! /*!
Creates a copy of a specified point and returns the copy @addtogroup appkit
@group CGPoint @{
@param the point to be copied
@return CGPoint the copy of the provided CGPoint
*/ */
CPPointCreateCopy = CGPointMakeCopy;
/*! /*!
Tests whether the two CGPoints are equal to each other by comparing their Creates a copy of a specified point and returns the copy
\c x and \c y members. @group CGPoint
@group @CGPoint @param the point to be copied
@param lhsPoint the first CGPoint to check @return CGPoint the copy of the provided CGPoint
@param rhsPoint the second CGPoint to check */
@return BOOL \c YES if the two points have the same x's, and the same y's. function CPPointCreateCopy(aPoint)
*/
CPPointEqualToPoint = CGPointEqualToPoint;
/*!
Tests whether the CGPoint is contained by the CGRect.
@group CGPoint
@param aPoint the CGPoint to check
@param aRect the CGRect to check
@return BOOL \c YES if the rect contains the point.
*/
CPPointInRect = function(aPoint, aRect)
{ {
return CGRectContainsPoint(aRect, aPoint) return { x: aPoint.x, y: aPoint.y };
}; }
/*!
Test whether the two CGRects have the same origin and size
@group CGRect
@param lhsRect the first CGRect to compare
@param rhsRect the second CGRect to compare
@return BOOL \c YES if the two rectangles have the same origin and size. \c NO, otherwise.
*/
CPRectEqualToRect = CGRectEqualToRect;
/*!
Returns \c YES if the CGRect has no area.
The test is performed by checking if the width and height are both zero.
@group CGRect
@param aRect the CGRect to test
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
*/
CPRectIsEmpty = CGRectIsEmpty;
/*!
Returns a \c BOOL indicating whether CGRect \c possibleOuter
contains CGRect \c possibleInner.
@group CGRect
@param possibleOuter the CGRect to test if \c possibleInner is inside of
@param possibleInner the CGRect to test if it fits inside \c possibleOuter.
@return BOOL \c YES if \c possibleInner fits inside \c possibleOuter.
*/
CPRectContainsRect = CGRectContainsRect;
/*!
Returns the intersection of the two provided rectangles as a new rectangle
@group CGRect
@param lhsRect the first rectangle used for calculation
@param rhsRect the second rectangle used for calculation
@return CGRect the intersection of the two rectangles
*/
CPRectIntersection = CGRectIntersection;
/*! /*!
Makes a CGPoint object out of two numbers provided as arguments Makes a CGPoint object out of two numbers provided as arguments
@@ -130,8 +55,11 @@ CPRectIntersection = CGRectIntersection;
@param x the x-coordinate of the CGPoint @param x the x-coordinate of the CGPoint
@param y the y-coordinate of the CGPoint @param y the y-coordinate of the CGPoint
@return CGPoint a CGPoint with an X and Y coordinate equal to the function arguments @return CGPoint a CGPoint with an X and Y coordinate equal to the function arguments
*/ */
CPPointMake = CGPointMake; function CPPointMake(x, y)
{
return { x: x, y: y };
}
/*! /*!
Makes a CGRect with an origin and size equal to \c aRect less the \c dX/dY insets specified. Makes a CGRect with an origin and size equal to \c aRect less the \c dX/dY insets specified.
@@ -140,7 +68,11 @@ CPPointMake = CGPointMake;
@group CGRect @group CGRect
@return CGRect a rectangle like \c aRect with an inset @return CGRect a rectangle like \c aRect with an inset
*/ */
CPRectInset = CGRectInset; function CPRectInset(aRect, dX, dY)
{
return CPRectMake( aRect.origin.x + dX, aRect.origin.y + dY,
aRect.size.width - 2 * dX, aRect.size.height - 2 * dY);
}
/*! /*!
@group CGRect @group CGRect
@@ -148,7 +80,31 @@ CPRectInset = CGRectInset;
@return void @return void
@deprecated @deprecated
*/ */
CPRectIntegral = CGRectIntegral; function CPRectIntegral(aRect)
{
// FIXME!!!
alert("CPRectIntegral unimplemented");
}
/*!
Returns the intersection of the two provided rectangles as a new rectangle
@group CGRect
@param lhsRect the first rectangle used for calculation
@param rhsRect the second rectangle used for calculation
@return CGRect the intersection of the two rectangles
*/
function CPRectIntersection(lhsRect, rhsRect)
{
var intersection = CPRectMake(
Math.max(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)),
Math.max(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
0, 0);
intersection.size.width = Math.min(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)) - CPRectGetMinX(intersection);
intersection.size.height = Math.min(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect)) - CPRectGetMinY(intersection);
return CPRectIsEmpty(intersection) ? CPRectMakeZero() : intersection;
}
/*! /*!
Creates a copy of the provided rectangle Creates a copy of the provided rectangle
@@ -156,7 +112,10 @@ CPRectIntegral = CGRectIntegral;
@param aRect the CGRect that will be copied @param aRect the CGRect that will be copied
@return CGRect the rectangle copy @return CGRect the rectangle copy
*/ */
CPRectCreateCopy = CGRectCreateCopy; function CPRectCreateCopy(aRect)
{
return { origin: CPPointCreateCopy(aRect.origin), size: CPSizeCreateCopy(aRect.size) };
}
/*! /*!
Returns a CGRect made of the specified arguments Returns a CGRect made of the specified arguments
@@ -167,7 +126,10 @@ CPRectCreateCopy = CGRectCreateCopy;
@param height the height of the new rectangle @param height the height of the new rectangle
@return CGRect the new rectangle @return CGRect the new rectangle
*/ */
CPRectMake = CGRectMake; function CPRectMake(x, y, width, height)
{
return { origin: CPPointMake(x, y), size: CPSizeMake(width, height) };
}
/*! /*!
Creates a new rectangle with its origin offset by \c dX and \c dY. Creates a new rectangle with its origin offset by \c dX and \c dY.
@@ -177,14 +139,36 @@ CPRectMake = CGRectMake;
@param dY the amount added to the y-size of the new rectangle @param dY the amount added to the y-size of the new rectangle
@return CGRect the new rectangle with modified size @return CGRect the new rectangle with modified size
*/ */
CPRectOffset = CGRectOffset; function CPRectOffset(aRect, dX, dY)
{
return CPRectMake(aRect.origin.x + dX, aRect.origin.y + dY, aRect.size.width, aRect.size.height);
}
/*! /*!
@group CGRect @group CGRect
@param aRect a CGRect @param aRect a CGRect
@return CGRect @return CGRect
*/ */
CPRectStandardize = CGRectStandardize; function CPRectStandardize(aRect)
{
var width = CPRectGetWidth(aRect),
height = CPRectGetHeight(aRect),
standardized = CPRectCreateCopy(aRect);
if (width < 0.0)
{
standardized.origin.x += width;
standardized.size.width = -width;
}
if (height < 0.0)
{
standardized.origin.y += height;
standardized.size.height = -height;
}
return standardized;
}
/*! /*!
Returns the smallest rectangle that can contain the two argument \c CGRects. Returns the smallest rectangle that can contain the two argument \c CGRects.
@@ -193,7 +177,15 @@ CPRectStandardize = CGRectStandardize;
@param rhsRect the second CGRect to use for the union calculation @param rhsRect the second CGRect to use for the union calculation
@return CGRect the union rectangle @return CGRect the union rectangle
*/ */
CPRectUnion = CGRectUnion; function CPRectUnion(lhsRect, rhsRect)
{
var minX = Math.min(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)),
minY = Math.min(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
maxX = Math.max(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)),
maxY = Math.max(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect));
return CPRectMake(minX, minY, maxX - minX, maxY - minY);
}
/*! /*!
Creates and returns a copy of the provided CGSize Creates and returns a copy of the provided CGSize
@@ -201,7 +193,10 @@ CPRectUnion = CGRectUnion;
@param aSize the CGSize to copy @param aSize the CGSize to copy
@return CGSize the copy of the CGSize @return CGSize the copy of the CGSize
*/ */
CPSizeCreateCopy = CGSizeCreateCopy; function CPSizeCreateCopy(aSize)
{
return { width: aSize.width, height: aSize.height };
}
/*! /*!
Creates and returns a new CGSize object from the provided dimensions. Creates and returns a new CGSize object from the provided dimensions.
@@ -210,7 +205,10 @@ CPSizeCreateCopy = CGSizeCreateCopy;
@param height the height for the new CGSize @param height the height for the new CGSize
@return CGSize the new CGSize @return CGSize the new CGSize
*/ */
CPSizeMake = CGSizeMake; function CPSizeMake(width, height)
{
return { width: width, height: height };
}
/*! /*!
Returns \c YES if the CGRect, \c aRect, contains Returns \c YES if the CGRect, \c aRect, contains
@@ -220,63 +218,132 @@ CPSizeMake = CGSizeMake;
@group CGRect @group CGRect
@return BOOL \c YES if the rectangle contains the point, \c NO otherwise. @return BOOL \c YES if the rectangle contains the point, \c NO otherwise.
*/ */
CPRectContainsPoint = CGRectContainsPoint; function CPRectContainsPoint(aRect, aPoint)
{
return aPoint.x >= CPRectGetMinX(aRect) &&
aPoint.y >= CPRectGetMinY(aRect) &&
aPoint.x < CPRectGetMaxX(aRect) &&
aPoint.y < CPRectGetMaxY(aRect);
}
/*!
Returns a \c BOOL indicating whether CGRect \c possibleOuter
contains CGRect \c possibleInner.
@group CGRect
@param possibleOuter the CGRect to test if \c possibleInner is inside of
@param possibleInner the CGRect to test if it fits inside \c possibleOuter.
@return BOOL \c YES if \c possibleInner fits inside \c possibleOuter.
*/
function CPRectContainsRect(possibleOuter, possibleInner)
{
return CGRectContainsRect(possibleOuter, possibleInner);
}
/*!
Tests whether the two CGPoints are equal to each other by comparing their
\c x and \c y members.
@group @CGPoint
@param lhsPoint the first CGPoint to check
@param rhsPoint the second CGPoint to check
@return BOOL \c YES if the two points have the same x's, and the same y's.
*/
function CPPointEqualToPoint(lhsPoint, rhsPoint)
{
return lhsPoint.x == rhsPoint.x && lhsPoint.y == rhsPoint.y;
}
/*!
Test whether the two CGRects have the same origin and size
@group CGRect
@param lhsRect the first CGRect to compare
@param rhsRect the second CGRect to compare
@return BOOL \c YES if the two rectangles have the same origin and size. \c NO, otherwise.
*/
function CPRectEqualToRect(lhsRect, rhsRect)
{
return CPPointEqualToPoint(lhsRect.origin, rhsRect.origin) &&
CPSizeEqualToSize(lhsRect.size, rhsRect.size);
}
/*! /*!
@group CGRect @group CGRect
@param aRect a CGRect @param aRect a CGRect
@return int @return int
*/ */
CPRectGetHeight = CGRectGetHeight; function CPRectGetHeight(aRect)
{
return aRect.size.height;
}
/*! /*!
@group CGRect @group CGRect
@param aRect a CGRect @param aRect a CGRect
@return int @return int
*/ */
CPRectGetMaxX = CGRectGetMaxX; function CPRectGetMaxX(aRect)
{
return aRect.origin.x + aRect.size.width;
}
/*! /*!
@group CGRect @group CGRect
@param aRect a CGRect @param aRect a CGRect
@return int @return int
*/ */
CPRectGetMaxY = CGRectGetMaxY; function CPRectGetMaxY(aRect)
{
return aRect.origin.y + aRect.size.height;
}
/*! /*!
@group CGRect @group CGRect
@param aRect a CGRect @param aRect a CGRect
@return float @return float
*/ */
CPRectGetMidX = CGRectGetMidX; function CPRectGetMidX(aRect)
{
return aRect.origin.x + (aRect.size.width) / 2.0;
}
/*! /*!
@group CGRect @group CGRect
@param aRect a CGRect @param aRect a CGRect
@return float @return float
*/ */
CPRectGetMidY = CGRectGetMidY; function CPRectGetMidY(aRect)
{
return aRect.origin.y + (aRect.size.height) / 2.0;
}
/*! /*!
@group CGRect @group CGRect
@param aRect a CGRect @param aRect a CGRect
@return int @return int
*/ */
CPRectGetMinX = CGRectGetMinX; function CPRectGetMinX(aRect)
{
return aRect.origin.x;
}
/*! /*!
@group CGRect @group CGRect
@param aRect a CGRect @param aRect a CGRect
@return int @return int
*/ */
CPRectGetMinY = CGRectGetMinY; function CPRectGetMinY(aRect)
{
return aRect.origin.y;
}
/*! /*!
@group CGRect @group CGRect
@param aRect a CGRect @param aRect a CGRect
@return int @return int
*/ */
CPRectGetWidth = CGRectGetWidth; function CPRectGetWidth(aRect)
{
return aRect.size.width;
}
/*! /*!
Returns \c YES if the two rectangles intersect Returns \c YES if the two rectangles intersect
@@ -285,7 +352,22 @@ CPRectGetWidth = CGRectGetWidth;
@param rhsRect the second CGRect @param rhsRect the second CGRect
@return BOOL \c YES if the two rectangles have any common spaces, and \c NO, otherwise. @return BOOL \c YES if the two rectangles have any common spaces, and \c NO, otherwise.
*/ */
CPRectIntersectsRect = CGRectIntersectsRect; function CPRectIntersectsRect(lhsRect, rhsRect)
{
return !CPRectIsEmpty(CPRectIntersection(lhsRect, rhsRect));
}
/*!
Returns \c YES if the CGRect has no area.
The test is performed by checking if the width and height are both zero.
@group CGRect
@param aRect the CGRect to test
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
*/
function CPRectIsEmpty(aRect)
{
return aRect.size.width <= 0.0 || aRect.size.height <= 0.0;
}
/*! /*!
Returns \c YES if the CGRect has no area. Returns \c YES if the CGRect has no area.
@@ -293,7 +375,10 @@ CPRectIntersectsRect = CGRectIntersectsRect;
@group CGRect @group CGRect
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise. @return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
*/ */
CPRectIsNull = CGRectIsNull; function CPRectIsNull(aRect)
{
return aRect.size.width <= 0.0 || aRect.size.height <= 0.0;
}
/*! /*!
Creates two rectangles -- slice and rem -- from inRect, by dividing inRect Creates two rectangles -- slice and rem -- from inRect, by dividing inRect
@@ -304,7 +389,10 @@ CPRectIsNull = CGRectIsNull;
@group CGRect @group CGRect
*/ */
CPDivideRect = CGRectDivide; function CPDivideRect(inRect, slice, rem, amount, edge)
{
CGRectDivide(inRect, slice, rem, amount, edge);
}
/*! /*!
Returns \c YES if the two CGSizes are identical. Returns \c YES if the two CGSizes are identical.
@@ -313,7 +401,10 @@ CPDivideRect = CGRectDivide;
@param rhsSize the second CGSize to compare @param rhsSize the second CGSize to compare
@return BOOL \c YES if the two sizes are identical. \c NO, otherwise. @return BOOL \c YES if the two sizes are identical. \c NO, otherwise.
*/ */
CPSizeEqualToSize = CGSizeEqualToSize; function CPSizeEqualToSize(lhsSize, rhsSize)
{
return lhsSize.width == rhsSize.width && lhsSize.height == rhsSize.height;
}
/*! /*!
Returns a human readable string of the provided CGPoint. Returns a human readable string of the provided CGPoint.
@@ -321,7 +412,10 @@ CPSizeEqualToSize = CGSizeEqualToSize;
@param aPoint the point to represent @param aPoint the point to represent
@return CGPoint a string representation of the CGPoint @return CGPoint a string representation of the CGPoint
*/ */
CPStringFromPoint = CGStringFromPoint; function CPStringFromPoint(aPoint)
{
return "{" + aPoint.x + ", " + aPoint.y + "}";
}
/*! /*!
Returns a human readable string of the provided CGSize. Returns a human readable string of the provided CGSize.
@@ -329,7 +423,10 @@ CPStringFromPoint = CGStringFromPoint;
@param aSize the size to represent @param aSize the size to represent
@return CGSize a string representation of the CGSize @return CGSize a string representation of the CGSize
*/ */
CPStringFromSize = CGStringFromSize; function CPStringFromSize(aSize)
{
return "{" + aSize.width + ", " + aSize.height + "}";
}
/*! /*!
Returns a human readable string of the provided CGRect. Returns a human readable string of the provided CGRect.
@@ -337,7 +434,10 @@ CPStringFromSize = CGStringFromSize;
@param aRect the rectangle to represent @param aRect the rectangle to represent
@return CGString the string representation of the rectangle @return CGString the string representation of the rectangle
*/ */
CPStringFromRect = CGStringFromRect; function CPStringFromRect(aRect)
{
return "{" + CPStringFromPoint(aRect.origin) + ", " + CPStringFromSize(aRect.size) + "}";
}
/*! /*!
Returns a CGPoint from a string with a comma separated pair of integers. Returns a CGPoint from a string with a comma separated pair of integers.
@@ -345,7 +445,12 @@ CPStringFromRect = CGStringFromRect;
@param aString a string containing two comma separated integers @param aString a string containing two comma separated integers
@return CGPoint the point object created from the string @return CGPoint the point object created from the string
*/ */
CPPointFromString = CGPointFromString; function CPPointFromString(aString)
{
var comma = aString.indexOf(',');
return { x:parseFloat(aString.substr(1, comma - 1), 10), y:parseFloat(aString.substring(comma + 1, aString.length), 10) };
}
/*! /*!
Returns a CGSize from a string containing a pair of comma separated integers. Returns a CGSize from a string containing a pair of comma separated integers.
@@ -353,7 +458,12 @@ CPPointFromString = CGPointFromString;
@param aString a string containing two comma separated integers @param aString a string containing two comma separated integers
@return CGSize the size object created from the string @return CGSize the size object created from the string
*/ */
CPSizeFromString = CGSizeFromString; function CPSizeFromString(aString)
{
var comma = aString.indexOf(',');
return { width:parseFloat(aString.substr(1, comma - 1), 10), height:parseFloat(aString.substring(comma + 1, aString.length), 10) };
}
/*! /*!
Returns a CGRect created from a string. Returns a CGRect created from a string.
@@ -361,32 +471,53 @@ CPSizeFromString = CGSizeFromString;
@param aString a string in the form generated by \c CPStringFromRect @param aString a string in the form generated by \c CPStringFromRect
@return CGRect the rectangle created from the string @return CGRect the rectangle created from the string
*/ */
CPRectFromString = CGRectFromString; function CPRectFromString(aString)
{
var comma = aString.indexOf(',', aString.indexOf(',') + 1);
return { origin:CPPointFromString(aString.substr(1, comma - 1)), size:CPSizeFromString(aString.substring(comma + 2, aString.length)) };
}
/*! /*!
@group CGPoint @group CGPoint
@param anEvent @param anEvent
@return CGPoint @return CGPoint
*/ */
CPPointFromEvent = CGPointFromEvent; function CPPointFromEvent(anEvent)
{
return CPPointMake(anEvent.clientX, anEvent.clientY, 0);
}
/*! /*!
Returns a zero sized CGSize. Returns a zero sized CGSize.
@group CGSize @group CGSize
@return CGSize a size object with zeros for \c width and \c height @return CGSize a size object with zeros for \c width and \c height
*/ */
CPSizeMakeZero = CGSizeMakeZero; function CPSizeMakeZero()
{
return CPSizeMake(0, 0);
}
/*! /*!
Returns a rectangle at origin \c (0,0) and size of \c (0,0). Returns a rectangle at origin \c (0,0) and size of \c (0,0).
@group CGRect @group CGRect
@return CGRect a zeroed out CGRect @return CGRect a zeroed out CGRect
*/ */
CPRectMakeZero = CGRectMakeZero; function CPRectMakeZero()
{
return CPRectMake(0, 0, 0, 0);
}
/*! /*!
Returns a point located at \c (0, 0). Returns a point located at \c (0, 0).
@group CGPoint @group CGPoint
@return CGPoint a point located at \c (0, 0) @return CGPoint a point located at \c (0, 0)
*/ */
CPPointMakeZero = CGPointMakeZero; function CPPointMakeZero()
{
return CPPointMake(0, 0, 0);
}
/*!
@}
*/
-169
View File
@@ -1,169 +0,0 @@
/*
* CPGradient.j
* AppKit
*
* Created by Alexander Ljungberg.
* Copyright 2012, SlevenBits Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPObject.j>
@import <Foundation/CPException.j>
@import <Foundation/CPMutableArray.j>
@import "CGColor.j"
@import "CGColorSpace.j"
@import "CGGradient.j"
@import "CPBezierPath.j"
@import "CPGraphicsContext.j"
CPGradientDrawsBeforeStartingLocation = kCGGradientDrawsBeforeStartLocation;
CPGradientDrawsAfterEndingLocation = kCGGradientDrawsAfterEndLocation;
/*!
A class for drawing linear and radial gradients with a convenient API.
*/
@implementation CPGradient : CPObject
{
CGGradient _gradient;
}
- (id)initWithStartingColor:(CPColor)startingColor endingColor:(CPColor)endingColor
{
return [self initWithColors:[startingColor, endingColor]];
}
- (id)initWithColors:(CPArray)someColors
{
var count = [someColors count];
if (count < 2)
[CPException raise:CPInvalidArgumentException reason:@"at least 2 colors required"];
var distance = 1.0 / (count - 1),
locations = [CPMutableArray array],
location = 0.0;
for (var i = 0; i < count; i++)
{
[locations addObject:location];
location += distance;
}
return [self initWithColors:someColors atLocations:locations colorSpace:nil];
}
- (id)initWithColors:(CPArray)someColors atLocations:(CPArray)someLocations colorSpace:(CPColorSpace)aColorSpace
{
if (self = [super init])
{
var colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB,
cgColors = [someColors arrayByApplyingBlock:function(color)
{
return CGColorCreate(colorSpace, [color components])
}];
_gradient = CGGradientCreateWithColors(colorSpace, cgColors, someLocations);
}
return self;
}
- (void)drawInRect:(CGRect)rect angle:(float)angle
{
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(ctx);
CGContextClipToRect(ctx, rect);
CGContextAddRect(ctx, rect);
[self _drawInRect:rect atAngle:angle];
CGContextRestoreGState(ctx);
}
/*!
@ignore like draw in rect but apply no clipping.
*/
- (void)_drawInRect:(CGRect)rect atAngle:(float)angle
{
var ctx = [[CPGraphicsContext currentContext] graphicsPort],
startPoint,
endPoint;
// Modulo of negative values doesn't work as expected in JS.
angle = ((angle % 360.0) + 360.0) % 360.0;
if (angle < 90.0)
startPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
else if (angle < 180.0)
startPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
else if (angle < 270.0)
startPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
else
startPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
// A line segment comes out of the starting point at the given angle, with the first colour
// at the starting point and the last at the end. To do what drawInRect: is supposed to do
// we want the opposite corner of the starting corner to just reach the final colour stop.
// So when the angle isn't a right angle, the segment has to extend beyond the edge of the
// rectangle just far enough. This is hard to describe without a picture but if we place
// another line through the opposite corner at -90 degrees, it'll help form some triangles
// from which we can derive this formula:
//
// length = cos(angle) * (rectWidth + rectHeight * tan(angle))
//
// This simplifies down to (in the first quadrant) rectWidth * cos(a) + rectHeight * sin(a).
var radians = PI * angle / 180.0,
length = ABS(CGRectGetWidth(rect) * COS(radians)) + ABS(CGRectGetHeight(rect) * SIN(radians));
endPoint = CGPointMake(startPoint.x + length * COS(radians),
startPoint.y + length * SIN(radians));
[self drawFromPoint:startPoint toPoint:endPoint options:CPGradientDrawsBeforeStartingLocation | CPGradientDrawsAfterEndingLocation];
}
- (void)drawInBezierPath:(CPBezierPath)aPath angle:(float)anAngle
{
[CPGraphicsContext saveGraphicsState];
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
// Nail down the path which CGContextDrawLinearGradient will cause to be filled.
// Note we don't do this by clipping to the path and then calling drawInRect:atAngle:
// as this would cut off any antialias of the drawing, plus any active context shadow.
CGContextBeginPath(ctx);
CGContextAddPath(ctx, aPath._path);
CGContextSetLineWidth(ctx, [aPath lineWidth]);
CGContextClosePath(ctx);
[self _drawInRect:[aPath bounds] atAngle:anAngle];
[CPGraphicsContext restoreGraphicsState];
}
- (void)drawFromPoint:(NSPoint)startingPoint toPoint:(NSPoint)endingPoint options:(NSGradientDrawingOptions)options
{
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
// TODO kCGGradientDrawsBeforeStartLocation and kCGGradientDrawsAfterEndLocation are not actually supported
// by CGContextDrawLinearGradient yet.
CGContextDrawLinearGradient(ctx, _gradient, startingPoint, endingPoint, options);
}
@end
+18 -31
View File
@@ -23,16 +23,6 @@
@import "CPColor.j" @import "CPColor.j"
@import "CPGraphicsContext.j" @import "CPGraphicsContext.j"
CPCalibratedWhiteColorSpace = @"CalibratedWhiteColorSpace";
CPCalibratedBlackColorSpace = @"CalibratedBlackColorSpace";
CPCalibratedRGBColorSpace = @"CalibratedRGBColorSpace";
CPDeviceWhiteColorSpace = @"DeviceWhiteColorSpace";
CPDeviceBlackColorSpace = @"DeviceBlackColorSpace";
CPDeviceRGBColorSpace = @"DeviceRGBColorSpace";
CPDeviceCMYKColorSpace = @"DeviceCMYKColorSpace";
CPNamedColorSpace = @"NamedColorSpace";
CPPatternColorSpace = @"PatternColorSpace";
CPCustomColorSpace = @"CustomColorSpace";
function CPDrawTiledRects( function CPDrawTiledRects(
/* CGRect */ boundsRect, /* CGRect */ boundsRect,
@@ -43,10 +33,10 @@ function CPDrawTiledRects(
if (sides.length != grays.length) if (sides.length != grays.length)
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and grays (length: " + grays.length + ") must have the same length."]; [CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and grays (length: " + grays.length + ") must have the same length."];
var colors = [grays arrayByApplyingBlock:function(gray) var colors = [];
{
return [CPColor colorWithCalibratedWhite:gray alpha:1.0]; for (var i = 0; i < grays.length; ++i)
}]; colors.push([CPColor colorWithCalibratedWhite:grays[i] alpha:1.0]);
return CPDrawColorTiledRects(boundsRect, clipRect, sides, colors); return CPDrawColorTiledRects(boundsRect, clipRect, sides, colors);
} }
@@ -60,9 +50,9 @@ function CPDrawColorTiledRects(
if (sides.length != colors.length) if (sides.length != colors.length)
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and colors (length: " + colors.length + ") must have the same length."]; [CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and colors (length: " + colors.length + ") must have the same length."];
var resultRect = CGRectMakeCopy(boundsRect), var resultRect = _CGRectMakeCopy(boundsRect),
slice = CGRectMakeZero(), slice = _CGRectMakeZero(),
remainder = CGRectMakeZero(), remainder = _CGRectMakeZero(),
context = [[CPGraphicsContext currentContext] graphicsPort]; context = [[CPGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(context); CGContextSaveGState(context);
@@ -77,34 +67,31 @@ function CPDrawColorTiledRects(
slice = CGRectIntersection(slice, clipRect); slice = CGRectIntersection(slice, clipRect);
// Cocoa docs say that only slices that are within the clipRect are actually drawn // Cocoa docs say that only slices that are within the clipRect are actually drawn
if (CGRectIsEmpty(slice)) if (_CGRectIsEmpty(slice))
continue; continue;
var minX, var minX, maxX, minY, maxY;
maxX,
minY,
maxY;
if (side == CPMinXEdge || side == CPMaxXEdge) if (side == CPMinXEdge || side == CPMaxXEdge)
{ {
// Make sure we have at least 1 pixel to draw a line // Make sure we have at least 1 pixel to draw a line
if (CGRectGetWidth(slice) < 1.0) if (_CGRectGetWidth(slice) < 1.0)
continue; continue;
minX = CGRectGetMinX(slice) + 0.5; minX = _CGRectGetMinX(slice) + 0.5;
maxX = minX; maxX = minX;
minY = CGRectGetMinY(slice); minY = _CGRectGetMinY(slice);
maxY = CGRectGetMaxY(slice); maxY = _CGRectGetMaxY(slice);
} }
else // CPMinYEdge || CPMaxYEdge else // CPMinYEdge || CPMaxYEdge
{ {
// Make sure we have at least 1 pixel to draw a line // Make sure we have at least 1 pixel to draw a line
if (CGRectGetHeight(slice) < 1.0) if (_CGRectGetHeight(slice) < 1.0)
continue; continue;
minX = CGRectGetMinX(slice); minX = _CGRectGetMinX(slice);
maxX = CGRectGetMaxX(slice); maxX = _CGRectGetMaxX(slice);
minY = CGRectGetMinY(slice) + 0.5; minY = _CGRectGetMinY(slice) + 0.5;
maxY = minY; maxY = minY;
} }
@@ -118,4 +105,4 @@ function CPDrawColorTiledRects(
CGContextRestoreGState(context); CGContextRestoreGState(context);
return resultRect; return resultRect;
} }
+1 -48
View File
@@ -25,8 +25,7 @@
@import "CGContext.j" @import "CGContext.j"
var CPGraphicsContextCurrent = nil, var CPGraphicsContextCurrent = nil;
CPGraphicsContextThreadStack = nil;
/*! /*!
@ingroup appkit @ingroup appkit
@@ -53,29 +52,6 @@ var CPGraphicsContextCurrent = nil,
CPGraphicsContextCurrent = aGraphicsContext; CPGraphicsContextCurrent = aGraphicsContext;
} }
+ (void)saveGraphicsState
{
if (!CPGraphicsContextCurrent)
return;
if (!CPGraphicsContextThreadStack)
CPGraphicsContextThreadStack = [CPMutableArray array];
[CPGraphicsContextThreadStack addObject:CPGraphicsContextCurrent];
[CPGraphicsContextCurrent saveGraphicsState];
}
+ (void)restoreGraphicsState
{
var lastContext = [CPGraphicsContextThreadStack lastObject];
if (lastContext)
{
[lastContext restoreGraphicsState];
[CPGraphicsContextThreadStack removeLastObject];
}
}
/*! /*!
Creates a graphics context with a provided port. Creates a graphics context with a provided port.
@param aContext the context to initialize with @param aContext the context to initialize with
@@ -110,27 +86,4 @@ var CPGraphicsContextCurrent = nil,
return _graphicsPort; return _graphicsPort;
} }
/*!
(Not implemented. Always returns Cappuccino's default, `YES`.)
Return `isFlipped` of the receiver's focused view, unless no view is focused in which case the receiver's
`flipped` flag, as set by `graphicsContextWithGraphicsPort:flipped:`, is returned.
Note: Cappuccino is flipped by default.
*/
- (BOOL)isFlipped
{
return YES;
}
- (void)saveGraphicsState
{
CGContextSaveGState(_graphicsPort);
}
- (void)restoreGraphicsState
{
CGContextRestoreGState(_graphicsPort);
}
@end @end
+26 -393
View File
@@ -25,25 +25,10 @@
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPRunLoop.j> @import <Foundation/CPRunLoop.j>
@import <Foundation/CPString.j> @import <Foundation/CPString.j>
@import <Foundation/CPData.j>
@import "CGGeometry.j" @import "CPGeometry.j"
@import "CPCompatibility.j"
@protocol CPImageDelegate <CPObject>
@optional
- (void)imageDidLoad:(CPImage)anImage;
- (void)imageDidError:(CPImage)anImage;
- (void)imageDidAbort:(CPImage)anImage;
@end
var CPImageDelegate_imageDidLoad_ = 1 << 1,
CPImageDelegate_imageDidError_ = 1 << 2,
CPImageDelegate_imageDidAbort_ = 1 << 3;
CPImageLoadStatusInitialized = 0; CPImageLoadStatusInitialized = 0;
CPImageLoadStatusLoading = 1; CPImageLoadStatusLoading = 1;
CPImageLoadStatusCompleted = 2; CPImageLoadStatusCompleted = 2;
@@ -59,50 +44,20 @@ CPImageNameColorPanel = @"CPImageNameColorPanel";
CPImageNameColorPanelHighlighted = @"CPImageNameColorPanelHighlighted"; CPImageNameColorPanelHighlighted = @"CPImageNameColorPanelHighlighted";
var imagesForNames = { }, var imagesForNames = { },
AppKitImageForNames = { }, AppKitImageForNames = { };
ImageDescriptionFormat = "%s {\n filename: \"%s\",\n size: { width:%f, height:%f }\n}";
AppKitImageForNames[CPImageNameColorPanel] = CGSizeMake(26.0, 29.0); AppKitImageForNames[CPImageNameColorPanel] = CGSizeMake(26.0, 29.0);
AppKitImageForNames[CPImageNameColorPanelHighlighted] = CGSizeMake(26.0, 29.0); AppKitImageForNames[CPImageNameColorPanelHighlighted] = CGSizeMake(26.0, 29.0);
/*! function CPImageInBundle(aFilename, aSize, aBundle)
Returns a resource image with a relative path and size
in a bundle.
@param filename A filename or relative path to a resource image.
@param width Width of the image. May be omitted.
@param height Height of the image. May be omitted if width is omitted.
@param size Instead of passing width/height, a CGSize may be passed.
@param bundle Bundle in which the image resource can be found.
If omitted, defaults to the main bundle.
@return CPImage
*/
function CPImageInBundle()
{ {
var filename = arguments[0], if (!aBundle)
size = nil, aBundle = [CPBundle mainBundle];
bundle = nil;
if (typeof(arguments[1]) === "number") if (aSize)
{ return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename] size:aSize];
if (arguments[1] !== nil && arguments[1] !== undefined)
size = CGSizeMake(arguments[1], arguments[2]);
bundle = arguments[3]; return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename]];
}
else if (typeof(arguments[1]) === "object")
{
size = arguments[1];
bundle = arguments[2];
}
if (!bundle)
bundle = [CPBundle mainBundle];
if (size)
return [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:filename] size:size];
return [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:filename]];
} }
function CPAppKitImage(aFilename, aSize) function CPAppKitImage(aFilename, aSize)
@@ -133,15 +88,14 @@ function CPAppKitImage(aFilename, aSize)
*/ */
@implementation CPImage : CPObject @implementation CPImage : CPObject
{ {
CGSize _size; CGSize _size;
CPString _filename; CPString _filename;
CPString _name; CPString _name;
id <CPImageDelegate> _delegate; id _delegate;
unsigned _loadStatus; unsigned _loadStatus;
unsigned _implementedDelegateMethods;
Image _image; Image _image;
} }
- (id)init - (id)init
@@ -151,24 +105,19 @@ function CPAppKitImage(aFilename, aSize)
/*! /*!
Initializes the image, by associating it with a filename. The image Initializes the image, by associating it with a filename. The image
denoted in \c aFilename is not actually loaded. It will be loaded denoted in \c aFilename is not actually loaded. It will
once needed. be loaded once needed.
@param aFilename the file containing the image @param aFilename the file containing the image
@param aSize the image's size @param aSize the image's size
@return the initialized image @return the initialized image
*/ */
- (id)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize - (id)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize
{ {
// Quietly return nil like in Cocoa, rather than crashing later.
if (aFilename === undefined || aFilename === nil)
return nil;
self = [super init]; self = [super init];
if (self) if (self)
{ {
_size = CGSizeMakeCopy(aSize); _size = CPSizeCreateCopy(aSize);
_filename = aFilename; _filename = aFilename;
_loadStatus = CPImageLoadStatusInitialized; _loadStatus = CPImageLoadStatusInitialized;
} }
@@ -208,20 +157,6 @@ function CPAppKitImage(aFilename, aSize)
return self; return self;
} }
/*!
Initializes the receiver with the specified data. The method loads the data into memory.
@param someData the CPData object representing the image
@return the initialized image
*/
- (id)initWithData:(CPData)someData
{
var base64 = [someData base64],
type = [base64 hasPrefix:@"/9j/4AAQSkZJRgABAQEASABIAAD/"] ? @"jpg" : @"png",
dataURL = "data:image/" + type + ";base64," + base64;
return [self initWithContentsOfFile:dataURL];
}
/*! /*!
Returns the path of the file associated with this image. Returns the path of the file associated with this image.
*/ */
@@ -230,37 +165,6 @@ function CPAppKitImage(aFilename, aSize)
return _filename; return _filename;
} }
/*!
Returns the data associated with this image.
@discussion Returns nil if the reciever was not initialized with -initWithData: and the browser does not support the canvas feature;
*/
- (CPData)data
{
#if PLATFORM(DOM)
var dataURL;
if ([_filename hasPrefix:@"data:image"])
dataURL = _filename;
else if (CPFeatureIsCompatible(CPHTMLCanvasFeature))
{
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
canvas.width = _image.width;
canvas.height = _image.height;
ctx.drawImage(_image, 0, 0);
dataURL = canvas.toDataURL("image/png");
}
else
return nil;
var base64 = dataURL.replace(/^data:image\/png;base64,/, "");
return [CPData dataWithBase64:base64];
#endif
}
/*! /*!
Sets the size of the image. Sets the size of the image.
@param the size of the image @param the size of the image
@@ -275,7 +179,7 @@ function CPAppKitImage(aFilename, aSize)
*/ */
- (CGSize)size - (CGSize)size
{ {
return CGSizeMakeCopy(_size); return _size;
} }
+ (id)imageNamed:(CPString)aName + (id)imageNamed:(CPString)aName
@@ -322,34 +226,13 @@ function CPAppKitImage(aFilename, aSize)
return _name; return _name;
} }
/*!
Returns the underlying Image element for a single image.
*/
- (Image)image
{
return _image;
}
/*! /*!
Sets the receiver's delegate. Sets the receiver's delegate.
@param the delegate @param the delegate
*/ */
- (void)setDelegate:(id <CPImageDelegate>)aDelegate - (void)setDelegate:(id)aDelegate
{ {
if (_delegate === aDelegate)
return;
_delegate = aDelegate; _delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(imageDidLoad:)])
_implementedDelegateMethods |= CPImageDelegate_imageDidLoad_;
if ([_delegate respondsToSelector:@selector(imageDidError:)])
_implementedDelegateMethods |= CPImageDelegate_imageDidError_;
if ([_delegate respondsToSelector:@selector(imageDidAbort:)])
_implementedDelegateMethods |= CPImageDelegate_imageDidAbort_;
} }
/*! /*!
@@ -397,7 +280,7 @@ function CPAppKitImage(aFilename, aSize)
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
} }
[self _derefFromImage]; [self _derefFromImage];
}; }
_image.onerror = function () _image.onerror = function ()
{ {
@@ -409,7 +292,7 @@ function CPAppKitImage(aFilename, aSize)
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
} }
[self _derefFromImage]; [self _derefFromImage];
}; }
_image.onabort = function () _image.onabort = function ()
{ {
@@ -421,7 +304,7 @@ function CPAppKitImage(aFilename, aSize)
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
} }
[self _derefFromImage]; [self _derefFromImage];
}; }
_image.src = _filename; _image.src = _filename;
@@ -431,11 +314,6 @@ function CPAppKitImage(aFilename, aSize)
#endif #endif
} }
- (BOOL)isSingleImage
{
return YES;
}
- (BOOL)isThreePartImage - (BOOL)isThreePartImage
{ {
return NO; return NO;
@@ -446,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
{ {
@@ -485,7 +345,7 @@ function CPAppKitImage(aFilename, aSize)
postNotificationName:CPImageDidLoadNotification postNotificationName:CPImageDidLoadNotification
object:self]; object:self];
if (_implementedDelegateMethods & CPImageDelegate_imageDidLoad_) if ([_delegate respondsToSelector:@selector(imageDidLoad:)])
[_delegate imageDidLoad:self]; [_delegate imageDidLoad:self];
} }
@@ -494,7 +354,7 @@ function CPAppKitImage(aFilename, aSize)
{ {
_loadStatus = CPImageLoadStatusReadError; _loadStatus = CPImageLoadStatusReadError;
if (_implementedDelegateMethods & CPImageDelegate_imageDidError_) if ([_delegate respondsToSelector:@selector(imageDidError:)])
[_delegate imageDidError:self]; [_delegate imageDidError:self];
} }
@@ -503,218 +363,12 @@ function CPAppKitImage(aFilename, aSize)
{ {
_loadStatus = CPImageLoadStatusCancelled; _loadStatus = CPImageLoadStatusCancelled;
if (_implementedDelegateMethods & CPImageDelegate_imageDidAbort_) if ([_delegate respondsToSelector:@selector(imageDidAbort:)])
[_delegate imageDidAbort:self]; [_delegate imageDidAbort:self];
} }
@end @end
#pragma mark -
#pragma mark CSS Theming
// The code below adds support for CSS theming with 100% compatibility with current theming system.
// The idea is to extend CPImage (and CPColor) with CSS components and adapt low level UI components to
// support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
//
// To create a CPImage that uses CSS, simply use the new class method :
// + (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
// where beforeDictionary & afterDictionary are related to ::before & ::after pseudo-elements.
// If you don't need them, just use the simplified class method :
// + (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary size:(CGSize)aSize
//
// Examples :
// regularImageNormal = [CPImage imageWithCSSDictionary:@{
// @"border-color": A3ColorActiveBorder,
// @"border-style": @"solid",
// @"border-width": @"1px",
// @"border-radius": @"50%",
// @"box-sizing": @"border-box",
// @"background-color": A3ColorBackgroundWhite,
// @"transition-duration": @"0.35s",
// @"transition-property": @"all",
// @"transition-timing-function": @"ease"
// }
// size:CGSizeMake(16,16)];
//
// imageSearch = [CPImage imageWithCSSDictionary:@{
// @"background-image": @"url(%%packed.png)",
// @"background-position": @"-16px -32px",
// @"background-repeat": @"no-repeat",
// @"background-size": @"100px 400px"
// }
// size:CGSizeMake(16,16)];
//
// Remark : Please note the special URL of the background image used in this example : url(%%packed.png)
// During theme loading, "%%" will be replaced by the path to the theme blend resources folder.
// Typically, a CSS theme will use some (rare) images all packed together in a single image resource (see packed.png in Aristo3 theme)
//
// Also, please note that if you don't use one of the CSS components, you can either set it to nil (best solution) or to an empty dictionary, like :
// aCssImage = [CPImage imageWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:@{ ... }];
//
// You can use -(BOOL)isCSSBased to determine how to cope with it in your code.
// -(BOOL)hasCSSDictionary, -(BOOL)hasCSSBeforeDictionary and -(BOOL)hasCSSAfterDictionary are convience methods you can use.
//
// Remark : -(DOMElement)applyCSSImageForView is meant to be used by low level UI widgets (like CPImageView and _CPImageAndTextView) to implement CSS theme support.
//
// In some circumstances, you may have to clear a CSS image. You can do this easily by replacing your current image with the special dummy empty CSS image :
// [CPImage dummyCSSImageOfSize:CGSizeMake(someWidth, someHeight)]
@implementation CPImage (CSSTheming)
{
CPDictionary _cssDictionary @accessors(property=cssDictionary);
CPDictionary _cssBeforeDictionary @accessors(property=cssBeforeDictionary);
CPDictionary _cssAfterDictionary @accessors(property=cssAfterDictionary);
}
+ (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary size:(CGSize)aSize
{
return [[CPImage alloc] initWithCSSDictionary:aDictionary beforeDictionary:nil afterDictionary:nil size:aSize];
}
+ (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
{
return [[CPImage alloc] initWithCSSDictionary:aDictionary beforeDictionary:beforeDictionary afterDictionary:afterDictionary size:aSize];
}
+ (CPImage)dummyCSSImageOfSize:(CGSize)aSize
{
// This is used to clear a previous CSS image
return [[CPImage alloc] initWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:nil size:aSize];
}
- (id)initWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
{
self = [super init];
if (self)
{
_size = CGSizeMakeCopy(aSize);
_filename = @"CSS image";
_loadStatus = CPImageLoadStatusCompleted;
_cssDictionary = aDictionary;
_cssBeforeDictionary = beforeDictionary;
_cssAfterDictionary = afterDictionary;
}
return self;
}
- (BOOL)isCSSBased
{
return !!(_cssDictionary || _cssBeforeDictionary || _cssAfterDictionary);
}
- (BOOL)hasCSSDictionary
{
return ([_cssDictionary count] > 0);
}
- (BOOL)hasCSSBeforeDictionary
{
return ([_cssBeforeDictionary count] > 0);
}
- (BOOL)hasCSSAfterDictionary
{
return ([_cssAfterDictionary count] > 0);
}
- (DOMElement)applyCSSImageForView:(CPView)aView onDOMElement:(DOMElement)aDOMElement styleNode:(DOMElement)aStyleNode previousState:(CPArrayRef)aPreviousStateRef
{
#if PLATFORM(DOM)
// First, restore previous CSS styling before applying the new one
var aPreviousState = @deref(aPreviousStateRef);
for (var i = 0, count = aPreviousState.length; i < count; i++)
aDOMElement.style[aPreviousState[i][0]] = aPreviousState[i][1];
aPreviousState = @[];
// Then apply new CSS styling
[_cssDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
{
[aPreviousState addObject:@[aKey, aDOMElement.style[aKey]]];
aDOMElement.style[aKey] = anObject;
}];
if (_cssBeforeDictionary || _cssAfterDictionary)
{
// We need to create a unique class name
var styleClassName = @".CP" + [aView UID],
styleContent = @"";
if (_cssBeforeDictionary)
{
styleContent += styleClassName + @"::before { ";
[_cssBeforeDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
{
styleContent += aKey + ": " + anObject + "; ";
}];
styleContent += "} ";
}
if (_cssAfterDictionary)
{
styleContent += styleClassName + @"::after { ";
[_cssAfterDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
{
styleContent += aKey + ": " + anObject + "; ";
}];
styleContent += "} ";
}
var styleDescription = document.createTextNode(styleContent);
if (!aStyleNode)
{
aStyleNode = document.createElement("style");
aView._DOMElement.insertBefore(aStyleNode, aView._DOMElement.firstChild);
aStyleNode.appendChild(styleDescription);
}
else
{
aStyleNode.replaceChild(styleDescription, aStyleNode.firstChild);
}
[aView setDOMClassName:@"CP"+[aView UID]];
}
else
{
// no before/after so remove aStyleNode if existing
if (aStyleNode)
{
aView._DOMElement.removeChild(aStyleNode);
aStyleNode = nil;
}
}
// Return actualised values
@deref(aPreviousStateRef) = aPreviousState;
return aStyleNode;
#endif
}
@end
var CPImageCSSDictionaryKey = @"CPImageCSSDictionaryKey",
CPImageCSSBeforeDictionaryKey = @"CPImageCSSBeforeDictionaryKey",
CPImageCSSAfterDictionaryKey = @"CPImageCSSAfterDictionaryKey";
#pragma mark -
@implementation CPImage (CPCoding) @implementation CPImage (CPCoding)
/*! /*!
@@ -724,10 +378,7 @@ var CPImageCSSDictionaryKey = @"CPImageCSSDictionaryKey",
*/ */
- (id)initWithCoder:(CPCoder)aCoder - (id)initWithCoder:(CPCoder)aCoder
{ {
if ([aCoder containsValueForKey:CPImageCSSDictionaryKey]) return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
return [self initWithCSSDictionary:[aCoder decodeObjectForKey:CPImageCSSDictionaryKey] beforeDictionary:[aCoder decodeObjectForKey:CPImageCSSBeforeDictionaryKey] afterDictionary:[aCoder decodeObjectForKey:CPImageCSSAfterDictionaryKey] size:[aCoder decodeSizeForKey:@"CPSize"]];
else
return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
} }
/*! /*!
@@ -738,14 +389,6 @@ var CPImageCSSDictionaryKey = @"CPImageCSSDictionaryKey",
{ {
[aCoder encodeObject:_filename forKey:@"CPFilename"]; [aCoder encodeObject:_filename forKey:@"CPFilename"];
[aCoder encodeSize:_size forKey:@"CPSize"]; [aCoder encodeSize:_size forKey:@"CPSize"];
// CSS Styling
if ([self isCSSBased])
{
[aCoder encodeObject:_cssDictionary forKey:CPImageCSSDictionaryKey];
[aCoder encodeObject:_cssBeforeDictionary forKey:CPImageCSSBeforeDictionaryKey];
[aCoder encodeObject:_cssAfterDictionary forKey:CPImageCSSAfterDictionaryKey];
}
} }
@end @end
@@ -784,11 +427,6 @@ var CPImageCSSDictionaryKey = @"CPImageCSSDictionaryKey",
return _isVertical; return _isVertical;
} }
- (BOOL)isSingleImage
{
return NO;
}
- (BOOL)isThreePartImage - (BOOL)isThreePartImage
{ {
return YES; return YES;
@@ -853,11 +491,6 @@ var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey",
return _imageSlices; return _imageSlices;
} }
- (BOOL)isSingleImage
{
return NO;
}
- (BOOL)isThreePartImage - (BOOL)isThreePartImage
{ {
return NO; return NO;
+90 -178
View File
@@ -26,10 +26,11 @@
@import "CPImage.j" @import "CPImage.j"
@import "CPShadowView.j" @import "CPShadowView.j"
@global CPImagesPboardType
@global appkit_tag_dom_elements
@typedef CPImageAlignment CPScaleProportionally = 0;
CPScaleToFit = 1;
CPScaleNone = 2;
CPImageAlignCenter = 0; CPImageAlignCenter = 0;
CPImageAlignTop = 1; CPImageAlignTop = 1;
CPImageAlignTopLeft = 2; CPImageAlignTopLeft = 2;
@@ -40,7 +41,15 @@ CPImageAlignBottomLeft = 6;
CPImageAlignBottomRight = 7; CPImageAlignBottomRight = 7;
CPImageAlignRight = 8; CPImageAlignRight = 8;
var CPImageViewEmptyPlaceholderImage = nil; var CPImageViewShadowBackgroundColor = nil,
CPImageViewEmptyPlaceholderImage = nil;
var LEFT_SHADOW_INSET = 3.0,
RIGHT_SHADOW_INSET = 3.0,
TOP_SHADOW_INSET = 3.0,
BOTTOM_SHADOW_INSET = 5.0,
VERTICAL_SHADOW_INSET = TOP_SHADOW_INSET + BOTTOM_SHADOW_INSET,
HORIZONTAL_SHADOW_INSET = LEFT_SHADOW_INSET + RIGHT_SHADOW_INSET;
/*! /*!
@ingroup appkit @ingroup appkit
@@ -63,24 +72,11 @@ var CPImageViewEmptyPlaceholderImage = nil;
+ (void)initialize + (void)initialize
{ {
if (self !== [CPImageView class])
return;
var bundle = [CPBundle bundleForClass:[CPView class]]; var bundle = [CPBundle bundleForClass:[CPView class]];
CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]]; CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]];
} }
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding === CPValueBinding || aBinding === CPValueURLBinding || aBinding === CPValuePathBinding || aBinding === CPDataBinding)
return [CPImageViewValueBinder class];
else if ([aBinding hasPrefix:CPEditableBinding])
return [CPMultipleValueAndBinding class];
return [super _binderClassForBinding:aBinding];
}
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
{ {
self = [super initWithFrame:aFrame]; self = [super initWithFrame:aFrame];
@@ -88,59 +84,26 @@ var CPImageViewEmptyPlaceholderImage = nil;
if (self) if (self)
{ {
#if PLATFORM(DOM) #if PLATFORM(DOM)
[self _createDOMImageElement]; _DOMImageElement = document.createElement("img");
_DOMImageElement.style.position = "absolute";
_DOMImageElement.style.left = "0px";
_DOMImageElement.style.top = "0px";
if ([CPPlatform supportsDragAndDrop])
{
_DOMImageElement.setAttribute("draggable", "true");
_DOMImageElement.style["-khtml-user-drag"] = "element";
}
CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement);
_DOMImageElement.style.visibility = "hidden";
#endif #endif
} }
return self; return self;
} }
- (void)_createDOMImageElement
{
#if PLATFORM(DOM)
var image = [self objectValue],
isCSSBasedImage = [image isCSSBased],
isIMGImageElement = _DOMImageElement && (_DOMImageElement.nodeName == "IMG");
// First, check if we need to destroy a current DOM image element. This is the case if :
// - we have one but not the right one (that is a DIV but needing an IMG, and vice versa)
if (_DOMImageElement)
{
if ((isIMGImageElement && isCSSBasedImage) || (!isIMGImageElement && !isCSSBasedImage))
{
// OK, destroy it
_DOMElement.removeChild(_DOMImageElement);
_DOMImageElement = nil;
// CSS styling cleaning
_cssStylePreviousState = @[];
_cssStyleNode = nil;
}
else
return;
}
_DOMImageElement = document.createElement(isCSSBasedImage ? "div" : "img");
_DOMImageElement.style.position = "absolute";
_DOMImageElement.style.left = "0px";
_DOMImageElement.style.top = "0px";
if ([CPPlatform supportsDragAndDrop])
{
_DOMImageElement.setAttribute("draggable", "true");
_DOMImageElement.style["-khtml-user-drag"] = "element";
}
_DOMImageElement.style.visibility = "hidden";
AppKitTagDOMElement(self, _DOMImageElement);
CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement);
#endif
}
/*! /*!
Returns the view's image. Returns the view's image.
*/ */
@@ -172,15 +135,7 @@ var CPImageViewEmptyPlaceholderImage = nil;
var newImage = [self objectValue]; var newImage = [self objectValue];
#if PLATFORM(DOM) #if PLATFORM(DOM)
[self _createDOMImageElement]; _DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
if ([newImage isCSSBased])
_cssStyleNode = [newImage applyCSSImageForView:self
onDOMElement:_DOMImageElement
styleNode:_cssStyleNode
previousState:@ref(_cssStylePreviousState)];
else
_DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
#endif #endif
var size = [newImage size]; var size = [newImage size];
@@ -206,7 +161,6 @@ var CPImageViewEmptyPlaceholderImage = nil;
- (void)imageDidLoad:(CPNotification)aNotification - (void)imageDidLoad:(CPNotification)aNotification
{ {
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPImageDidLoadNotification object:[self objectValue]];
[self hideOrDisplayContents]; [self hideOrDisplayContents];
[self setNeedsLayout]; [self setNeedsLayout];
@@ -286,7 +240,7 @@ var CPImageViewEmptyPlaceholderImage = nil;
[super setImageScaling:anImageScaling]; [super setImageScaling:anImageScaling];
#if PLATFORM(DOM) #if PLATFORM(DOM)
if ([self currentValueForThemeAttribute:@"image-scaling"] === CPImageScaleAxesIndependently) if ([self currentValueForThemeAttribute:@"image-scaling"] === CPScaleToFit)
{ {
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, 0.0, 0.0); CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, 0.0, 0.0);
} }
@@ -296,7 +250,7 @@ var CPImageViewEmptyPlaceholderImage = nil;
[self setNeedsDisplay:YES]; [self setNeedsDisplay:YES];
} }
- (CPUInteger)imageScaling - (unsigned)imageScaling
{ {
return [self currentValueForThemeAttribute:@"image-scaling"]; return [self currentValueForThemeAttribute:@"image-scaling"];
} }
@@ -343,19 +297,19 @@ var CPImageViewEmptyPlaceholderImage = nil;
imageScaling = [self currentValueForThemeAttribute:@"image-scaling"], imageScaling = [self currentValueForThemeAttribute:@"image-scaling"],
x = 0.0, x = 0.0,
y = 0.0, y = 0.0,
insetWidth = (_hasShadow ? [_shadowView horizontalInset] : 0.0), insetWidth = (_hasShadow ? HORIZONTAL_SHADOW_INSET : 0.0),
insetHeight = (_hasShadow ? [_shadowView verticalInset] : 0.0), insetHeight = (_hasShadow ? VERTICAL_SHADOW_INSET : 0.0),
boundsWidth = CGRectGetWidth(bounds), boundsWidth = _CGRectGetWidth(bounds),
boundsHeight = CGRectGetHeight(bounds), boundsHeight = _CGRectGetHeight(bounds),
width = boundsWidth - insetWidth, width = boundsWidth - insetWidth,
height = boundsHeight - insetHeight; height = boundsHeight - insetHeight;
if (imageScaling === CPImageScaleAxesIndependently) if (imageScaling === CPScaleToFit)
{ {
#if PLATFORM(DOM) #if PLATFORM(DOM)
_DOMImageElement.width = ROUND(width); _DOMImageElement.width = ROUND(width);
_DOMImageElement.height = ROUND(height); _DOMImageElement.height = ROUND(height);
#endif #endif
} }
else else
{ {
@@ -364,19 +318,17 @@ var CPImageViewEmptyPlaceholderImage = nil;
if (size.width == -1 && size.height == -1) if (size.width == -1 && size.height == -1)
return; return;
switch (imageScaling) if (imageScaling === CPScaleProportionally)
{ {
case CPImageScaleProportionallyDown: // The max size it can be is size.width x size.height, so only
if (width >= size.width && height >= size.height) // only proportion otherwise.
{ if (width >= size.width && height >= size.height)
width = size.width; {
height = size.height; width = size.width;
break; height = size.height;
} }
else
// intentionally fall through to the next case {
case CPImageScaleProportionallyUpOrDown:
var imageRatio = size.width / size.height, var imageRatio = size.width / size.height,
viewRatio = width / height; viewRatio = width / height;
@@ -384,19 +336,26 @@ var CPImageViewEmptyPlaceholderImage = nil;
width = height * imageRatio; width = height * imageRatio;
else else
height = width / imageRatio; height = width / imageRatio;
break; }
case CPImageScaleAxesIndependently: #if PLATFORM(DOM)
case CPImageScaleNone:
width = size.width;
height = size.height;
break;
}
#if PLATFORM(DOM)
_DOMImageElement.width = ROUND(width); _DOMImageElement.width = ROUND(width);
_DOMImageElement.height = ROUND(height); _DOMImageElement.height = ROUND(height);
#endif #endif
}
else
{
width = size.width;
height = size.height;
}
if (imageScaling == CPScaleNone)
{
#if PLATFORM(DOM)
_DOMImageElement.width = ROUND(size.width);
_DOMImageElement.height = ROUND(size.height);
#endif
}
var x, var x,
y; y;
@@ -444,10 +403,10 @@ var CPImageViewEmptyPlaceholderImage = nil;
#endif #endif
} }
_imageRect = CGRectMake(x, y, width, height); _imageRect = _CGRectMake(x, y, width, height);
if (_hasShadow) if (_hasShadow)
[_shadowView setFrame:CGRectMake(x - [_shadowView leftInset], y - [_shadowView topInset], width + insetWidth, height + insetHeight)]; [_shadowView setFrame:_CGRectMake(x - LEFT_SHADOW_INSET, y - TOP_SHADOW_INSET, width + insetWidth, height + insetHeight)];
} }
- (void)mouseDown:(CPEvent)anEvent - (void)mouseDown:(CPEvent)anEvent
@@ -498,53 +457,6 @@ var CPImageViewEmptyPlaceholderImage = nil;
@end @end
@implementation CPImageViewValueBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
[self _setPlaceholder:nil forMarker:CPMultipleValuesMarker isDefault:YES];
[self _setPlaceholder:nil forMarker:CPNoSelectionMarker isDefault:YES];
[self _setPlaceholder:nil forMarker:CPNotApplicableMarker isDefault:YES];
[self _setPlaceholder:nil forMarker:CPNullMarker isDefault:YES];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
[_source setImage:nil];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
var image;
if (aValue == nil)
image = nil;
else if (aBinding === CPDataBinding)
image = [[CPImage alloc] initWithData:aValue];
else if (aBinding === CPValueURLBinding || aBinding === CPValuePathBinding)
image = [CPImage cachedImageWithContentsOfFile:aValue];
else if (aBinding === CPValueBinding)
image = aValue;
[_source setImage:image];
}
- (id)valueForBinding:(CPString)aBinding
{
var image = [_source image];
if (aBinding === CPDataBinding)
return [image data];
else if (aBinding === CPValueURLBinding || aBinding === CPValuePathBinding)
return [image filename];
else if (aBinding === CPValueBinding)
return image;
}
@end
var CPImageViewImageKey = @"CPImageViewImageKey", var CPImageViewImageKey = @"CPImageViewImageKey",
CPImageViewImageScalingKey = @"CPImageViewImageScalingKey", CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey", CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey",
@@ -560,18 +472,31 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
*/ */
- (id)initWithCoder:(CPCoder)aCoder - (id)initWithCoder:(CPCoder)aCoder
{ {
#if PLATFORM(DOM)
_DOMImageElement = document.createElement("img");
_DOMImageElement.style.position = "absolute";
_DOMImageElement.style.left = "0px";
_DOMImageElement.style.top = "0px";
_DOMImageElement.style.visibility = "hidden";
if ([CPPlatform supportsDragAndDrop])
{
_DOMImageElement.setAttribute("draggable", "true");
_DOMImageElement.style["-khtml-user-drag"] = "element";
}
#endif
self = [super initWithCoder:aCoder]; self = [super initWithCoder:aCoder];
if (self) if (self)
{ {
#if PLATFORM(DOM) #if PLATFORM(DOM)
[self _createDOMImageElement]; _DOMElement.appendChild(_DOMImageElement);
#endif #endif
[self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]]; [self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
[self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]]; [self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]];
if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey]) if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO)
[self setEditable:YES]; [self setEditable:YES];
[self setNeedsLayout]; [self setNeedsLayout];
@@ -591,12 +516,17 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
// We do this in order to avoid encoding the _shadowView, which // We do this in order to avoid encoding the _shadowView, which
// should just automatically be created programmatically as needed. // should just automatically be created programmatically as needed.
if (_shadowView) if (_shadowView)
[_shadowView removeFromSuperview]; {
var actualSubviews = _subviews;
_subviews = [_subviews copy];
[_subviews removeObjectIdenticalTo:_shadowView];
}
[super encodeWithCoder:aCoder]; [super encodeWithCoder:aCoder];
if (_shadowView) if (_shadowView)
[self addSubview:_shadowView]; _subviews = actualSubviews;
[aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey]; [aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey];
[aCoder encodeInt:_imageAlignment forKey:CPImageViewImageAlignmentKey]; [aCoder encodeInt:_imageAlignment forKey:CPImageViewImageAlignmentKey];
@@ -606,21 +536,3 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
} }
@end @end
@implementation CPImage (CachedImage)
+ (CPImage)cachedImageWithContentsOfFile:(CPString)aFile
{
var cached_name = [CPString stringWithFormat:@"%@_%d", [self class], [aFile hash]],
image = [CPImage imageNamed:cached_name];
if (!image)
{
image = [[CPImage alloc] initWithContentsOfFile:aFile];
[image setName:cached_name];
}
return image;
}
@end
+3 -5
View File
@@ -21,15 +21,13 @@
*/ */
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPArray.j>
@import "CPEvent_Constants.j" @import "CPEvent.j"
@import "CPText.j"
CPStandardKeyBindings = { CPStandardKeyBindings = {
@"@.": @"cancelOperation:", @"@.": @"cancelOperation:",
@"@a": @"selectAll:",
@"^a": @"moveToBeginningOfParagraph:", @"^a": @"moveToBeginningOfParagraph:",
@"^$a": @"moveToBeginningOfParagraphAndModifySelection:", @"^$a": @"moveToBeginningOfParagraphAndModifySelection:",
@"^b": @"moveBackward:", @"^b": @"moveBackward:",
@@ -154,7 +152,7 @@ var CPKeyBindingCache = {};
+ (void)initialize + (void)initialize
{ {
if (self !== [CPKeyBinding class]) if ([self class] !== CPKeyBinding)
return; return;
[self createKeyBindingsFromJSObject:CPStandardKeyBindings]; [self createKeyBindingsFromJSObject:CPStandardKeyBindings];
+97 -535
View File
@@ -26,26 +26,21 @@
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPArray.j> @import <Foundation/CPArray.j>
@import <Foundation/CPDictionary.j> @import <Foundation/CPDictionary.j>
@import <Foundation/CPInvocation.j>
@import <Foundation/CPValueTransformer.j> @import <Foundation/CPValueTransformer.j>
@import <Foundation/CPKeyValueObserving.j>
@class CPButton
var exposedBindingsMap = @{}, var exposedBindingsMap = [CPDictionary new],
bindingsMap = @{}; bindingsMap = [CPDictionary new];
@typedef CPBindingOperationKind
var CPBindingOperationAnd = 0, var CPBindingOperationAnd = 0,
CPBindingOperationOr = 1; CPBindingOperationOr = 1;
@implementation CPBinder : CPObject @implementation CPBinder : CPObject
{ {
CPDictionary _info; CPDictionary _info;
id _source @accessors(getter=source); id _source;
JSObject _suppressedNotifications; JSObject _suppressedNotifications;
JSObject _placeholderForMarker;
} }
+ (void)exposeBinding:(CPString)aBinding forClass:(Class)aClass + (void)exposeBinding:(CPString)aBinding forClass:(Class)aClass
@@ -71,20 +66,6 @@ var CPBindingOperationAnd = 0,
return [[bindingsMap objectForKey:[anObject UID]] objectForKey:aBinding]; return [[bindingsMap objectForKey:[anObject UID]] objectForKey:aBinding];
} }
+ (void)_reverseSetValueFromExclusiveBinderForObject:(id)anObject
{
var bindersByBindingName = [bindingsMap objectForKey:[anObject UID]];
[bindersByBindingName enumerateKeysAndObjectsUsingBlock:function(binding, binder, stop)
{
if ([binder isKindOfClass:[self class]])
{
[binder reverseSetValueFor:binding];
stop(YES);
}
}];
}
+ (CPDictionary)infoForBinding:(CPString)aBinding forObject:(id)anObject + (CPDictionary)infoForBinding:(CPString)aBinding forObject:(id)anObject
{ {
var theBinding = [self getBinding:aBinding forObject:anObject]; var theBinding = [self getBinding:aBinding forObject:anObject];
@@ -112,9 +93,9 @@ var CPBindingOperationAnd = 0,
if (!theBinding) if (!theBinding)
return; return;
var info = theBinding._info, var infoDictionary = theBinding._info,
observedObject = [info objectForKey:CPObservedObjectKey], observedObject = [infoDictionary objectForKey:CPObservedObjectKey],
keyPath = [info objectForKey:CPObservedKeyPathKey]; keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey];
[observedObject removeObserver:theBinding forKeyPath:keyPath]; [observedObject removeObserver:theBinding forKeyPath:keyPath];
[bindings removeObjectForKey:aBinding]; [bindings removeObjectForKey:aBinding];
@@ -123,7 +104,6 @@ var CPBindingOperationAnd = 0,
+ (void)unbindAllForObject:(id)anObject + (void)unbindAllForObject:(id)anObject
{ {
var bindings = [bindingsMap objectForKey:[anObject UID]]; var bindings = [bindingsMap objectForKey:[anObject UID]];
if (!bindings) if (!bindings)
return; return;
@@ -131,39 +111,30 @@ var CPBindingOperationAnd = 0,
count = allKeys.length; count = allKeys.length;
while (count--) while (count--)
[anObject unbind:allKeys[count]]; [anObject unbind:[bindings objectForKey:allKeys[count]]];
[bindingsMap removeObjectForKey:[anObject UID]]; [bindingsMap removeObjectForKey:[anObject UID]];
} }
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource - (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
{ {
// We use [self init] here because subclasses override init. We can't override this method self = [super init];
// because their initialization has to occur before this method in this class is executed.
self = [self init];
if (self) if (self)
{ {
_source = aSource; _source = aSource;
_info = @{ _info = [CPDictionary dictionaryWithObjects:[aDestination, aKeyPath] forKeys:[CPObservedObjectKey, CPObservedKeyPathKey]];
CPObservedObjectKey: aDestination,
CPObservedKeyPathKey: aKeyPath,
};
_suppressedNotifications = {}; _suppressedNotifications = {};
_placeholderForMarker = {};
if (options) if (options)
[_info setObject:options forKey:CPOptionsKey]; [_info setObject:options forKey:CPOptionsKey];
[self _updatePlaceholdersWithOptions:options forBinding:aName]; [aDestination addObserver:self forKeyPath:aKeyPath options:CPKeyValueObservingOptionNew context:aBinding];
[aDestination addObserver:self forKeyPath:aKeyPath options:0 context:aBinding];
var bindings = [bindingsMap objectForKey:[_source UID]]; var bindings = [bindingsMap objectForKey:[_source UID]];
if (!bindings) if (!bindings)
{ {
bindings = @{}; bindings = [CPDictionary new];
[bindingsMap setObject:bindings forKey:[_source UID]]; [bindingsMap setObject:bindings forKey:[_source UID]];
} }
@@ -174,62 +145,15 @@ var CPBindingOperationAnd = 0,
return self; return self;
} }
+ (BOOL)isBindingAllowed:(CPString)aBinding forObject:(id)anObject - (void)setValueFor:(CPString)aBinding
{
if ([[anObject class] isBindingExclusive:aBinding])
{
var bindingsForObject = [bindingsMap objectForKey:[anObject UID]],
allBindings = [bindingsForObject allKeys],
count = [allBindings count];
while (count--)
{
if ([[anObject class] isBindingExclusive:allBindings[count]])
return NO;
}
}
return YES;
}
- (void)raiseIfNotApplicable:(id)aValue forKeyPath:(CPString)keyPath options:(CPDictionary)options
{
if (aValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
{
[CPException raise:CPGenericException
reason:@"Cannot transform non-applicable key on: " + _source + " key path: " + keyPath + " value: " + aValue];
}
}
- (void)setValueFor:(CPString)theBinding
{ {
var destination = [_info objectForKey:CPObservedObjectKey], var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey], keyPath = [_info objectForKey:CPObservedKeyPathKey],
options = [_info objectForKey:CPOptionsKey], options = [_info objectForKey:CPOptionsKey],
newValue = [destination valueForKeyPath:keyPath]; newValue = [destination valueForKeyPath:keyPath];
if (CPIsControllerMarker(newValue)) newValue = [self transformValue:newValue withOptions:options];
{ [_source setValue:newValue forKey:aBinding];
[self raiseIfNotApplicable:newValue forKeyPath:keyPath options:options];
var value = [self _placeholderForMarker:newValue];
[self setPlaceholderValue:value withMarker:newValue forBinding:theBinding];
}
else
{
var value = [self transformValue:newValue withOptions:options];
[self setValue:value forBinding:theBinding];
}
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
[_source setValue:aValue forKey:aBinding];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source setValue:aValue forKey:aBinding];
} }
- (void)reverseSetValueFor:(CPString)aBinding - (void)reverseSetValueFor:(CPString)aBinding
@@ -237,18 +161,10 @@ var CPBindingOperationAnd = 0,
var destination = [_info objectForKey:CPObservedObjectKey], var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey], keyPath = [_info objectForKey:CPObservedKeyPathKey],
options = [_info objectForKey:CPOptionsKey], options = [_info objectForKey:CPOptionsKey],
newValue = [self valueForBinding:aBinding]; newValue = [_source valueForKeyPath:aBinding];
newValue = [self reverseTransformValue:newValue withOptions:options]; newValue = [self reverseTransformValue:newValue withOptions:options];
[self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
[destination setValue:newValue forKeyPath:keyPath]; [destination setValue:newValue forKeyPath:keyPath];
[self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
}
- (id)valueForBinding:(CPString)aBinding
{
return [_source valueForKeyPath:aBinding];
} }
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
@@ -257,7 +173,6 @@ var CPBindingOperationAnd = 0,
return; return;
var objectSuppressions = _suppressedNotifications[[anObject UID]]; var objectSuppressions = _suppressedNotifications[[anObject UID]];
if (objectSuppressions && objectSuppressions[aKeyPath]) if (objectSuppressions && objectSuppressions[aKeyPath])
return; return;
@@ -276,7 +191,6 @@ var CPBindingOperationAnd = 0,
if (!valueTransformer) if (!valueTransformer)
{ {
var valueTransformerClass = CPClassFromString(valueTransformerName); var valueTransformerClass = CPClassFromString(valueTransformerName);
if (valueTransformerClass) if (valueTransformerClass)
{ {
valueTransformer = [[valueTransformerClass alloc] init]; valueTransformer = [[valueTransformerClass alloc] init];
@@ -290,17 +204,14 @@ var CPBindingOperationAnd = 0,
if (valueTransformer) if (valueTransformer)
aValue = [valueTransformer transformedValue:aValue]; aValue = [valueTransformer transformedValue:aValue];
// If the value is nil AND the source doesn't respond to setPlaceholderString: then
// we set the value to the placeholder. Otherwise, we do not want to short cut the process if (aValue === undefined || aValue === nil || aValue === [CPNull null])
// of setting the placeholder that is based on the fact that the value is nil.
if ((aValue === undefined || aValue === nil || aValue === [CPNull null])
&& ![_source respondsToSelector:@selector(setPlaceholderString:)])
aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil; aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
return aValue; return aValue;
} }
- (id)reverseTransformValue:(id)aValue withOptions:(CPDictionary)options - (id)reverseTransformValue:(id)aValue withOptions: (CPDictionary)options
{ {
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption], var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
valueTransformer; valueTransformer;
@@ -338,7 +249,6 @@ var CPBindingOperationAnd = 0,
var uid = [anObject UID], var uid = [anObject UID],
objectSuppressions = _suppressedNotifications[uid]; objectSuppressions = _suppressedNotifications[uid];
if (!objectSuppressions) if (!objectSuppressions)
_suppressedNotifications[uid] = objectSuppressions = {}; _suppressedNotifications[uid] = objectSuppressions = {};
@@ -355,57 +265,12 @@ var CPBindingOperationAnd = 0,
var uid = [anObject UID], var uid = [anObject UID],
objectSuppressions = _suppressedNotifications[uid]; objectSuppressions = _suppressedNotifications[uid];
if (!objectSuppressions) if (!objectSuppressions)
return; return;
delete objectSuppressions[aKeyPath]; delete objectSuppressions[aKeyPath];
} }
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
var count = [CPBinderPlaceholderMarkers count];
while (count--)
{
var marker = CPBinderPlaceholderMarkers[count],
optionName = CPBinderPlaceholderOptions[count],
isExplicit = [options containsKey:optionName],
placeholder = isExplicit ? [options objectForKey:optionName] : nil;
[self _setPlaceholder:placeholder forMarker:marker isDefault:!isExplicit];
}
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
{
[self _updatePlaceholdersWithOptions:options];
}
- (JSObject)_placeholderForMarker:(id)aMarker
{
var placeholder = _placeholderForMarker[[aMarker UID]];
if (placeholder)
return placeholder.value;
return nil;
}
- (void)_setPlaceholder:(id)aPlaceholder forMarker:(id)aMarker isDefault:(BOOL)isDefault
{
if (isDefault)
{
var existingPlaceholder = _placeholderForMarker[[aMarker UID]];
// Don't overwrite an explicitly set placeholder with a default.
if (existingPlaceholder && !existingPlaceholder.isDefault)
return;
}
_placeholderForMarker[[aMarker UID]] = { 'isDefault': isDefault, 'value': aPlaceholder };
}
@end @end
@implementation CPObject (KeyValueBindingCreation) @implementation CPObject (KeyValueBindingCreation)
@@ -415,16 +280,11 @@ var CPBindingOperationAnd = 0,
[CPBinder exposeBinding:aBinding forClass:[self class]]; [CPBinder exposeBinding:aBinding forClass:[self class]];
} }
+ (Class)_binderClassForBinding:(CPString)aBinding + (Class)_binderClassForBinding:(CPString)theBinding
{ {
return [CPBinder class]; return [CPBinder class];
} }
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return NO;
}
- (CPArray)exposedBindings - (CPArray)exposedBindings
{ {
var exposedBindings = [], var exposedBindings = [],
@@ -451,15 +311,10 @@ 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 (![CPBinder isBindingAllowed:aBinding forObject:self])
{
CPLog.warn([self description] + " : cannot bind " + aBinding + " because another binding with the same functionality is already in use.");
return;
}
//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]; var binderClass = [[self class] _binderClassForBinding:aBinding];
@@ -478,7 +333,7 @@ var CPBindingOperationAnd = 0,
[binderClass unbind:aBinding forObject:self]; [binderClass unbind:aBinding forObject:self];
} }
- (CPString)_replacementKeyPathForBinding:(CPString)binding - (id)_replacementKeyPathForBinding:(CPString)binding
{ {
return binding; return binding;
} }
@@ -487,13 +342,13 @@ var CPBindingOperationAnd = 0,
/*! /*!
@ignore @ignore
Provides stub implementations that simply calls super for the "objectValue" binding. 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. 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
IMPORTANT!
Every class with a value binding should implement a subclass to handle its specific value binding logic.
*/ */
@implementation _CPValueBinder : CPBinder @implementation _CPValueBinder : CPBinder
{
}
- (void)setValueFor:(CPString)theBinding - (void)setValueFor:(CPString)theBinding
{ {
@@ -507,9 +362,8 @@ var CPBindingOperationAnd = 0,
@end @end
@implementation _CPMultipleValueBooleanBinding : CPBinder @implementation _CPKeyValueOrBinding : CPBinder
{ {
CPBindingOperationKind _operation;
} }
- (void)setValueFor:(CPString)aBinding - (void)setValueFor:(CPString)aBinding
@@ -519,14 +373,7 @@ var CPBindingOperationAnd = 0,
if (!bindings) if (!bindings)
return; return;
var baseBinding = aBinding.replace(/\d$/, ""); [_source setValue:resolveMultipleValues(aBinding, bindings, CPBindingOperationOr) forKey:aBinding];
[_source setValue:[self resolveMultipleValuesForBinding:baseBinding bindings:bindings booleanOperation:_operation] forKey:baseBinding];
}
- (void)reverseSetValueFor:(CPString)theBinding
{
// read-only
} }
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
@@ -534,146 +381,61 @@ var CPBindingOperationAnd = 0,
[self setValueFor:context]; [self setValueFor:context];
} }
- (BOOL)resolveMultipleValuesForBinding:(CPString)aBinding bindings:(CPDictionary)bindings booleanOperation:(CPBindingOperationKind)operation @end
@implementation _CPKeyValueAndBinding : CPBinder
{ {
var bindingName = aBinding, }
- (void)setValueFor:(CPString)aBinding
{
var bindings = [bindingsMap objectForKey:[_source UID]];
if (!bindings)
return;
[_source setValue:resolveMultipleValues(aBinding, bindings, CPBindingOperationAnd) forKey:aBinding];
}
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObejct change:(CPDictionary)changes context:(id)context
{
[self setValueFor:context];
}
@end
var resolveMultipleValues = function resolveMultipleValues(/*CPString*/key, /*CPDictionary*/bindings, /*GSBindingOperationKind*/operation)
{
var bindingName = key,
theBinding, theBinding,
count = 2; count = 1;
while (theBinding = [bindings objectForKey:bindingName]) while (theBinding = [bindings objectForKey:bindingName])
{ {
var info = theBinding._info, var infoDictionary = theBinding._info,
object = [info objectForKey:CPObservedObjectKey], object = [infoDictionary objectForKey:CPObservedObjectKey],
keyPath = [info objectForKey:CPObservedKeyPathKey], keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
options = [info objectForKey:CPOptionsKey], options = [infoDictionary objectForKey:CPOptionsKey];
value = [object valueForKeyPath:keyPath];
if (CPIsControllerMarker(value)) var value = [theBinding transformValue:[object valueForKeyPath:keyPath] withOptions:options];
{
[self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
value = [theBinding _placeholderForMarker:value];
}
else
value = [theBinding transformValue:value withOptions:options];
if (operation === CPBindingOperationOr) if (value == operation)
{ return operation;
// Any true condition means true for OR
if (value)
return YES;
}
// Any false condition means false for AND bindingName = [CPString stringWithFormat:@"%@%i", key, ++count];
else if (!value)
return NO;
bindingName = aBinding + (count++);
} }
// If we get here, all OR conditions were false or all AND conditions were true return !operation;
return operation === CPBindingOperationOr ? NO : YES;
} }
@end var invokeAction = function invokeAction(/*CPString*/targetKey, /*CPString*/argumentKey, /*CPDictionary*/bindings)
@implementation CPMultipleValueAndBinding : _CPMultipleValueBooleanBinding
- (id)init
{ {
if (self = [super init]) var theBinding = [bindings objectForKey:targetKey],
_operation = CPBindingOperationAnd; infoDictionary = theBinding._info,
return self; object = [infoDictionary objectForKey:CPObservedObjectKey],
} keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
options = [infoDictionary objectForKey:CPOptionsKey],
@end
@implementation CPMultipleValueOrBinding : _CPMultipleValueBooleanBinding
- (id)init
{
if (self = [super init])
_operation = CPBindingOperationOr;
return self;
}
@end
@implementation _CPMultipleValueActionBinding : CPBinder
{
CPString _argumentBinding;
CPString _targetBinding;
}
- (void)setValueFor:(CPString)theBinding
{
// Called when the binding is first created
[self checkForNullBinding:theBinding initializing:YES];
}
- (void)reverseSetValueFor:(CPString)theBinding
{
// no-op
}
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
{
// context is the binding name
[self checkForNullBinding:context initializing:NO];
}
/*!
@ignore
When the value of a multiple-value argument binding changes,
if the binding is marked not to allow null arguments, we have to check
if the binding's value is nil and disable the button if it is.
Otherwise the button is enabled.
*/
- (void)checkForNullBinding:(CPString)theBinding initializing:(BOOL)isInitializing
{
// Only done for buttons
if (![_source isKindOfClass:CPButton])
return;
// We start with the button enabled for the first argument during init,
// and subsequent checks can disable it.
if (isInitializing && theBinding === CPArgumentBinding)
[_source setEnabled:YES];
var bindings = [bindingsMap valueForKey:[_source UID]],
binding = [bindings objectForKey:theBinding],
info = binding._info,
options = [info objectForKey:CPOptionsKey];
if (![options valueForKey:CPAllowsNullArgumentBindingOption])
{
var object = [info objectForKey:CPObservedObjectKey],
keyPath = [info objectForKey:CPObservedKeyPathKey],
value = [object valueForKeyPath:keyPath];
if (value === nil || value === undefined)
{
[_source setEnabled:NO];
return;
}
}
// If a binding value changed and did not fail the null test, enable the button
if (!isInitializing)
[_source setEnabled:YES];
}
- (void)invokeAction
{
var bindings = [bindingsMap valueForKey:[_source UID]],
theBinding = [bindings objectForKey:CPTargetBinding],
info = theBinding._info,
object = [info objectForKey:CPObservedObjectKey],
keyPath = [info objectForKey:CPObservedKeyPathKey],
options = [info objectForKey:CPOptionsKey],
target = [object valueForKeyPath:keyPath], target = [object valueForKeyPath:keyPath],
selector = [options objectForKey:CPSelectorNameBindingOption]; selector = [options objectForKey:CPSelectorNameBindingOption];
@@ -681,195 +443,28 @@ var CPBindingOperationAnd = 0,
if (!target || !selector) if (!target || !selector)
return; return;
var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]], var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]];
bindingName = CPArgumentBinding, [invocation setSelector:selector];
var bindingName = argumentKey,
count = 1; count = 1;
while (theBinding = [bindings objectForKey:bindingName]) while (theBinding = [bindings objectForKey:bindingName])
{ {
info = theBinding._info; infoDictionary = theBinding._info;
object = [info objectForKey:CPObservedObjectKey];
keyPath = [info objectForKey:CPObservedKeyPathKey];
[invocation setArgument:[object valueForKeyPath:keyPath] atIndex:++count]; keyPath = [infoDictionary objectForKey:CPObserverKeyPathKey];
object = [[infoDictionary objectForKey:CPObservedObjectKey] valueForKeyPath:keyPath];
bindingName = CPArgumentBinding + count; if (object)
[invocation setArgument:object atIndex:++count];
bindingName = [CPString stringWithFormat:@"%@%i", argumentKey, count];
} }
[invocation setSelector:selector]; [invocation invoke];
[invocation invokeWithTarget:target];
} }
@end
@implementation CPActionBinding : _CPMultipleValueActionBinding
- (id)init
{
if (self = [super init])
{
_argumentBinding = CPArgumentBinding;
_targetBinding = CPTargetBinding;
}
return self;
}
@end
@implementation CPDoubleClickActionBinding : _CPMultipleValueActionBinding
- (id)init
{
if (self = [super init])
{
_argumentBinding = CPArgumentBinding;
_targetBinding = CPTargetBinding;
}
return self;
}
@end
/*!
Abstract superclass for CPValueWithPatternBinding and CPTitleWithPatternBinding.
*/
@implementation _CPPatternBinding : CPBinder
{
CPString _bindingKey;
CPString _patternPlaceholder;
}
- (void)setValueFor:(CPString)aBinding
{
var bindings = [bindingsMap valueForKey:[_source UID]];
if (!bindings)
return;
// Strip off any trailing number from the binding name
var baseBinding = aBinding.replace(/\d$/, ""),
result = [self resolveMultipleValuesForBindings:bindings];
if (result.isPlaceholder)
[self setPlaceholderValue:result.value withMarker:result.marker forBinding:baseBinding];
else
[self setValue:result.value forBinding:baseBinding];
}
- (void)reverseSetValueFor:(CPString)theBinding
{
// read-only
}
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
{
[self setValueFor:context];
}
- (JSObject)resolveMultipleValuesForBindings:(CPDictionary)bindings
{
var theBinding,
result = { value:@"", isPlaceholder:NO, marker:nil };
for (var count = 1; theBinding = [bindings objectForKey:_bindingKey + count]; ++count)
{
var info = theBinding._info,
object = [info objectForKey:CPObservedObjectKey],
keyPath = [info objectForKey:CPObservedKeyPathKey],
options = [info objectForKey:CPOptionsKey],
value = [object valueForKeyPath:keyPath];
if (count === 1)
result.value = [options objectForKey:CPDisplayPatternBindingOption];
if (CPIsControllerMarker(value))
{
[self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
result.isPlaceholder = YES;
result.marker = value;
value = [theBinding _placeholderForMarker:value];
}
else
value = [theBinding transformValue:value withOptions:options];
if (value === nil || value === undefined)
value = @"";
result.value = result.value.replace("%{" + _patternPlaceholder + count + "}@", [value description]);
}
return result;
}
@end
/*!
Users of this class must override setValue:forKey: and if the
key is CPDisplayPatternValueBinding, set the appropriate value
for the control class. For example, CPTextField uses setObjectValue.
*/
@implementation CPValueWithPatternBinding : _CPPatternBinding
- (id)init
{
if (self = [super init])
{
_bindingKey = CPDisplayPatternValueBinding;
_patternPlaceholder = @"value";
}
return self;
}
@end
/*!
Users of this class must override setValue:forKey: and if the
key is CPDisplayPatternTitleBinding, set the appropriate value
for the control class. For example, CPBox uses setTitle.
*/
@implementation CPTitleWithPatternBinding : _CPPatternBinding
- (id)init
{
if (self = [super init])
{
_bindingKey = CPDisplayPatternTitleBinding;
_patternPlaceholder = @"title";
}
return self;
}
@end
@implementation _CPStateMarker : CPObject
{
CPString _name;
}
- (id)initWithName:(CPString)aName
{
if (self = [super init])
_name = aName;
return self;
}
- (CPString)description
{
return "<" + _name + ">";
}
@end
// Keys in options dictionary // Keys in options dictionary
// Keys in dictionary returned by infoForBinding // Keys in dictionary returned by infoForBinding
@@ -878,52 +473,23 @@ CPObservedKeyPathKey = @"CPObservedKeyPathKey";
CPOptionsKey = @"CPOptionsKey"; CPOptionsKey = @"CPOptionsKey";
// special markers // special markers
CPNoSelectionMarker = [[_CPStateMarker alloc] initWithName:@"NO SELECTION MARKER"]; CPMultipleValuesMarker = @"CPMultipleValuesMarker";
CPMultipleValuesMarker = [[_CPStateMarker alloc] initWithName:@"MULTIPLE VALUES MARKER"]; CPNoSelectionMarker = @"CPNoSelectionMarker";
CPNotApplicableMarker = [[_CPStateMarker alloc] initWithName:@"NOT APPLICABLE MARKER"]; CPNotApplicableMarker = @"CPNotApplicableMarker";
CPNullMarker = [[_CPStateMarker alloc] initWithName:@"NULL MARKER"]; CPNullMarker = @"CPNullMarker";
// Binding name constants // Binding name constants
CPAlignmentBinding = @"alignment"; CPAlignmentBinding = @"alignment";
CPArgumentBinding = @"argument"; CPEditableBinding = @"editable";
CPContentArrayBinding = @"contentArray"; CPEnabledBinding = @"enabled";
CPContentBinding = @"content"; CPFontBinding = @"font";
CPContentObjectBinding = @"contentObject"; CPHiddenBinding = @"hidden";
CPContentObjectsBinding = @"contentObjects"; CPSelectedIndexBinding = @"selectedIndex";
CPContentValuesBinding = @"contentValues"; CPTextColorBinding = @"textColor";
CPDisplayPatternTitleBinding = @"displayPatternTitle"; CPToolTipBinding = @"toolTip";
CPDisplayPatternValueBinding = @"displayPatternValue"; CPValueBinding = @"value";
CPDoubleClickArgumentBinding = @"doubleClickArgument";
CPDoubleClickTargetBinding = @"doubleClickTarget";
CPEditableBinding = @"editable";
CPEnabledBinding = @"enabled";
CPFontBinding = @"font";
CPFontNameBinding = @"fontName";
CPFontBoldBinding = @"fontBold";
CPHiddenBinding = @"hidden";
CPFilterPredicateBinding = @"filterPredicate";
CPMaxValueBinding = @"maxValue";
CPMinValueBinding = @"minValue";
CPPredicateBinding = @"predicate";
CPSelectedIndexBinding = @"selectedIndex";
CPSelectedLabelBinding = @"selectedLabel";
CPSelectedObjectBinding = @"selectedObject";
CPSelectedObjectsBinding = @"selectedObjects";
CPSelectedTagBinding = @"selectedTag";
CPSelectedValueBinding = @"selectedValue";
CPSelectedValuesBinding = @"selectedValues";
CPSelectionIndexesBinding = @"selectionIndexes";
CPTargetBinding = @"target";
CPTextColorBinding = @"textColor";
CPTitleBinding = @"title";
CPToolTipBinding = @"toolTip";
CPValueBinding = @"value";
CPAttributedStringBinding = @"attributedString";
CPValueURLBinding = @"valueURL";
CPValuePathBinding = @"valuePath";
CPDataBinding = @"data";
// Binding options constants //Binding options constants
CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelection"; CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelection";
CPAllowsNullArgumentBindingOption = @"CPAllowsNullArgument"; CPAllowsNullArgumentBindingOption = @"CPAllowsNullArgument";
CPConditionallySetsEditableBindingOption = @"CPConditionallySetsEditable"; CPConditionallySetsEditableBindingOption = @"CPConditionallySetsEditable";
@@ -952,8 +518,4 @@ CPValueTransformerBindingOption = @"CPValueTransformer";
CPIsControllerMarker = function(/*id*/anObject) CPIsControllerMarker = function(/*id*/anObject)
{ {
return anObject === CPMultipleValuesMarker || anObject === CPNoSelectionMarker || anObject === CPNotApplicableMarker || anObject === CPNullMarker; return anObject === CPMultipleValuesMarker || anObject === CPNoSelectionMarker || anObject === CPNotApplicableMarker || anObject === CPNullMarker;
}; }
var CPBinderPlaceholderMarkers = [CPMultipleValuesMarker, CPNoSelectionMarker, CPNotApplicableMarker, CPNullMarker],
CPBinderPlaceholderOptions = [CPMultipleValuesPlaceholderBindingOption, CPNoSelectionPlaceholderBindingOption, CPNotApplicablePlaceholderBindingOption, CPNullPlaceholderBindingOption];
+81 -49
View File
@@ -21,22 +21,25 @@
*/ */
@import "CPControl.j" @import "CPControl.j"
@import "CPWindow_Constants.j"
@global CPApp
@typedef CPTickMarkPosition
CPTickMarkBelow = 0; CPTickMarkBelow = 0;
CPTickMarkAbove = 1; CPTickMarkAbove = 1;
CPTickMarkLeft = CPTickMarkAbove; CPTickMarkLeft = CPTickMarkAbove;
CPTickMarkRight = CPTickMarkBelow; CPTickMarkRight = CPTickMarkBelow;
@typedef CPLevelIndicatorStyle
CPRelevancyLevelIndicatorStyle = 0; CPRelevancyLevelIndicatorStyle = 0;
CPContinuousCapacityLevelIndicatorStyle = 1; CPContinuousCapacityLevelIndicatorStyle = 1;
CPDiscreteCapacityLevelIndicatorStyle = 2; CPDiscreteCapacityLevelIndicatorStyle = 2;
CPRatingLevelIndicatorStyle = 3; CPRatingLevelIndicatorStyle = 3;
var _CPLevelIndicatorBezelColor = nil,
_CPLevelIndicatorSegmentEmptyColor = nil,
_CPLevelIndicatorSegmentNormalColor = nil,
_CPLevelIndicatorSegmentWarningColor = nil,
_CPLevelIndicatorSegmentCriticalColor = nil,
_CPLevelIndicatorSpacing = 1;
/*! /*!
@ingroup appkit @ingroup appkit
@class CPLevelIndicator @class CPLevelIndicator
@@ -59,21 +62,54 @@ CPRatingLevelIndicatorStyle = 3;
BOOL _isTracking; BOOL _isTracking;
} }
+ (CPString)defaultThemeClass + (void)initialize
{ {
return "level-indicator"; var bundle = [CPBundle bundleForClass:CPLevelIndicator];
}
+ (CPDictionary)themeAttributes _CPLevelIndicatorBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
{ [
return @{ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-left.png"] size:CGSizeMake(3.0, 18.0)],
@"bezel-color": [CPNull null], [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-center.png"] size:CGSizeMake(1.0, 18.0)],
@"color-empty": [CPNull null], [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-right.png"] size:CGSizeMake(3.0, 18.0)]
@"color-normal": [CPNull null], ]
@"color-warning": [CPNull null], isVertical:NO
@"color-critical": [CPNull null], ]];
@"spacing": 1.0,
}; _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 - (id)initWithFrame:(CGRect)aFrame
@@ -86,42 +122,37 @@ CPRatingLevelIndicatorStyle = 3;
_maxValue = 2; _maxValue = 2;
_warningValue = 2; _warningValue = 2;
_criticalValue = 2; _criticalValue = 2;
[self _init];
} }
return self; return self;
} }
- (void)_init
{
}
- (void)layoutSubviews - (void)layoutSubviews
{ {
var bezelView = [self layoutEphemeralSubviewNamed:"bezel" var bezelView = [self layoutEphemeralSubviewNamed:"bezel"
positioned:CPWindowBelow positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:nil]; relativeToEphemeralSubviewNamed:nil];
// TODO Make themable. // TODO Make themable.
[bezelView setBackgroundColor:[self valueForThemeAttribute:@"bezel-color"]]; [bezelView setBackgroundColor:_CPLevelIndicatorBezelColor];
var segmentCount = _maxValue - _minValue; var segmentCount = _maxValue - _minValue;
if (segmentCount <= 0) if (segmentCount <= 0)
return; return;
var filledColor = [self valueForThemeAttribute:@"color-normal"], var filledColor = _CPLevelIndicatorSegmentNormalColor,
value = [self doubleValue]; value = [self doubleValue];
if (_warningValue < _criticalValue) if (value < _criticalValue)
{ filledColor = _CPLevelIndicatorSegmentCriticalColor;
if (value >= _criticalValue) else if (value < _warningValue)
filledColor = [self valueForThemeAttribute:@"color-critical"]; filledColor = _CPLevelIndicatorSegmentWarningColor;
else if (value >= _warningValue)
filledColor = [self valueForThemeAttribute:@"color-warning"];
}
else
{
if (value <= _criticalValue)
filledColor = [self valueForThemeAttribute:@"color-critical"];
else if (value <= _warningValue)
filledColor = [self valueForThemeAttribute:@"color-warning"];
}
for (var i = 0; i < segmentCount; i++) for (var i = 0; i < segmentCount; i++)
{ {
@@ -129,13 +160,13 @@ CPRatingLevelIndicatorStyle = 3;
positioned:CPWindowAbove positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:bezelView]; relativeToEphemeralSubviewNamed:bezelView];
[segmentView setBackgroundColor:(_minValue + i) < value ? filledColor : [self valueForThemeAttribute:@"color-empty"]]; [segmentView setBackgroundColor:(_minValue + i) < value ? filledColor : _CPLevelIndicatorSegmentEmptyColor];
} }
} }
- (CPView)createEphemeralSubviewNamed:(CPString)aName - (CPView)createEphemeralSubviewNamed:(CPString)aName
{ {
return [[CPView alloc] initWithFrame:CGRectMakeZero()]; return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
} }
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName - (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
@@ -143,11 +174,11 @@ CPRatingLevelIndicatorStyle = 3;
// TODO Put into theme attributes. // TODO Put into theme attributes.
var bezelHeight = 18, var bezelHeight = 18,
segmentHeight = 17, segmentHeight = 17,
bounds = CGRectCreateCopy([self bounds]); bounds = _CGRectCreateCopy([self bounds]);
if (aViewName == "bezel") if (aViewName == "bezel")
{ {
bounds.origin.y = (CGRectGetHeight(bounds) - bezelHeight) / 2.0; bounds.origin.y = (_CGRectGetHeight(bounds) - bezelHeight) / 2.0;
bounds.size.height = bezelHeight; bounds.size.height = bezelHeight;
return bounds; return bounds;
} }
@@ -157,21 +188,20 @@ CPRatingLevelIndicatorStyle = 3;
segmentCount = _maxValue - _minValue; segmentCount = _maxValue - _minValue;
if (segment >= segmentCount) if (segment >= segmentCount)
return CGRectMakeZero(); return _CGRectMakeZero();
var basicSegmentWidth = bounds.size.width / segmentCount, var basicSegmentWidth = bounds.size.width / segmentCount,
segmentFrame = CGRectCreateCopy([self bounds]), segmentFrame = CGRectCreateCopy([self bounds]);
spacing = [self valueForThemeAttribute:@"spacing"];
segmentFrame.origin.y = (CGRectGetHeight(bounds) - bezelHeight) / 2.0; segmentFrame.origin.y = (_CGRectGetHeight(bounds) - bezelHeight) / 2.0;
segmentFrame.origin.x = FLOOR(segment * basicSegmentWidth); segmentFrame.origin.x = FLOOR(segment * basicSegmentWidth);
segmentFrame.size.width = (segment == segmentCount - 1) ? bounds.size.width - segmentFrame.origin.x : FLOOR(((segment + 1) * basicSegmentWidth)) - FLOOR((segment * basicSegmentWidth)) - spacing; segmentFrame.size.width = (segment == segmentCount - 1) ? bounds.size.width - segmentFrame.origin.x : FLOOR(((segment + 1) * basicSegmentWidth)) - FLOOR((segment * basicSegmentWidth)) - _CPLevelIndicatorSpacing;
segmentFrame.size.height = segmentHeight; segmentFrame.size.height = segmentHeight;
return segmentFrame; return segmentFrame;
} }
return CGRectMakeZero(); return _CGRectMakeZero();
} }
/*! /*!
@@ -193,7 +223,7 @@ CPRatingLevelIndicatorStyle = 3;
return _isEditable; return _isEditable;
} }
- (CPView)hitTest:(CGPoint)aPoint - (CPView)hitTest:(CPPoint)aPoint
{ {
// Don't swallow clicks when displayed in a table. // Don't swallow clicks when displayed in a table.
if (![self isEditable]) if (![self isEditable])
@@ -223,7 +253,7 @@ CPRatingLevelIndicatorStyle = 3;
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil], var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
bounds = [self bounds], bounds = [self bounds],
oldValue = [self doubleValue], oldValue = [self doubleValue];
newValue = oldValue; newValue = oldValue;
// Moving the mouse outside of the widget to the left sets it // Moving the mouse outside of the widget to the left sets it
@@ -292,7 +322,7 @@ CPRatingLevelIndicatorStyle = 3;
[self setNeedsLayout]; [self setNeedsLayout];
} }
- (void)setWarningValue:(double)warningValue - (void)setWarningValue:(double)warningValue;
{ {
if (_warningValue === warningValue) if (_warningValue === warningValue)
return; return;
@@ -301,7 +331,7 @@ CPRatingLevelIndicatorStyle = 3;
[self setNeedsLayout]; [self setNeedsLayout];
} }
- (void)setCriticalValue:(double)criticalValue - (void)setCriticalValue:(double)criticalValue;
{ {
if (_criticalValue === criticalValue) if (_criticalValue === criticalValue)
return; return;
@@ -355,6 +385,8 @@ var CPLevelIndicatorStyleKey = "CPLevelIndicatorStyleKey",
_isEditable = [aCoder decodeBoolForKey:CPLevelIndicatorIsEditableKey]; _isEditable = [aCoder decodeBoolForKey:CPLevelIndicatorIsEditableKey];
[self _init];
[self setNeedsLayout]; [self setNeedsLayout];
[self setNeedsDisplay:YES]; [self setNeedsDisplay:YES];
} }
+64 -197
View File
@@ -25,21 +25,12 @@
@import <Foundation/CPNotificationCenter.j> @import <Foundation/CPNotificationCenter.j>
@import <Foundation/CPString.j> @import <Foundation/CPString.j>
@import "CPKeyValueBinding.j" @import "_CPMenuManager.j"
@import "CPApplication.j"
@import "CPClipView.j"
@import "CPMenuItem.j" @import "CPMenuItem.j"
@import "CPPanel.j"
@global CPApp
@protocol CPMenuDelegate <CPObject>
@optional
- (void)menuWillOpen:(CPMenu)aMenu;
- (void)menuDidClose:(CPMenu)aMenu;
@end
var CPMenuDelegate_menuWillOpen_ = 1 << 1,
CPMenuDelegate_menuDidClose_ = 1 << 2;
CPMenuDidAddItemNotification = @"CPMenuDidAddItemNotification"; CPMenuDidAddItemNotification = @"CPMenuDidAddItemNotification";
CPMenuDidChangeItemNotification = @"CPMenuDidChangeItemNotification"; CPMenuDidChangeItemNotification = @"CPMenuDidChangeItemNotification";
@@ -47,8 +38,12 @@ CPMenuDidRemoveItemNotification = @"CPMenuDidRemoveItemNotification";
CPMenuDidEndTrackingNotification = @"CPMenuDidEndTrackingNotification"; CPMenuDidEndTrackingNotification = @"CPMenuDidEndTrackingNotification";
var MENUBAR_HEIGHT = 28.0;
var _CPMenuBarVisible = NO, var _CPMenuBarVisible = NO,
_CPMenuBarTitle = @"", _CPMenuBarTitle = @"",
_CPMenuBarIconImage = nil,
_CPMenuBarIconImageAlphaValue = 1.0,
_CPMenuBarAttributes = nil, _CPMenuBarAttributes = nil,
_CPMenuBarSharedWindow = nil; _CPMenuBarSharedWindow = nil;
@@ -61,37 +56,34 @@ var _CPMenuBarVisible = NO,
*/ */
@implementation CPMenu : CPObject @implementation CPMenu : CPObject
{ {
CPMenu _supermenu; CPMenu _supermenu;
CPString _title; CPString _title;
CPString _name; CPString _name;
CPFont _font; CPFont _font;
float _minimumWidth; float _minimumWidth;
CPMutableArray _items; CPMutableArray _items;
BOOL _autoenablesItems; BOOL _autoenablesItems;
BOOL _showsStateColumn; BOOL _showsStateColumn;
id <CPMenuDelegate> _delegate; id _delegate;
unsigned _implementedDelegateMethods;
int _highlightedIndex; int _highlightedIndex;
_CPMenuWindow _menuWindow; _CPMenuWindow _menuWindow;
CPEvent _lastCloseEvent;
} }
// Managing the Menu Bar // Managing the Menu Bar
+ (void)initialize + (void)initialize
{ {
if (self !== [CPMenu class]) if (self !== CPMenu)
return; return;
[[self class] setMenuBarAttributes:@{}]; [[self class] setMenuBarAttributes:[CPDictionary dictionary]];
} }
+ (BOOL)menuBarVisible + (BOOL)menuBarVisible
@@ -117,8 +109,8 @@ var _CPMenuBarVisible = NO,
[_CPMenuBarSharedWindow setMenu:[CPApp mainMenu]]; [_CPMenuBarSharedWindow setMenu:[CPApp mainMenu]];
[_CPMenuBarSharedWindow setTitle:_CPMenuBarTitle]; [_CPMenuBarSharedWindow setTitle:_CPMenuBarTitle];
[_CPMenuBarSharedWindow setIconImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-icon-image" forClass:_CPMenuView]]; [_CPMenuBarSharedWindow setIconImage:_CPMenuBarIconImage];
[_CPMenuBarSharedWindow setIconImageAlphaValue:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-icon-image-alpha-value" forClass:_CPMenuView]]; [_CPMenuBarSharedWindow setIconImageAlphaValue:_CPMenuBarIconImageAlphaValue];
[_CPMenuBarSharedWindow setColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarBackgroundColor"]]; [_CPMenuBarSharedWindow setColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarBackgroundColor"]];
[_CPMenuBarSharedWindow setTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTextColor"]]; [_CPMenuBarSharedWindow setTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTextColor"]];
@@ -162,13 +154,6 @@ var _CPMenuBarVisible = NO,
return _CPMenuBarImage; return _CPMenuBarImage;
} }
+ (void)_setOrRemoveMenuBarAttribute:(id)aValue forKey:(id)aKey
{
if (aValue === nil)
[_CPMenuBarAttributes removeObjectForKey:aKey];
else
[_CPMenuBarAttributes setObject:aValue forKey:aKey];
}
+ (void)setMenuBarAttributes:(CPDictionary)attributes + (void)setMenuBarAttributes:(CPDictionary)attributes
{ {
@@ -193,8 +178,8 @@ var _CPMenuBarVisible = NO,
else if (!textColor && !titleColor) else if (!textColor && !titleColor)
{ {
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-color" forClass:_CPMenuView] forKey:@"CPMenuBarTextColor"]; [_CPMenuBarAttributes setObject:[CPColor colorWithRed:0.051 green:0.2 blue:0.275 alpha:1.0] forKey:@"CPMenuBarTextColor"];
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-title-color" forClass:_CPMenuView] forKey:@"CPMenuBarTitleColor"]; [_CPMenuBarAttributes setObject:[CPColor colorWithRed:0.051 green:0.2 blue:0.275 alpha:1.0] forKey:@"CPMenuBarTitleColor"];
} }
if (!textShadowColor && titleShadowColor) if (!textShadowColor && titleShadowColor)
@@ -205,18 +190,18 @@ var _CPMenuBarVisible = NO,
else if (!textShadowColor && !titleShadowColor) else if (!textShadowColor && !titleShadowColor)
{ {
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-shadow-color" forClass:_CPMenuView] forKey:@"CPMenuBarTextShadowColor"]; [_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarTextShadowColor"];
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-title-shadow-color" forClass:_CPMenuView] forKey:@"CPMenuBarTitleShadowColor"]; [_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarTitleShadowColor"];
} }
if (!highlightColor) if (!highlightColor)
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-color" forClass:_CPMenuView] forKey:@"CPMenuBarHighlightColor"]; [_CPMenuBarAttributes setObject:[CPColor colorWithCalibratedRed:94.0 / 255.0 green:130.0 / 255.0 blue:186.0 / 255.0 alpha:1.0] forKey:@"CPMenuBarHighlightColor"];
if (!highlightTextColor) if (!highlightTextColor)
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-color" forClass:_CPMenuView] forKey:@"CPMenuBarHighlightTextColor"]; [_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarHighlightTextColor"];
if (!highlightTextShadowColor) if (!highlightTextShadowColor)
[self _setOrRemoveMenuBarAttribute:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-shadow-color" forClass:_CPMenuView] forKey:@"CPMenuBarHighlightTextShadowColor"]; [_CPMenuBarAttributes setObject:[CPColor blackColor] forKey:@"CPMenuBarHighlightTextShadowColor"];
if (_CPMenuBarSharedWindow) if (_CPMenuBarSharedWindow)
{ {
@@ -245,14 +230,14 @@ var _CPMenuBarVisible = NO,
- (float)menuBarHeight - (float)menuBarHeight
{ {
if (self === [CPApp mainMenu]) if (self === [CPApp mainMenu])
return [CPMenu menuBarHeight]; return MENUBAR_HEIGHT;
return 0.0; return 0.0;
} }
+ (float)menuBarHeight + (float)menuBarHeight
{ {
return [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-height" forClass:_CPMenuView]; return MENUBAR_HEIGHT;
} }
// Creating a CPMenu Object // Creating a CPMenu Object
@@ -290,7 +275,7 @@ var _CPMenuBarVisible = NO,
@param aMenuItem the item to insert @param aMenuItem the item to insert
@param anIndex the index in the menu to insert the item. @param anIndex the index in the menu to insert the item.
*/ */
- (void)insertItem:(CPMenuItem)aMenuItem atIndex:(CPUInteger)anIndex - (void)insertItem:(CPMenuItem)aMenuItem atIndex:(unsigned)anIndex
{ {
[self insertObject:aMenuItem inItemsAtIndex:anIndex]; [self insertObject:aMenuItem inItemsAtIndex:anIndex];
} }
@@ -303,7 +288,7 @@ var _CPMenuBarVisible = NO,
@param anIndex the index location in the menu for the new item @param anIndex the index location in the menu for the new item
@return the new menu item @return the new menu item
*/ */
- (CPMenuItem)insertItemWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent atIndex:(CPUInteger)anIndex - (CPMenuItem)insertItemWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent atIndex:(unsigned)anIndex
{ {
var item = [[CPMenuItem alloc] initWithTitle:aTitle action:anAction keyEquivalent:aKeyEquivalent]; var item = [[CPMenuItem alloc] initWithTitle:aTitle action:anAction keyEquivalent:aKeyEquivalent];
@@ -347,7 +332,7 @@ var _CPMenuBarVisible = NO,
Removes the item at the specified index from the menu Removes the item at the specified index from the menu
@param anIndex the index of the item to remove @param anIndex the index of the item to remove
*/ */
- (void)removeItemAtIndex:(CPUInteger)anIndex - (void)removeItemAtIndex:(unsigned)anIndex
{ {
[self removeObjectFromItemsAtIndex:anIndex]; [self removeObjectFromItemsAtIndex:anIndex];
} }
@@ -367,12 +352,8 @@ var _CPMenuBarVisible = NO,
while (count--) while (count--)
[_items[count] setMenu:nil]; [_items[count] setMenu:nil];
[self _highlightItemAtIndex:CPNotFound];
// Because we are changing _items directly, be sure to notify KVO
[self willChangeValueForKey:@"items"];
_items = [CPMutableArray array]; _items = [CPMutableArray array];
[self didChangeValueForKey:@"items"]; _highlightedIndex = CPNotFound;
} }
/*! /*!
@@ -381,12 +362,7 @@ var _CPMenuBarVisible = NO,
*/ */
- (void)itemChanged:(CPMenuItem)aMenuItem - (void)itemChanged:(CPMenuItem)aMenuItem
{ {
/* if ([aMenuItem menu] !== self)
During cib unarchiving, menu items will have a reference to their menu,
but of course the items are still being unarchived and the menu's _items
have not yet been instantiated. In that case we will not do anything here.
*/
if ([aMenuItem menu] !== self || !_items)
return; return;
[aMenuItem setValue:[aMenuItem valueForKey:@"changeCount"] + 1 forKey:@"changeCount"]; [aMenuItem setValue:[aMenuItem valueForKey:@"changeCount"] + 1 forKey:@"changeCount"];
@@ -394,7 +370,7 @@ var _CPMenuBarVisible = NO,
[[CPNotificationCenter defaultCenter] [[CPNotificationCenter defaultCenter]
postNotificationName:CPMenuDidChangeItemNotification postNotificationName:CPMenuDidChangeItemNotification
object:self object:self
userInfo:@{ @"CPMenuItemIndex": [_items indexOfObjectIdenticalTo:aMenuItem] }]; userInfo:[CPDictionary dictionaryWithObject:[_items indexOfObjectIdenticalTo:aMenuItem] forKey:@"CPMenuItemIndex"]];
} }
// Finding Menu Items // Finding Menu Items
@@ -627,17 +603,15 @@ var _CPMenuBarVisible = NO,
/*! /*!
Enables or disables the receivers menu items. Enables or disables the receivers menu items.
If the item has no target, an action binding is checked. If the target does not implement If the target does not implement the menu item's action method the item is disabled.
the menu item's action method or the binding's selector, the item is disabled. If the target responsds to selector validateMenuItem: or validateUserInterfaceItem: (in that order) the return value is used.
If the target responds to selector validateMenuItem: or validateUserInterfaceItem: (in that order) the return value is used.
*/ */
- (void)update - (void)update
{ {
if (!_autoenablesItems) if (![self autoenablesItems])
return; return;
var items = [self itemArray]; var items = [self itemArray];
for (var i = 0; i < [items count]; i++) for (var i = 0; i < [items count]; i++)
{ {
var item = [items objectAtIndex:i]; var item = [items objectAtIndex:i];
@@ -645,51 +619,14 @@ var _CPMenuBarVisible = NO,
if ([item hasSubmenu]) if ([item hasSubmenu])
continue; continue;
// If there are enabled bindings for the item, they override anything else var validator = [CPApp targetForAction:[item action] to:[item target] from:item];
var binder = [CPBinder getBinding:CPEnabledBinding forObject:item];
if (binder) if (!validator || ![validator respondsToSelector:[item action]])
{ [item _setEnabled:NO];
[binder setValueFor:CPEnabledBinding];
[[_menuWindow _menuView] tile];
return;
}
var validator = [CPApp targetForAction:[item action] to:[item target] from:item],
shouldBeEnabled = YES;
if (!validator)
{
// If targetForAction: returns nil, it could be that there is no action.
// If there is an action and nil is returned, no valid target could be found.
if ([item action] || [item target])
shouldBeEnabled = NO;
else
{
// Check to see if there is a target binding with an invalid selector
var info = [CPBinder infoForBinding:CPTargetBinding forObject:item];
if (info)
{
var object = [info objectForKey:CPObservedObjectKey],
keyPath = [info objectForKey:CPObservedKeyPathKey],
options = [info objectForKey:CPOptionsKey],
target = [object valueForKeyPath:keyPath],
selector = [options valueForKey:CPSelectorNameBindingOption];
if (target && selector && ![target respondsToSelector:CPSelectorFromString(selector)])
shouldBeEnabled = NO;
}
}
}
else if (![validator respondsToSelector:[item action]])
shouldBeEnabled = NO;
else if ([validator respondsToSelector:@selector(validateMenuItem:)]) else if ([validator respondsToSelector:@selector(validateMenuItem:)])
shouldBeEnabled = [validator validateMenuItem:item]; [item _setEnabled:[validator validateMenuItem:item]];
else if ([validator respondsToSelector:@selector(validateUserInterfaceItem:)]) else if ([validator respondsToSelector:@selector(validateUserInterfaceItem:)])
shouldBeEnabled = [validator validateUserInterfaceItem:item]; [item _setEnabled:[validator validateUserInterfaceItem:item]];
[item setEnabled:shouldBeEnabled];
} }
[[_menuWindow _menuView] tile]; [[_menuWindow _menuView] tile];
@@ -734,13 +671,7 @@ var _CPMenuBarVisible = NO,
// highlightedItem is always enabled. Do there exist edge cases: disabling on closing a menu, // highlightedItem is always enabled. Do there exist edge cases: disabling on closing a menu,
// etc.? Requires further investigation and tests. // etc.? Requires further investigation and tests.
if (highlightedItem && [highlightedItem isEnabled]) if (highlightedItem && [highlightedItem isEnabled])
{
// Perform any action binding
var binding = [CPBinder getBinding:CPTargetBinding forObject:highlightedItem];
[binding invokeAction];
[CPApp sendAction:[highlightedItem action] to:[highlightedItem target] from:highlightedItem]; [CPApp sendAction:[highlightedItem action] to:[highlightedItem target] from:highlightedItem];
}
} }
// //
@@ -780,7 +711,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:self];
// Convert location to global coordinates if not already in them. // Convert location to global coordinates if not already in them.
if (aView) if (aView)
@@ -868,13 +802,13 @@ 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
{ {
// This is needed when we are making several rights click var delegate = [aMenu delegate];
[[_CPMenuManager sharedMenuManager] cancelActiveMenu];
[aMenu _menuWillOpen]; if ([delegate respondsToSelector:@selector(menuWillOpen:)])
[delegate menuWillOpen:aMenu];
if (!aFont) if (!aFont)
aFont = [CPFont systemFontOfSize:[CPFont systemFontSize]]; aFont = [CPFont systemFontOfSize:12.0];
var theWindow = [aView window], var theWindow = [aView window],
menuWindow = [_CPMenuWindow menuWindowWithMenu:aMenu font:aFont]; menuWindow = [_CPMenuWindow menuWindowWithMenu:aMenu font:aFont];
@@ -941,32 +875,14 @@ 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
- (void)setDelegate:(id <CPMenuDelegate>)aDelegate - (void)setDelegate:(id)aDelegate
{ {
if (_delegate === aDelegate)
return;
_delegate = aDelegate; _delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(menuWillOpen:)])
_implementedDelegateMethods |= CPMenuDelegate_menuWillOpen_;
if ([_delegate respondsToSelector:@selector(menuDidClose:)])
_implementedDelegateMethods |= CPMenuDelegate_menuDidClose_;
} }
- (id)delegate - (id)delegate
@@ -974,20 +890,6 @@ var _CPMenuBarVisible = NO,
return _delegate; return _delegate;
} }
- (void)_menuWillOpen
{
[self _sendDelegateMenuWillOpen];
}
- (void)_menuDidClose
{
// Remember which event caused this menu to close, if any. CPPopUpButton uses this to detect
// when a click on the button itself caused the menu to close.
_lastCloseEvent = [CPApp currentEvent];
[self _sendDelegateMenuDidClose];
}
// Handling Tracking // Handling Tracking
/*! /*!
Cancels tracking. Cancels tracking.
@@ -1001,7 +903,7 @@ var _CPMenuBarVisible = NO,
{ {
[CPApp sendEvent:[CPEvent [CPApp sendEvent:[CPEvent
otherEventWithType:CPAppKitDefined otherEventWithType:CPAppKitDefined
location:CGPointMakeZero() location:_CGPointMakeZero()
modifierFlags:0 modifierFlags:0
timestamp:0 timestamp:0
windowNumber:0 windowNumber:0
@@ -1052,7 +954,8 @@ var _CPMenuBarVisible = NO,
for (; index < count; ++index) for (; index < count; ++index)
{ {
var item = _items[index]; var item = _items[index],
modifierMask = [item keyEquivalentModifierMask];
if ([anEvent _triggersKeyEquivalent:[item keyEquivalent] withModifierMask:[item keyEquivalentModifierMask]]) if ([anEvent _triggersKeyEquivalent:[item keyEquivalent] withModifierMask:[item keyEquivalentModifierMask]])
{ {
@@ -1078,7 +981,7 @@ var _CPMenuBarVisible = NO,
Sends the action of the menu item at the specified index. Sends the action of the menu item at the specified index.
@param anIndex the index of the item @param anIndex the index of the item
*/ */
- (void)performActionForItemAtIndex:(CPUInteger)anIndex - (void)performActionForItemAtIndex:(unsigned)anIndex
{ {
var item = _items[anIndex]; var item = _items[anIndex];
@@ -1149,36 +1052,6 @@ var _CPMenuBarVisible = NO,
@end @end
@implementation CPMenu (CPMenuDelegate)
/*!
@ignore
Call delegate menuWillOpen
*/
- (void)_sendDelegateMenuWillOpen
{
if (!(_implementedDelegateMethods & CPMenuDelegate_menuWillOpen_))
return;
[_delegate menuWillOpen:self];
}
/*!
@ignore
Call delegate menuDidClose
*/
- (void)_sendDelegateMenuDidClose
{
if (!(_implementedDelegateMethods & CPMenuDelegate_menuDidClose_))
return;
[_delegate menuDidClose:self];
}
@end
@implementation CPMenu (CPKeyValueCoding) @implementation CPMenu (CPKeyValueCoding)
- (CPUInteger)countOfItems - (CPUInteger)countOfItems
@@ -1211,13 +1084,12 @@ var _CPMenuBarVisible = NO,
return; return;
[aMenuItem setMenu:self]; [aMenuItem setMenu:self];
[self _highlightItemAtIndex:CPNotFound];
[_items insertObject:aMenuItem atIndex:anIndex]; [_items insertObject:aMenuItem atIndex:anIndex];
[[CPNotificationCenter defaultCenter] [[CPNotificationCenter defaultCenter]
postNotificationName:CPMenuDidAddItemNotification postNotificationName:CPMenuDidAddItemNotification
object:self object:self
userInfo:@{ @"CPMenuItemIndex": anIndex }]; userInfo:[CPDictionary dictionaryWithObject:anIndex forKey:@"CPMenuItemIndex"]];
} }
- (void)removeObjectFromItemsAtIndex:(CPUInteger)anIndex - (void)removeObjectFromItemsAtIndex:(CPUInteger)anIndex
@@ -1226,13 +1098,12 @@ var _CPMenuBarVisible = NO,
return; return;
[[_items objectAtIndex:anIndex] setMenu:nil]; [[_items objectAtIndex:anIndex] setMenu:nil];
[self _highlightItemAtIndex:CPNotFound];
[_items removeObjectAtIndex:anIndex]; [_items removeObjectAtIndex:anIndex];
[[CPNotificationCenter defaultCenter] [[CPNotificationCenter defaultCenter]
postNotificationName:CPMenuDidRemoveItemNotification postNotificationName:CPMenuDidRemoveItemNotification
object:self object:self
userInfo:@{ @"CPMenuItemIndex": anIndex }]; userInfo:[CPDictionary dictionaryWithObject:anIndex forKey:@"CPMenuItemIndex"]];
} }
@end @end
@@ -1240,8 +1111,7 @@ var _CPMenuBarVisible = NO,
var CPMenuTitleKey = @"CPMenuTitleKey", var CPMenuTitleKey = @"CPMenuTitleKey",
CPMenuNameKey = @"CPMenuNameKey", CPMenuNameKey = @"CPMenuNameKey",
CPMenuItemsKey = @"CPMenuItemsKey", CPMenuItemsKey = @"CPMenuItemsKey",
CPMenuShowsStateColumnKey = @"CPMenuShowsStateColumnKey", CPMenuShowsStateColumnKey = @"CPMenuShowsStateColumnKey";
CPMenuAutoEnablesItemsKey = @"CPMenuAutoEnablesItemsKey";
@implementation CPMenu (CPCoding) @implementation CPMenu (CPCoding)
@@ -1263,7 +1133,7 @@ var CPMenuTitleKey = @"CPMenuTitleKey",
_showsStateColumn = ![aCoder containsValueForKey:CPMenuShowsStateColumnKey] || [aCoder decodeBoolForKey:CPMenuShowsStateColumnKey]; _showsStateColumn = ![aCoder containsValueForKey:CPMenuShowsStateColumnKey] || [aCoder decodeBoolForKey:CPMenuShowsStateColumnKey];
_autoenablesItems = ![aCoder containsValueForKey:CPMenuAutoEnablesItemsKey] || [aCoder decodeBoolForKey:CPMenuAutoEnablesItemsKey]; _autoenablesItems = YES;
[self setMinimumWidth:0]; [self setMinimumWidth:0];
} }
@@ -1286,9 +1156,6 @@ var CPMenuTitleKey = @"CPMenuTitleKey",
if (!_showsStateColumn) if (!_showsStateColumn)
[aCoder encodeBool:_showsStateColumn forKey:CPMenuShowsStateColumnKey]; [aCoder encodeBool:_showsStateColumn forKey:CPMenuShowsStateColumnKey];
if (!_autoenablesItems)
[aCoder encodeBool:_autoenablesItems forKey:CPMenuAutoEnablesItemsKey];
} }
@end @end
+136 -52
View File
@@ -1,37 +1,18 @@
/*
* _CPMenuBarWindow.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPPanel.j" @import "CPPanel.j"
@import "_CPMenuManager.j" @import "_CPMenuWindow.j"
@class _CPMenuView
@class CPMenu
@global CPMenuDidAddItemNotification var MENUBAR_HEIGHT = 28.0,
@global CPMenuDidChangeItemNotification MENUBAR_MARGIN = 10.0,
@global CPMenuDidRemoveItemNotification MENUBAR_LEFT_MARGIN = 10.0,
MENUBAR_RIGHT_MARGIN = 10.0;
var _CPMenuBarWindowBackgroundColor = nil,
_CPMenuBarWindowFont = nil;
@implementation _CPMenuBarWindow : CPPanel @implementation _CPMenuBarWindow : CPPanel
{ {
CPMenu _menu;
CPView _highlightView; CPView _highlightView;
CPArray _menuItemViews; CPArray _menuItemViews;
@@ -51,9 +32,19 @@
CPColor _highlightTextShadowColor; CPColor _highlightTextShadowColor;
} }
+ (void)initialize
{
if (self != [_CPMenuBarWindow class])
return;
var bundle = [CPBundle bundleForClass:self];
_CPMenuBarWindowFont = [CPFont boldSystemFontOfSize:12.0];
}
+ (CPFont)font + (CPFont)font
{ {
return [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-font" forClass:_CPMenuView]; return _CPMenuBarWindowFont;
} }
- (id)init - (id)init
@@ -61,14 +52,12 @@
// This only shows up in browser land, so don't bother calculating metrics in desktop. // This only shows up in browser land, so don't bother calculating metrics in desktop.
var contentRect = [[CPPlatformWindow primaryPlatformWindow] contentBounds]; var contentRect = [[CPPlatformWindow primaryPlatformWindow] contentBounds];
contentRect.size.height = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-height" forClass:_CPMenuView]; contentRect.size.height = MENUBAR_HEIGHT;
self = [super initWithContentRect:contentRect styleMask:CPBorderlessWindowMask]; self = [super initWithContentRect:contentRect styleMask:CPBorderlessWindowMask];
if (self) if (self)
{ {
_constrainsToUsableScreen = NO;
[self setLevel:CPMainMenuWindowLevel]; [self setLevel:CPMainMenuWindowLevel];
[self setAutoresizingMask:CPWindowWidthSizable]; [self setAutoresizingMask:CPWindowWidthSizable];
@@ -85,7 +74,7 @@
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; _titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
[_titleField setFont:[CPFont boldSystemFontOfSize:[CPFont systemFontSize] + 1]]; [_titleField setFont:[CPFont boldSystemFontOfSize:13.0]];
[_titleField setAlignment:CPCenterTextAlignment]; [_titleField setAlignment:CPCenterTextAlignment];
[_titleField setTextShadowOffset:CGSizeMake(0, 1)]; [_titleField setTextShadowOffset:CGSizeMake(0, 1)];
@@ -130,7 +119,12 @@
- (void)setColor:(CPColor)aColor - (void)setColor:(CPColor)aColor
{ {
if (!aColor) if (!aColor)
[[self contentView] setBackgroundColor:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-background-color" forClass:_CPMenuView]]; {
if (!_CPMenuBarWindowBackgroundColor)
_CPMenuBarWindowBackgroundColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[_CPMenuBarWindow class]] pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackground.png"] size:CGSizeMake(1.0, 28.0)]];
[[self contentView] setBackgroundColor:_CPMenuBarWindowBackgroundColor];
}
else else
[[self contentView] setBackgroundColor:aColor]; [[self contentView] setBackgroundColor:aColor];
} }
@@ -143,7 +137,6 @@
_textColor = aColor; _textColor = aColor;
[_menuItemViews makeObjectsPerformSelector:@selector(setTextColor:) withObject:_textColor]; [_menuItemViews makeObjectsPerformSelector:@selector(setTextColor:) withObject:_textColor];
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuTextColor:) withObject:_textColor];
} }
- (void)setTitleColor:(CPColor)aColor - (void)setTitleColor:(CPColor)aColor
@@ -164,7 +157,6 @@
_textShadowColor = aColor; _textShadowColor = aColor;
[_menuItemViews makeObjectsPerformSelector:@selector(setTextShadowColor:) withObject:_textShadowColor]; [_menuItemViews makeObjectsPerformSelector:@selector(setTextShadowColor:) withObject:_textShadowColor];
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuTextShadowColor:) withObject:_textShadowColor];
} }
- (void)setTitleShadowColor:(CPColor)aColor - (void)setTitleShadowColor:(CPColor)aColor
@@ -183,8 +175,6 @@
return; return;
_highlightColor = aColor; _highlightColor = aColor;
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuHighlightColor:) withObject:_highlightColor];
} }
- (void)setHighlightTextColor:(CPColor)aColor - (void)setHighlightTextColor:(CPColor)aColor
@@ -194,7 +184,7 @@
_highlightTextColor = aColor; _highlightTextColor = aColor;
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuHighlightTextColor:) withObject:_highlightTextColor]; // [_menuItemViews makeObjectsPerformSelector:@selector(setActivateColor:) withObject:_highlightTextColor];
} }
- (void)setHighlightTextShadowColor:(CPColor)aColor - (void)setHighlightTextShadowColor:(CPColor)aColor
@@ -204,7 +194,7 @@
_highlightTextShadowColor = aColor; _highlightTextShadowColor = aColor;
[_menuItemViews makeObjectsPerformSelector:@selector(setParentMenuHighlightTextShadowColor:) withObject:_highlightTextShadowColor]; // [_menuItemViews makeObjectsPerformSelector:@selector(setActivateShadowColor:) withObject:_highlightTextShadowColor];
} }
- (void)setMenu:(CPMenu)aMenu - (void)setMenu:(CPMenu)aMenu
@@ -267,7 +257,7 @@
items = [_menu itemArray], items = [_menu itemArray],
count = items.length; count = items.length;
for (var index = 0; index < count; ++index) for (index = 0; index < count; ++index)
{ {
var item = items[index], var item = items[index],
menuItemView = [item _menuItemView]; menuItemView = [item _menuItemView];
@@ -326,11 +316,6 @@
[self tile]; [self tile];
} }
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
{
return YES;
}
- (void)mouseDown:(CPEvent)anEvent - (void)mouseDown:(CPEvent)anEvent
{ {
var constraintRect = CGRectInset([[self platformWindow] visibleFrame], 5.0, 0.0); var constraintRect = CGRectInset([[self platformWindow] visibleFrame], 5.0, 0.0);
@@ -350,7 +335,7 @@
- (CPFont)font - (CPFont)font
{ {
[CPFont systemFontOfSize:[CPFont systemFontSize]]; [CPFont systemFontOfSize:12.0];
} }
- (void)tile - (void)tile
@@ -359,7 +344,7 @@
index = 0, index = 0,
count = items.length, count = items.length,
x = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-left-margin" forClass:_CPMenuView], x = MENUBAR_LEFT_MARGIN,
y = 0.0, y = 0.0,
isLeftAligned = YES; isLeftAligned = YES;
@@ -369,7 +354,7 @@
if ([item isSeparatorItem]) if ([item isSeparatorItem])
{ {
x = CGRectGetWidth([self frame]) - [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-right-margin" forClass:_CPMenuView]; x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN;
isLeftAligned = NO; isLeftAligned = NO;
continue; continue;
@@ -383,13 +368,13 @@
if (isLeftAligned) if (isLeftAligned)
{ {
[menuItemView setFrame:CGRectMake(x, 0.0, CGRectGetWidth(frame), [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-height" forClass:_CPMenuView])]; [menuItemView setFrame:CGRectMake(x, 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
x += CGRectGetWidth([menuItemView frame]); x += CGRectGetWidth([menuItemView frame]);
} }
else else
{ {
[menuItemView setFrame:CGRectMake(x - CGRectGetWidth(frame), 0.0, CGRectGetWidth(frame), [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-height" forClass:_CPMenuView])]; [menuItemView setFrame:CGRectMake(x - CGRectGetWidth(frame), 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
x = CGRectGetMinX([menuItemView frame]); x = CGRectGetMinX([menuItemView frame]);
} }
@@ -417,7 +402,7 @@
[super setFrame:aRect display:shouldDisplay animate:shouldAnimate]; [super setFrame:aRect display:shouldDisplay animate:shouldAnimate];
if (!CGSizeEqualToSize(size, aRect.size)) if (!_CGSizeEqualToSize(size, aRect.size))
[self tile]; [self tile];
} }
@@ -467,3 +452,102 @@
} }
@end @end
@implementation _CPMenuBarView : _CPMenuView
{
}
- (CGRect)rectForItemAtIndex:(int)anIndex
{
return [_menuItemViews[anIndex === CPNotFound ? 0 : anIndex] frame];
}
- (int)itemIndexAtPoint:(CGPoint)aPoint
{
var bounds = [self bounds];
if (!CGRectContainsPoint(bounds, aPoint))
return CPNotFound;
var x = aPoint.x,
low = 0,
high = _visibleMenuItemInfos.length - 1;
while (low <= high)
{
var middle = FLOOR(low + (high - low) / 2),
info = _visibleMenuItemInfos[middle],
frame = [info.view frame];
if (x < CGRectGetMinX(frame))
high = middle - 1;
else if (x > CGRectGetMaxX(frame))
low = middle + 1;
else
return info.index;
}
return CPNotFound;
}
- (void)tile
{
var items = [_menu itemArray],
index = 0,
count = items.length,
x = MENUBAR_LEFT_MARGIN,
y = 0.0,
isLeftAligned = YES;
for (; index < count; ++index)
{
var item = items[index];
if ([item isSeparatorItem])
{
x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN;
isLeftAligned = NO;
continue;
}
if ([item isHidden])
continue;
var menuItemView = [item _menuItemView],
frame = [menuItemView frame];
if (isLeftAligned)
{
[menuItemView setFrameOrigin:CGPointMake(x, (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)];
x += CGRectGetWidth([menuItemView frame]) + MENUBAR_MARGIN;
}
else
{
[menuItemView setFrameOrigin:CGPointMake(x - CGRectGetWidth(frame), (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)];
x = CGRectGetMinX([menuItemView frame]) - MENUBAR_MARGIN;
}
}
var bounds = [[self contentView] bounds],
titleFrame = [_titleField frame];
if ([_iconImageView isHidden])
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(titleFrame)) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
else
{
var iconFrame = [_iconImageView frame],
iconWidth = CGRectGetWidth(iconFrame),
totalWidth = iconWidth + CGRectGetWidth(titleFrame);
[_iconImageView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(iconFrame)) / 2.0)];
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0 + iconWidth, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
}
}
@end
+51 -174
View File
@@ -1,52 +1,16 @@
/*
* _CPMenuManager.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPNotificationCenter.j>
@import "CPEvent.j"
@import "CPKeyBinding.j"
@class CPWindow
@class _CPMenuWindow
@global CPApp _CPMenuManagerScrollingStateUp = -1,
@global CPMenuDidEndTrackingNotification _CPMenuManagerScrollingStateDown = 1,
@global _CPMenuWindowMenuBarBackgroundStyle
@global _CPMenuWindowPopUpBackgroundStyle
_CPMenuManagerScrollingStateUp = -1;
_CPMenuManagerScrollingStateDown = 1;
_CPMenuManagerScrollingStateNone = 0; _CPMenuManagerScrollingStateNone = 0;
var STICKY_TIME_INTERVAL = 0.4, var STICKY_TIME_INTERVAL = 500,
SharedMenuManager = nil; SharedMenuManager = nil;
@implementation _CPMenuManager: CPObject @implementation _CPMenuManager: CPObject
{ {
CPTimeInterval _startTime; CPTimeInterval _startTime;
CPEvent _openEvent;
BOOL _mouseWasDragged;
int _scrollingState; int _scrollingState;
CGPoint _lastGlobalLocation; CGPoint _lastGlobalLocation;
@@ -61,8 +25,6 @@ var STICKY_TIME_INTERVAL = 0.4,
CPMenuItem _previousActiveItem; CPMenuItem _previousActiveItem;
int _showTimerID; int _showTimerID;
int _menuBarButtonItemIndex;
} }
+ (_CPMenuManager)sharedMenuManager + (_CPMenuManager)sharedMenuManager
@@ -98,13 +60,9 @@ var STICKY_TIME_INTERVAL = 0.4,
{ {
var menu = [aMenuContainer menu]; var menu = [aMenuContainer menu];
if ([menu numberOfItems] <= 0)
return;
CPApp._activeMenu = menu; CPApp._activeMenu = menu;
_startTime = [anEvent timestamp]; _startTime = [anEvent timestamp];//new Date();
_openEvent = anEvent;
_scrollingState = _CPMenuManagerScrollingStateNone; _scrollingState = _CPMenuManagerScrollingStateNone;
_constraintRect = aRect; _constraintRect = aRect;
@@ -123,22 +81,15 @@ var STICKY_TIME_INTERVAL = 0.4,
activeItem = activeItemIndex !== CPNotFound ? [menu itemAtIndex:activeItemIndex] : nil; activeItem = activeItemIndex !== CPNotFound ? [menu itemAtIndex:activeItemIndex] : nil;
_menuBarButtonItemIndex = activeItemIndex; _menuBarButtonItemIndex = activeItemIndex;
// _menuBarButtonMenuContainer = aMenuContainer; _menuBarButtonMenuContainer = aMenuContainer;
if ([activeItem _isMenuBarButton]) if ([activeItem _isMenuBarButton])
return [self trackMenuBarButtonEvent:anEvent]; return [self trackMenuBarButtonEvent:anEvent];
} }
_mouseWasDragged = NO;
[self trackEvent:anEvent]; [self trackEvent:anEvent];
} }
- (void)_trackAgain
{
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPKeyDownMask | CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseDownMask | CPLeftMouseUpMask | CPRightMouseUpMask | CPAppKitDefinedMask | CPScrollWheelMask untilDate:nil inMode:nil dequeue:YES];
}
- (void)trackEvent:(CPEvent)anEvent - (void)trackEvent:(CPEvent)anEvent
{ {
var type = [anEvent type], var type = [anEvent type],
@@ -148,6 +99,8 @@ var STICKY_TIME_INTERVAL = 0.4,
if (type === CPAppKitDefined) if (type === CPAppKitDefined)
return [self completeTracking]; return [self completeTracking];
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPKeyDownMask | CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask | CPScrollWheelMask untilDate:nil inMode:nil dequeue:YES];
if (type === CPKeyDown) if (type === CPKeyDown)
{ {
var menu = trackingMenu, var menu = trackingMenu,
@@ -163,18 +116,26 @@ var STICKY_TIME_INTERVAL = 0.4,
if ([menu numberOfItems]) if ([menu numberOfItems])
[self interpretKeyEvent:anEvent forMenu:menu]; [self interpretKeyEvent:anEvent forMenu:menu];
[self _trackAgain];
return; 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];
if (!globalLocation) // Remember this for the next periodic event.
{ _lastGlobalLocation = globalLocation;
[self _trackAgain];
if (!_lastGlobalLocation)
return; 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],
@@ -185,43 +146,6 @@ var STICKY_TIME_INTERVAL = 0.4,
activeItemIndex = activeMenuContainer ? [activeMenuContainer itemIndexAtPoint:menuLocation] : CPNotFound, activeItemIndex = activeMenuContainer ? [activeMenuContainer itemIndexAtPoint:menuLocation] : CPNotFound,
activeItem = activeItemIndex !== CPNotFound ? [activeMenu itemAtIndex:activeItemIndex] : nil; activeItem = activeItemIndex !== CPNotFound ? [activeMenu itemAtIndex:activeItemIndex] : nil;
/*
Click outside the menu structure?
The event which caused the menu to open is not considered even if it's
technically a click outside of an open menu, since this would mean the same
click which opened the menu could also close it. This is actually common.
E.g. you click on a button and it opens a menu below itself.
*/
if (type === CPLeftMouseDown && _openEvent !== anEvent && (!activeMenuContainer || !CGRectContainsPoint([activeMenuContainer globalFrame], globalLocation)))
{
[self completeTracking];
/*
You can close and interact with a control in a single click. E.g. you can have a menu open,
click on a button outside of it and have the menu immediately close and the button activate,
without having to click once to close the menu and once to activate the button.
Since we normally dequeue the event after tracking it, we'll have to put it back on the stack
in this special case. Note that it's important the event is executed /right now/, since certain
controls such as HTML upload buttons need a native click event at the top of the stack trace
to activate - it's not something we can fake later.
*/
[CPApp sendEvent:anEvent];
return;
}
[self _trackAgain];
// unhighlight when mouse is moved off the menu
if (_lastGlobalLocation && CGRectContainsPoint([activeMenuContainer globalFrame], _lastGlobalLocation)
&& !CGRectContainsPoint([activeMenuContainer globalFrame], globalLocation))
[activeMenu _highlightItemAtIndex:CPNotFound];
// Remember this for the next periodic event.
_lastGlobalLocation = globalLocation;
// If the item isn't enabled its as if we clicked on nothing. // If the item isn't enabled its as if we clicked on nothing.
if (![activeItem isEnabled] || [activeItem _isMenuBarButton]) if (![activeItem isEnabled] || [activeItem _isMenuBarButton])
{ {
@@ -231,7 +155,7 @@ var STICKY_TIME_INTERVAL = 0.4,
var mouseOverMenuView = [activeItem view]; var mouseOverMenuView = [activeItem view];
if (type === CPScrollWheel && ![activeMenuContainer isMenuBar]) if (type === CPScrollWheel)
[activeMenuContainer scrollByDelta:[anEvent deltaY]]; [activeMenuContainer scrollByDelta:[anEvent deltaY]];
if (type === CPPeriodic) if (type === CPPeriodic)
@@ -250,16 +174,16 @@ var STICKY_TIME_INTERVAL = 0.4,
if (_lastMouseOverMenuView != mouseOverMenuView) if (_lastMouseOverMenuView != mouseOverMenuView)
{ {
[_lastMouseOverMenuView mouseExited:anEvent]; [mouseOverMenuView mouseExited:anEvent];
// FIXME: Possibly multiple of these? // FIXME: Possibly multiple of these?
[mouseOverMenuView mouseEntered:anEvent]; [_lastMouseOverMenuView mouseEntered:anEvent];
_lastMouseOverMenuView = mouseOverMenuView; _lastMouseOverMenuView = mouseOverMenuView;
} }
var menuContainerWindow = activeMenuContainer; var menuContainerWindow = activeMenuContainer;
if (![menuContainerWindow isKindOfClass:CPWindow]) if (![menuContainerWindow isKindOfClass:[CPWindow class]])
menuContainerWindow = [menuContainerWindow window]; menuContainerWindow = [menuContainerWindow window];
[menuContainerWindow [menuContainerWindow
@@ -281,14 +205,10 @@ var STICKY_TIME_INTERVAL = 0.4,
_lastMouseOverMenuView = nil; _lastMouseOverMenuView = nil;
} }
if (activeItemIndex !== CPNotFound) [activeMenu _highlightItemAtIndex:activeItemIndex];
[activeMenu _highlightItemAtIndex:activeItemIndex];
if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown || type === CPPeriodic) if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown || type === CPPeriodic)
{ {
if (type === CPLeftMouseDragged)
_mouseWasDragged = YES;
var oldScrollingState = _scrollingState; var oldScrollingState = _scrollingState;
_scrollingState = [activeMenuContainer scrollingStateForPoint:globalLocation]; _scrollingState = [activeMenuContainer scrollingStateForPoint:globalLocation];
@@ -301,38 +221,8 @@ var STICKY_TIME_INTERVAL = 0.4,
[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 + [activeMenu numberOfItems] * 5)))
{ [trackingMenu cancelTracking];
/*
There are a few possibilites:
1. The user clicks and releases without dragging within the sticky time.
This is considered a regular mouse click and not a drag. In this case
we allow the user to track the menu by moving the mouse. The next
mouse up will end tracking.
2. The user clicks and releases without dragging after the sticky time.
This is considered a drag and release and tracking ends.
3. The user clicks, drags and then releases. Tracking ends.
*/
if (_mouseWasDragged || ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL))
{
/*
Close the menu if:
1. The mouse was dragged.
2. The mouse is released in the menubar.
3. The current item has a submenu with a custom action.
*/
if (_mouseWasDragged ||
[activeMenuContainer isMenuBar] ||
[activeItem action] !== @selector(submenuAction:))
{
[trackingMenu cancelTracking];
}
}
}
} }
// Prevent previous selected menu items from opening by stopping the timer if a // Prevent previous selected menu items from opening by stopping the timer if a
@@ -344,7 +234,7 @@ var STICKY_TIME_INTERVAL = 0.4,
} }
// 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; newMenuOrigin;
@@ -407,15 +297,6 @@ var STICKY_TIME_INTERVAL = 0.4,
[menu cancelTracking]; [menu cancelTracking];
} }
- (void)cancelActiveMenu
{
if (CPApp._activeMenu)
{
[self completeTracking];
_menuContainerStack = [];
}
}
- (void)completeTracking - (void)completeTracking
{ {
var trackingMenu = [self trackingMenu]; var trackingMenu = [self trackingMenu];
@@ -425,7 +306,11 @@ var STICKY_TIME_INTERVAL = 0.4,
// 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);
@@ -466,8 +351,8 @@ var STICKY_TIME_INTERVAL = 0.4,
{ {
var menuContainer = _menuContainerStack[index], var menuContainer = _menuContainerStack[index],
menuContainerFrame = [menuContainer globalFrame], menuContainerFrame = [menuContainer globalFrame],
menuContainerMinX = CGRectGetMinX(menuContainerFrame), menuContainerMinX = _CGRectGetMinX(menuContainerFrame),
menuContainerMaxX = CGRectGetMaxX(menuContainerFrame); menuContainerMaxX = _CGRectGetMaxX(menuContainerFrame);
// If within the x bounds of this menu container, return it. // If within the x bounds of this menu container, return it.
if (x < menuContainerMaxX && x >= menuContainerMinX) if (x < menuContainerMaxX && x >= menuContainerMinX)
@@ -494,8 +379,6 @@ var STICKY_TIME_INTERVAL = 0.4,
var count = _menuContainerStack.length, var count = _menuContainerStack.length,
index = count; index = count;
[newMenu _menuWillOpen];
// Hide all menus up to the base menu... // Hide all menus up to the base menu...
while (index--) while (index--)
{ {
@@ -507,7 +390,7 @@ var STICKY_TIME_INTERVAL = 0.4,
break; break;
// If this menu is already being shown, unhighlight and return. // If this menu is already being shown, unhighlight and return.
if (menu === newMenu) //&& [menu supermenu] === baseMenu) if (menu === newMenu)//&& [menu supermenu] === baseMenu)
return [newMenu _highlightItemAtIndex:CPNotFound]; return [newMenu _highlightItemAtIndex:CPNotFound];
[menuContainer orderOut:self]; [menuContainer orderOut:self];
@@ -515,8 +398,6 @@ var STICKY_TIME_INTERVAL = 0.4,
[_CPMenuWindow poolMenuWindow:menuContainer]; [_CPMenuWindow poolMenuWindow:menuContainer];
[_menuContainerStack removeObjectAtIndex:index]; [_menuContainerStack removeObjectAtIndex:index];
[menu _menuDidClose];
} }
if (!newMenu) if (!newMenu)
@@ -553,7 +434,7 @@ var STICKY_TIME_INTERVAL = 0.4,
var iter = [selectorNames objectEnumerator], var iter = [selectorNames objectEnumerator],
obj; obj;
while ((obj = [iter nextObject]) !== nil) while (obj = [iter nextObject])
{ {
var aSelector = CPSelectorFromString(obj); var aSelector = CPSelectorFromString(obj);
@@ -563,12 +444,9 @@ var STICKY_TIME_INTERVAL = 0.4,
} }
else if (!(modifierFlags & (CPCommandKeyMask | CPControlKeyMask))) else if (!(modifierFlags & (CPCommandKeyMask | CPControlKeyMask)))
{ {
if (([anEvent timestamp] - _startTime) > STICKY_TIME_INTERVAL)
_keyBuffer = nil;
if (!_keyBuffer) if (!_keyBuffer)
{ {
_startTime = [anEvent timestamp]; _startTime = [CPDate date];
_keyBuffer = character; _keyBuffer = character;
[CPEvent stopPeriodicEvents]; [CPEvent stopPeriodicEvents];
@@ -577,17 +455,17 @@ var STICKY_TIME_INTERVAL = 0.4,
else else
_keyBuffer += character; _keyBuffer += character;
[self selectNextItemBeginningWith:_keyBuffer inMenu:menu]; [self selectNextItemBeginningWith:_keyBuffer inMenu:menu clearBuffer:NO];
_lastGlobalLocation = nil; _lastGlobalLocation = Nil;
} }
} }
- (void)selectNextItemBeginningWith:(CPString)characters inMenu:(CPMenu)menu - (void)selectNextItemBeginningWith:(CPString)characters inMenu:(CPMenu)menu clearBuffer:(BOOL)shouldClear
{ {
var iter = [[menu itemArray] objectEnumerator], var iter = [[menu itemArray] objectEnumerator],
obj; obj;
while ((obj = [iter nextObject]) !== nil) while (obj = [iter nextObject])
{ {
if ([obj isHidden] || ![obj isEnabled]) if ([obj isHidden] || ![obj isEnabled])
continue; continue;
@@ -599,7 +477,13 @@ var STICKY_TIME_INTERVAL = 0.4,
} }
} }
_startTime = [CPEvent currentTimestamp]; if (shouldClear)
{
[CPEvent stopPeriodicEvents];
_keyBuffer = Nil;
}
else
_startTime = [CPDate date];
} }
- (void)scrollToBeginningOfDocument:(CPMenu)menu - (void)scrollToBeginningOfDocument:(CPMenu)menu
@@ -627,7 +511,7 @@ var STICKY_TIME_INTERVAL = 0.4,
return; return;
} }
var next = current + (last - first); next = current + (last - first);
if (next < [menu numberOfItems]) if (next < [menu numberOfItems])
[menu _highlightItemAtIndex:next]; [menu _highlightItemAtIndex:next];
@@ -655,7 +539,7 @@ var STICKY_TIME_INTERVAL = 0.4,
return; return;
} }
var next = current - (last - first); next = current - (last - first);
if (next < 0) if (next < 0)
[menu _highlightItemAtIndex:0]; [menu _highlightItemAtIndex:0];
@@ -736,8 +620,6 @@ var STICKY_TIME_INTERVAL = 0.4,
if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled]) if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled])
[self moveDown:menu]; [self moveDown:menu];
} }
else if (menu == [CPApp mainMenu])
[menu _highlightItemAtIndex:0];
} }
- (void)moveUp:(CPMenu)menu - (void)moveUp:(CPMenu)menu
@@ -745,12 +627,7 @@ var STICKY_TIME_INTERVAL = 0.4,
var index = menu._highlightedIndex - 1; var index = menu._highlightedIndex - 1;
if (index < 0) if (index < 0)
{
if (index != CPNotFound || menu == [CPApp mainMenu])
[menu _highlightItemAtIndex:[menu numberOfItems] - 1];
return; return;
}
[menu _highlightItemAtIndex:index]; [menu _highlightItemAtIndex:index];
+83 -105
View File
@@ -1,31 +1,5 @@
/*
* _CPMenuWindow.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPClipView.j"
@import "CPImageView.j"
@import "CPWindow.j" @import "CPWindow.j"
@import "_CPMenuManager.j"
@class _CPMenuView
var _CPMenuWindowPool = [], var _CPMenuWindowPool = [],
_CPMenuWindowPoolCapacity = 5, _CPMenuWindowPoolCapacity = 5,
@@ -36,6 +10,15 @@ _CPMenuWindowMenuBarBackgroundStyle = 0;
_CPMenuWindowPopUpBackgroundStyle = 1; _CPMenuWindowPopUpBackgroundStyle = 1;
_CPMenuWindowAttachedMenuBackgroundStyle = 2; _CPMenuWindowAttachedMenuBackgroundStyle = 2;
var STICKY_TIME_INTERVAL = 500,
TOP_MARGIN = 5.0,
LEFT_MARGIN = 1.0,
RIGHT_MARGIN = 1.0,
BOTTOM_MARGIN = 5.0,
SCROLL_INDICATOR_HEIGHT = 16.0;
/* /*
@ignore @ignore
*/ */
@@ -74,24 +57,32 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
+ (void)poolMenuWindow:(_CPMenuWindow)aMenuWindow + (void)poolMenuWindow:(_CPMenuWindow)aMenuWindow
{ {
// FIXME :the poolMenuWindow is called too many times somewhere.... if (!aMenuWindow || _CPMenuWindowPool.length >= _CPMenuWindowPoolCapacity)
if (!aMenuWindow || _CPMenuWindowPool.length >= _CPMenuWindowPoolCapacity || [_CPMenuWindowPool containsObject:aMenuWindow])
return; return;
_CPMenuWindowPool.push(aMenuWindow); _CPMenuWindowPool.push(aMenuWindow);
} }
+ (void)initialize
{
if (self != [_CPMenuWindow class])
return;
var bundle = [CPBundle bundleForClass:self];
_CPMenuWindowMoreAboveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreAbove.png"] size:CGSizeMake(38.0, 18.0)];
_CPMenuWindowMoreBelowImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreBelow.png"] size:CGSizeMake(38.0, 18.0)];
}
- (id)initWithContentRect:(CGRect)aRect styleMask:(unsigned)aStyleMask - (id)initWithContentRect:(CGRect)aRect styleMask:(unsigned)aStyleMask
{ {
_constraintRect = CGRectMakeZero(); _constraintRect = _CGRectMakeZero();
_unconstrainedFrame = CGRectMakeZero(); _unconstrainedFrame = _CGRectMakeZero();
self = [super initWithContentRect:aRect styleMask:CPBorderlessWindowMask]; self = [super initWithContentRect:aRect styleMask:CPBorderlessWindowMask];
if (self) if (self)
{ {
_constrainsToUsableScreen = NO;
[self setLevel:CPPopUpMenuWindowLevel]; [self setLevel:CPPopUpMenuWindowLevel];
[self setHasShadow:YES]; [self setHasShadow:YES];
[self setShadowStyle:CPMenuWindowShadowStyle]; [self setShadowStyle:CPMenuWindowShadowStyle];
@@ -101,24 +92,26 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
_menuView = [[_CPMenuView alloc] initWithFrame:CGRectMakeZero()]; _menuView = [[_CPMenuView alloc] initWithFrame:CGRectMakeZero()];
_menuClipView = [[CPClipView alloc] initWithFrame:CGRectMake([_menuView valueForThemeAttribute:@"menu-window-margin-inset"].left, [_menuView valueForThemeAttribute:@"menu-window-margin-inset"].top, 0.0, 0.0)]; _menuClipView = [[CPClipView alloc] initWithFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, 0.0, 0.0)];
[_menuClipView setDocumentView:_menuView]; [_menuClipView setDocumentView:_menuView];
[contentView addSubview:_menuClipView]; [contentView addSubview:_menuClipView];
_moreAboveView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; _moreAboveView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
[_moreAboveView setImage:[_menuView valueForThemeAttribute:@"menu-window-more-above-image"]]; [_moreAboveView setImage:_CPMenuWindowMoreAboveImage];
[_moreAboveView setFrameSize:[[_menuView valueForThemeAttribute:@"menu-window-more-above-image"] size]]; [_moreAboveView setFrameSize:[_CPMenuWindowMoreAboveImage size]];
[contentView addSubview:_moreAboveView]; [contentView addSubview:_moreAboveView];
_moreBelowView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; _moreBelowView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
[_moreBelowView setImage:[_menuView valueForThemeAttribute:@"menu-window-more-below-image"]]; [_moreBelowView setImage:_CPMenuWindowMoreBelowImage];
[_moreBelowView setFrameSize:[[_menuView valueForThemeAttribute:@"menu-window-more-below-image"] size]]; [_moreBelowView setFrameSize:[_CPMenuWindowMoreBelowImage size]];
[contentView addSubview:_moreBelowView]; [contentView addSubview:_moreBelowView];
[self setShadowStyle:CPWindowShadowStyleMenu];
} }
return self; return self;
@@ -126,7 +119,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
+ (float)_standardLeftMargin + (float)_standardLeftMargin
{ {
return [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-window-margin-inset" forClass:_CPMenuView].left; return LEFT_MARGIN;
} }
- (void)setFont:(CPFont)aFont - (void)setFont:(CPFont)aFont
@@ -148,10 +141,36 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
var bundle = [CPBundle bundleForClass:[self class]]; var bundle = [CPBundle bundleForClass:[self class]];
if (aBackgroundStyle == _CPMenuWindowPopUpBackgroundStyle) if (aBackgroundStyle == _CPMenuWindowPopUpBackgroundStyle)
color = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-window-pop-up-background-style-color" forClass:_CPMenuView]; color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
[
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded0.png"] size:CGSizeMake(4.0, 4.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow1.png"] size:CGSizeMake(1.0, 4.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded2.png"] size:CGSizeMake(4.0, 4.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)]
]]];
else if (aBackgroundStyle == _CPMenuWindowMenuBarBackgroundStyle) else if (aBackgroundStyle == _CPMenuWindowMenuBarBackgroundStyle)
color = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-window-menu-bar-background-style-color" forClass:_CPMenuView]; color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
[
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 0.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 0.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 0.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)]
]]];
_CPMenuWindowBackgroundColors[aBackgroundStyle] = color; _CPMenuWindowBackgroundColors[aBackgroundStyle] = color;
} }
@@ -169,13 +188,12 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
[aMenu _setMenuWindow:self]; [aMenu _setMenuWindow:self];
[_menuView setMenu:aMenu]; [_menuView setMenu:aMenu];
var menuViewSize = [_menuView frame].size, var menuViewSize = [_menuView frame].size;
marginInset = [_menuView valueForThemeAttribute:@"menu-window-margin-inset"];
[self setFrameSize:CGSizeMake(marginInset.left + menuViewSize.width + marginInset.right, marginInset.top + menuViewSize.height + marginInset.bottom)]; [self setFrameSize:CGSizeMake(LEFT_MARGIN + menuViewSize.width + RIGHT_MARGIN, TOP_MARGIN + menuViewSize.height + BOTTOM_MARGIN)];
[_menuView scrollPoint:CGPointMake(0.0, 0.0)]; [_menuView scrollPoint:CGPointMake(0.0, 0.0)];
[_menuClipView setFrame:CGRectMake(marginInset.left, marginInset.top, menuViewSize.width, menuViewSize.height)]; [_menuClipView setFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, menuViewSize.width, menuViewSize.height)];
} }
- (void)setMinWidth:(float)aWidth - (void)setMinWidth:(float)aWidth
@@ -212,30 +230,28 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
- (CGRect)unconstrainedFrame - (CGRect)unconstrainedFrame
{ {
return CGRectMakeCopy(_unconstrainedFrame); return _CGRectMakeCopy(_unconstrainedFrame);
} }
// We need this because if not this will call setFrame: with -frame instead of -unconstrainedFrame, turning // We need this because if not this will call setFrame: with -frame instead of -unconstrainedFrame, turning
// the constrained frame into the unconstrained frame. // the constrained frame into the unconstrained frame.
- (void)setFrameOrigin:(CGPoint)aPoint - (void)setFrameOrigin:(CGPoint)aPoint
{ {
[super setFrame:CGRectMake(aPoint.x, aPoint.y, CGRectGetWidth(_unconstrainedFrame), CGRectGetHeight(_unconstrainedFrame))]; [super setFrame:_CGRectMake(aPoint.x, aPoint.y, _CGRectGetWidth(_unconstrainedFrame), _CGRectGetHeight(_unconstrainedFrame))];
} }
- (void)setFrameSize:(CGSize)aSize - (void)setFrameSize:(CGSize)aSize
{ {
[super setFrame:CGRectMake(CGRectGetMinX(_unconstrainedFrame), CGRectGetMinY(_unconstrainedFrame), aSize.width, aSize.height)]; [super setFrame:_CGRectMake(_CGRectGetMinX(_unconstrainedFrame), _CGRectGetMinY(_unconstrainedFrame), aSize.width, aSize.height)];
} }
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate - (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
{ {
// FIXME: There are integral window issues with platform windows. // FIXME: There are integral window issues with platform windows.
// FIXME: This gets called far too often. // FIXME: This gets called far too often.
_unconstrainedFrame = CGRectMakeCopy(aFrame); _unconstrainedFrame = _CGRectMakeCopy(aFrame);
var constrainedFrame = CGRectIntersection(_unconstrainedFrame, _constraintRect), var constrainedFrame = CGRectIntersection(_unconstrainedFrame, _constraintRect);
marginInset = [_menuView valueForThemeAttribute:@"menu-window-margin-inset"],
scrollIndicatorHeight = [_menuView valueForThemeAttribute:@"menu-window-scroll-indicator-height"];
// We don't want to simply intersect the visible frame and the unconstrained frame. // We don't want to simply intersect the visible frame and the unconstrained frame.
// We should be allowing as much of the width to fit as possible (pushing back and forward). // We should be allowing as much of the width to fit as possible (pushing back and forward).
@@ -254,41 +270,41 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
[super setFrame:constrainedFrame display:shouldDisplay animate:shouldAnimate]; [super setFrame:constrainedFrame display:shouldDisplay animate:shouldAnimate];
// This needs to happen before changing the frame. // This needs to happen before changing the frame.
var menuViewOrigin = CGPointMake(CGRectGetMinX(aFrame) + marginInset.left, CGRectGetMinY(aFrame) + marginInset.top), var menuViewOrigin = CGPointMake(CGRectGetMinX(aFrame) + LEFT_MARGIN, CGRectGetMinY(aFrame) + TOP_MARGIN),
moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + marginInset.top, moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + TOP_MARGIN,
moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - marginInset.bottom, moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - BOTTOM_MARGIN,
topMargin = marginInset.top, topMargin = TOP_MARGIN,
bottomMargin = marginInset.bottom, bottomMargin = BOTTOM_MARGIN,
contentView = [self contentView], contentView = [self contentView],
bounds = [contentView bounds]; bounds = [contentView bounds];
if (moreAbove) if (moreAbove)
{ {
topMargin += scrollIndicatorHeight; topMargin += SCROLL_INDICATOR_HEIGHT;
var frame = [_moreAboveView frame]; var frame = [_moreAboveView frame];
[_moreAboveView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(frame)) / 2.0, (marginInset.top + scrollIndicatorHeight - CGRectGetHeight(frame)) / 2.0)]; [_moreAboveView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(frame)) / 2.0, (TOP_MARGIN + SCROLL_INDICATOR_HEIGHT - CGRectGetHeight(frame)) / 2.0)];
} }
[_moreAboveView setHidden:!moreAbove]; [_moreAboveView setHidden:!moreAbove];
if (moreBelow) if (moreBelow)
{ {
bottomMargin += scrollIndicatorHeight; bottomMargin += SCROLL_INDICATOR_HEIGHT;
[_moreBelowView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth([_moreBelowView frame])) / 2.0, CGRectGetHeight(bounds) - scrollIndicatorHeight - marginInset.bottom)]; [_moreBelowView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth([_moreBelowView frame])) / 2.0, CGRectGetHeight(bounds) - SCROLL_INDICATOR_HEIGHT - BOTTOM_MARGIN)];
} }
[_moreBelowView setHidden:!moreBelow]; [_moreBelowView setHidden:!moreBelow];
var clipFrame = CGRectMakeZero(); var clipFrame = CGRectMakeZero();
clipFrame.origin.x = marginInset.left; clipFrame.origin.x = LEFT_MARGIN;
clipFrame.origin.y = topMargin; clipFrame.origin.y = topMargin;
clipFrame.size.width = CGRectGetWidth(constrainedFrame) - marginInset.left - marginInset.right; clipFrame.size.width = CGRectGetWidth(constrainedFrame) - LEFT_MARGIN - RIGHT_MARGIN;
clipFrame.size.height = CGRectGetHeight(constrainedFrame) - topMargin - bottomMargin; clipFrame.size.height = CGRectGetHeight(constrainedFrame) - topMargin - bottomMargin;
[_menuClipView setFrame:clipFrame]; [_menuClipView setFrame:clipFrame];
@@ -384,15 +400,15 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation - (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
{ {
var frame = [self frame]; var frame = [self frame];
if (!CGRectContainsPoint(frame,aGlobalLocation) || ![self canScroll]) if (!CPRectContainsPoint(frame,aGlobalLocation) || ![self canScroll])
return _CPMenuManagerScrollingStateNone; return _CPMenuManagerScrollingStateNone;
// If we're at or above of the top scroll indicator... // If we're at or above of the top scroll indicator...
if (aGlobalLocation.y < CGRectGetMinY(frame) + [_menuView valueForThemeAttribute:@"menu-window-margin-inset"].top + [_menuView valueForThemeAttribute:@"menu-window-scroll-indicator-height"] && ![_moreAboveView isHidden]) if (aGlobalLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT && ![_moreAboveView isHidden])
return _CPMenuManagerScrollingStateUp; return _CPMenuManagerScrollingStateUp;
// If we're at or below the bottom scroll indicator... // If we're at or below the bottom scroll indicator...
if (aGlobalLocation.y > CGRectGetMaxY(frame) - [_menuView valueForThemeAttribute:@"menu-window-margin-inset"].bottom - [_menuView valueForThemeAttribute:@"menu-window-scroll-indicator-height"] && ![_moreBelowView isHidden]) if (aGlobalLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT && ![_moreBelowView isHidden])
return _CPMenuManagerScrollingStateDown; return _CPMenuManagerScrollingStateDown;
return _CPMenuManagerScrollingStateNone; return _CPMenuManagerScrollingStateNone;
@@ -400,7 +416,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
- (float)deltaYForItemAtIndex:(int)anIndex - (float)deltaYForItemAtIndex:(int)anIndex
{ {
return [_menuView valueForThemeAttribute:@"menu-window-margin-inset"].top + CGRectGetMinY([_menuView rectForItemAtIndex:anIndex]); return TOP_MARGIN + CGRectGetMinY([_menuView rectForItemAtIndex:anIndex]);
} }
- (CGPoint)rectForItemAtIndex:(int)anIndex - (CGPoint)rectForItemAtIndex:(int)anIndex
@@ -412,7 +428,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
{ {
// Don't return indexes of items that aren't visible. // Don't return indexes of items that aren't visible.
if (!CGRectContainsPoint([_menuClipView bounds], [_menuClipView convertPoint:aPoint fromView:nil])) if (!CGRectContainsPoint([_menuClipView bounds], [_menuClipView convertPoint:aPoint fromView:nil]))
return CPNotFound; return NO;
return [_menuView itemIndexAtPoint:[_menuView convertPoint:aPoint fromView:nil]]; return [_menuView itemIndexAtPoint:[_menuView convertPoint:aPoint fromView:nil]];
} }
@@ -431,44 +447,6 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
CPFont _font @accessors(property=font); CPFont _font @accessors(property=font);
} }
+ (CPString)defaultThemeClass
{
return "menu-view";
}
+ (CPDictionary)themeAttributes
{
return @{
@"menu-window-more-above-image": [CPNull null],
@"menu-window-more-below-image": [CPNull null],
@"menu-window-pop-up-background-style-color": [CPNull null],
@"menu-window-menu-bar-background-style-color": [CPNull null],
@"menu-window-margin-inset": CGInsetMake(5.0, 1.0, 1.0, 5.0),
@"menu-window-scroll-indicator-height": 16.0,
@"menu-bar-window-background-color": [CPNull null],
@"menu-bar-window-background-selected-color": [CPNull null],
@"menu-bar-window-font": [CPNull null],
@"menu-bar-window-height": 30.0,
@"menu-bar-window-margin": 10.0,
@"menu-bar-window-left-margin": 10.0,
@"menu-bar-window-right-margin": 10.0,
@"menu-bar-text-color": [CPNull null],
@"menu-bar-title-color": [CPNull null],
@"menu-bar-text-shadow-color": [CPNull null],
@"menu-bar-title-shadow-color": [CPNull null],
@"menu-bar-highlight-color": [CPNull null],
@"menu-bar-highlight-text-color": [CPNull null],
@"menu-bar-highlight-text-shadow-color": [CPNull null],
@"menu-bar-height": 28.0,
@"menu-bar-icon-image": [CPNull null],
@"menu-bar-icon-image-alpha-value": 1.0,
@"menu-general-icon-new": [CPNull null],
@"menu-general-icon-save": [CPNull null],
@"menu-general-icon-open": [CPNull null],
};
}
- (unsigned)numberOfUnhiddenItems - (unsigned)numberOfUnhiddenItems
{ {
return _visibleMenuItemInfos.length; return _visibleMenuItemInfos.length;
+41 -50
View File
@@ -25,33 +25,29 @@
@import <Foundation/CPString.j> @import <Foundation/CPString.j>
@import "CPImage.j" @import "CPImage.j"
@import "CPText.j" @import "CPMenu.j"
@import "CPView.j" @import "CPView.j"
@import "_CPMenuItemView.j" @import "_CPMenuItemView.j"
@class CPMenu
@global CPApp var CPMenuItemStringRepresentationDictionary = [CPDictionary dictionary];
[CPMenuItemStringRepresentationDictionary setObject:"\u238B" forKey:CPEscapeFunctionKey];
var CPMenuItemStringRepresentationDictionary = @{ [CPMenuItemStringRepresentationDictionary setObject:"\u21E5" forKey:CPTabCharacter];
CPEscapeFunctionKey: "\u238B", [CPMenuItemStringRepresentationDictionary setObject:"\u21E4" forKey:CPBackTabCharacter];
CPTabCharacter: "\u21E5", [CPMenuItemStringRepresentationDictionary setObject:"\u2423" forKey:CPSpaceFunctionKey];
CPBackTabCharacter: "\u21E4", [CPMenuItemStringRepresentationDictionary setObject:"\u23CE" forKey:CPCarriageReturnCharacter];
CPSpaceFunctionKey: "\u2423", [CPMenuItemStringRepresentationDictionary setObject:"\u232B" forKey:CPBackspaceCharacter];
CPCarriageReturnCharacter: "\u23CE", [CPMenuItemStringRepresentationDictionary setObject:"\u232B" forKey:CPDeleteFunctionKey];
CPBackspaceCharacter: "\u232B", [CPMenuItemStringRepresentationDictionary setObject:"\u2326" forKey:CPDeleteCharacter];
CPDeleteFunctionKey: "\u232B", [CPMenuItemStringRepresentationDictionary setObject:"\u21F1" forKey:CPHomeFunctionKey];
CPDeleteCharacter: "\u2326", [CPMenuItemStringRepresentationDictionary setObject:"\u21F2" forKey:CPEndFunctionKey];
CPHomeFunctionKey: "\u21F1", [CPMenuItemStringRepresentationDictionary setObject:"\u21DE" forKey:CPPageUpFunctionKey];
CPEndFunctionKey: "\u21F2", [CPMenuItemStringRepresentationDictionary setObject:"\u21DF" forKey:CPPageDownFunctionKey];
CPPageUpFunctionKey: "\u21DE", [CPMenuItemStringRepresentationDictionary setObject:"\u2191" forKey:CPUpArrowFunctionKey];
CPPageDownFunctionKey: "\u21DF", [CPMenuItemStringRepresentationDictionary setObject:"\u2193" forKey:CPDownArrowFunctionKey];
CPUpArrowFunctionKey: "\u2191", [CPMenuItemStringRepresentationDictionary setObject:"\u2190" forKey:CPLeftArrowFunctionKey];
CPDownArrowFunctionKey: "\u2193", [CPMenuItemStringRepresentationDictionary setObject:"\u2192" forKey:CPRightArrowFunctionKey];
CPLeftArrowFunctionKey: "\u2190", [CPMenuItemStringRepresentationDictionary setObject:"\u2327" forKey:CPClearDisplayFunctionKey];
CPRightArrowFunctionKey: "\u2192",
CPClearDisplayFunctionKey: "\u2327",
};
/*! /*!
@ingroup appkit @ingroup appkit
@@ -105,16 +101,6 @@ var CPMenuItemStringRepresentationDictionary = @{
_CPMenuItemView _menuItemView; _CPMenuItemView _menuItemView;
} }
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if ([aBinding hasPrefix:CPEnabledBinding])
return [CPMultipleValueAndBinding class];
else if (aBinding === CPTargetBinding || [aBinding hasPrefix:CPArgumentBinding])
return [CPActionBinding class];
return [super _binderClassForBinding:aBinding];
}
- (id)init - (id)init
{ {
return [self initWithTitle:@"" action:nil keyEquivalent:nil]; return [self initWithTitle:@"" action:nil keyEquivalent:nil];
@@ -163,11 +149,16 @@ var CPMenuItemStringRepresentationDictionary = @{
*/ */
- (void)setEnabled:(BOOL)isEnabled - (void)setEnabled:(BOOL)isEnabled
{ {
if (_isEnabled === isEnabled) if ([_menu autoenablesItems])
return; return;
if (!isEnabled && [self isHighlighted]) [self _setEnabled:isEnabled];
[_menu _highlightItemAtIndex:CPNotFound]; }
- (void)_setEnabled:(BOOL)isEnabled
{
if (_isEnabled === isEnabled)
return;
_isEnabled = !!isEnabled; _isEnabled = !!isEnabled;
@@ -298,7 +289,7 @@ var CPMenuItemStringRepresentationDictionary = @{
*/ */
- (void)setFont:(CPFont)aFont - (void)setFont:(CPFont)aFont
{ {
if ([_font isEqual:aFont]) if (_font == aFont)
return; return;
_font = aFont; _font = aFont;
@@ -505,7 +496,6 @@ CPOffState
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:)];
@@ -843,7 +833,7 @@ CPControlKeyMask
[item setTarget:_target]; [item setTarget:_target];
[item setAction:_action]; [item setAction:_action];
[item setEnabled:_isEnabled]; [item setEnabled:_isEnabled];
[item setHidden:_isHidden]; [item setHidden:_isHidden]
[item setTag:_tag]; [item setTag:_tag];
[item setState:_state]; [item setState:_state];
[item setImage:_image]; [item setImage:_image];
@@ -949,33 +939,34 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
_action = [aCoder decodeObjectForKey:CPMenuItemActionKey]; _action = [aCoder decodeObjectForKey:CPMenuItemActionKey];
_isEnabled = DEFAULT_VALUE(CPMenuItemIsEnabledKey, YES); _isEnabled = DEFAULT_VALUE(CPMenuItemIsEnabledKey, YES);
_isHidden = [aCoder decodeBoolForKey:CPMenuItemIsHiddenKey]; _isHidden = DEFAULT_VALUE(CPMenuItemIsHiddenKey, NO);
_tag = [aCoder decodeIntForKey:CPMenuItemTagKey]; _tag = DEFAULT_VALUE(CPMenuItemTagKey, 0);
_state = [aCoder decodeIntForKey:CPMenuItemStateKey]; _state = DEFAULT_VALUE(CPMenuItemStateKey, CPOffState);
_image = [aCoder decodeObjectForKey:CPMenuItemImageKey]; _image = DEFAULT_VALUE(CPMenuItemImageKey, nil);
_alternateImage = [aCoder decodeObjectForKey:CPMenuItemAlternateImageKey]; _alternateImage = DEFAULT_VALUE(CPMenuItemAlternateImageKey, nil);
// CPImage _onStateImage; // CPImage _onStateImage;
// CPImage _offStateImage; // CPImage _offStateImage;
// CPImage _mixedStateImage; // CPImage _mixedStateImage;
// This order matters because setSubmenu: needs _menu to be around. // This order matters because setSubmenu: needs _menu to be around.
_menu = [aCoder decodeObjectForKey:CPMenuItemMenuKey]; _menu = DEFAULT_VALUE(CPMenuItemMenuKey, nil);
[self setSubmenu:[aCoder decodeObjectForKey:CPMenuItemSubmenuKey]]; [self setSubmenu:DEFAULT_VALUE(CPMenuItemSubmenuKey, nil)];
_keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @""; _keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @"";
_keyEquivalentModifierMask = [aCoder decodeIntForKey:CPMenuItemKeyEquivalentModifierMaskKey]; _keyEquivalentModifierMask = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentModifierMaskKey] || 0;
// int _mnemonicLocation; // int _mnemonicLocation;
// BOOL _isAlternate; // BOOL _isAlternate;
[self setIndentationLevel:[aCoder decodeIntForKey:CPMenuItemIndentationLevelKey]]; // Default is 0.
[self setIndentationLevel:[aCoder decodeIntForKey:CPMenuItemIndentationLevelKey] || 0];
// CPString _toolTip; // CPString _toolTip;
_representedObject = [aCoder decodeObjectForKey:CPMenuItemRepresentedObjectKey]; _representedObject = DEFAULT_VALUE(CPMenuItemRepresentedObjectKey, nil);
_view = [aCoder decodeObjectForKey:CPMenuItemViewKey]; _view = DEFAULT_VALUE(CPMenuItemViewKey, nil);
} }
return self; return self;
+140 -108
View File
@@ -1,62 +1,54 @@
/* @import "CPView.j"
* _CPMenuItemMenuBarView.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPControl.j"
@import "_CPImageAndTextView.j"
@class _CPMenuBarWindow var HORIZONTAL_MARGIN = 8.0,
@class _CPMenuView SUBMENU_INDICATOR_MARGIN = 3.0,
@class CPMenuItem VERTICAL_MARGIN = 4.0;
var SelectionColor = nil,
SUBMENU_INDICATOR_COLOR = nil,
_CPMenuItemSelectionColor = nil,
_CPMenuItemTextShadowColor = nil,
_CPMenuItemDefaultStateImages = [],
_CPMenuItemDefaultStateHighlightedImages = [];
@implementation _CPMenuItemMenuBarView : CPView @implementation _CPMenuItemMenuBarView : CPView
{ {
CPColor _highlightColor @accessors(property=highlightColor);
CPColor _textColor @accessors(property=textColor);
CPColor _textShadowColor @accessors(property=textShadowColor);
CPColor _highlightTextColor @accessors(property=highlightTextColor);
CPColor _highlightTextShadowColor @accessors(property=highlightTextShadowColor);
CPMenuItem _menuItem @accessors(property=menuItem); CPMenuItem _menuItem @accessors(property=menuItem);
CPFont _font; CPFont _font;
CPColor _textColor;
CPColor _textShadowColor;
BOOL _isDirty; BOOL _isDirty;
BOOL _shouldHighlight;
_CPImageAndTextView _imageAndTextView; _CPImageAndTextView _imageAndTextView;
CPView _submenuIndicatorView;
} }
+ (CPString)defaultThemeClass + (void)initialize
{ {
return "menu-item-bar-view"; if (self !== [_CPMenuItemMenuBarView class])
} return;
+ (CPDictionary)themeAttributes var bundle = [CPBundle bundleForClass:self];
{
return @{ SelectionColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackgroundSelected.png"] size:CGSizeMake(1.0, 28.0)]];
@"horizontal-margin": 9.0,
@"submenu-indicator-margin": 3.0, SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
@"vertical-margin": 4.0,
}; _CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
_CPMenuItemDefaultStateImages[CPOffState] = nil;
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
} }
+ (id)view + (id)view
@@ -70,7 +62,7 @@
if (self) if (self)
{ {
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake([self valueForThemeAttribute:@"horizontal-margin"], 0.0, 0.0, 0.0)]; _imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(HORIZONTAL_MARGIN, 0.0, 0.0, 0.0)];
[_imageAndTextView setImagePosition:CPImageLeft]; [_imageAndTextView setImagePosition:CPImageLeft];
[_imageAndTextView setImageOffset:3.0]; [_imageAndTextView setImageOffset:3.0];
@@ -79,98 +71,88 @@
[self addSubview:_imageAndTextView]; [self addSubview:_imageAndTextView];
_submenuIndicatorView = [[_CPMenuItemMenuBarSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 9.0, 6.0)];
[_submenuIndicatorView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
[self addSubview:_submenuIndicatorView];
[self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; [self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
} }
return self; return self;
} }
- (void)setTextColor:(CPColor)aColor
{
_textColor = aColor;
[self setNeedsLayout];
}
- (void)setTextShadowColor:(CPColor)aColor
{
_textShadowColor = aColor;
[self setNeedsLayout];
}
- (void)setHighlightTextColor:(CPColor)aColor
{
_highlightTextColor = aColor;
[self setNeedsLayout];
}
- (void)setHighlightTextShadowColor:(CPColor)aColor
{
_highlightTextShadowColor = aColor;
[self setNeedsLayout];
}
- (CPColor)textColor - (CPColor)textColor
{ {
if (![_menuItem isEnabled]) if (![_menuItem isEnabled])
return [CPColor lightGrayColor]; return [CPColor lightGrayColor];
return _textColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-color" forClass:_CPMenuView]; return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
} }
- (CPColor)textShadowColor - (CPColor)textShadowColor
{ {
if (![_menuItem isEnabled]) if (![_menuItem isEnabled])
return [CPColor clearColor]; return [CPColor colorWithWhite:0.8 alpha:0.8];
return _textShadowColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-text-shadow-color" forClass:_CPMenuView]; return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
}
- (CPColor)highlightTextColor
{
if (![_menuItem isEnabled])
return [CPColor lightGrayColor];
return _highlightTextColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-color" forClass:_CPMenuView];
}
- (CPColor)highlightTextShadowColor
{
if (![_menuItem isEnabled])
return [CPColor clearColor];
return _highlightTextShadowColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-highlight-text-shadow-color" forClass:_CPMenuView];
}
- (CPColor)highlightColor
{
return _highlightColor || [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-background-selected-color" forClass:_CPMenuView];
} }
- (void)update - (void)update
{ {
var x = [self valueForThemeAttribute:@"horizontal-margin"], var x = HORIZONTAL_MARGIN,
height = 0.0; height = 0.0;
[_imageAndTextView setText:[_menuItem title]];
[_imageAndTextView setFont:[_menuItem font] || [_CPMenuBarWindow font]]; [_imageAndTextView setFont:[_menuItem font] || [_CPMenuBarWindow font]];
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment]; [_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
[_imageAndTextView setImage:[_menuItem image]]; [_imageAndTextView setImage:[_menuItem image]];
[_imageAndTextView setText:[_menuItem title]];
[_imageAndTextView setTextColor:[self textColor]];
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
[_imageAndTextView sizeToFit]; [_imageAndTextView sizeToFit];
var imageAndTextViewFrame = [_imageAndTextView frame]; var imageAndTextViewFrame = [_imageAndTextView frame];
imageAndTextViewFrame.origin.x = x; imageAndTextViewFrame.origin.x = x;
x += CGRectGetWidth(imageAndTextViewFrame); x += CGRectGetWidth(imageAndTextViewFrame);
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame)) + 2.0 * [self valueForThemeAttribute:@"vertical-margin"]; height = MAX(height, CGRectGetHeight(imageAndTextViewFrame));
var hasSubmenuIndicator = [_menuItem hasSubmenu] && [_menuItem action];
if (hasSubmenuIndicator)
{
[_submenuIndicatorView setHidden:NO];
[_submenuIndicatorView setColor:[self textColor]];
[_submenuIndicatorView setShadowColor:[self textShadowColor]];
var submenuViewFrame = [_submenuIndicatorView frame];
submenuViewFrame.origin.x = x + SUBMENU_INDICATOR_MARGIN;
x = CGRectGetMaxX(submenuViewFrame);
height = MAX(height, CGRectGetHeight(submenuViewFrame));
}
else
[_submenuIndicatorView setHidden:YES];
height += 2.0 * VERTICAL_MARGIN;
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0); imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
[_imageAndTextView setFrame:imageAndTextViewFrame]; [_imageAndTextView setFrame:imageAndTextViewFrame];
if (hasSubmenuIndicator)
{
submenuViewFrame.origin.y = FLOOR((height - CGRectGetHeight(submenuViewFrame)) / 2.0) + 1.0;
[_submenuIndicatorView setFrame:submenuViewFrame];
}
_minSize = CGSizeMake(x + HORIZONTAL_MARGIN, height);
[self setAutoresizesSubviews:NO]; [self setAutoresizesSubviews:NO];
[self setFrameSize:CGSizeMake(x + [self valueForThemeAttribute:@"horizontal-margin"], height)]; [self setFrameSize:_minSize];
[self setAutoresizesSubviews:YES]; [self setAutoresizesSubviews:YES];
[self setNeedsLayout];
} }
- (void)highlight:(BOOL)shouldHighlight - (void)highlight:(BOOL)shouldHighlight
@@ -179,20 +161,17 @@
if (![_menuItem isEnabled]) if (![_menuItem isEnabled])
shouldHighlight = NO; shouldHighlight = NO;
_shouldHighlight = shouldHighlight; if (shouldHighlight)
[self setNeedsLayout];
}
- (void)layoutSubviews
{
if (_shouldHighlight)
{ {
if (![_menuItem _isMenuBarButton]) if (![_menuItem _isMenuBarButton])
[self setBackgroundColor:[self highlightColor]]; [self setBackgroundColor:SelectionColor];
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]]; [_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
[_imageAndTextView setTextColor:[self highlightTextColor]]; [_imageAndTextView setTextColor:[CPColor whiteColor]];
[_imageAndTextView setTextShadowColor:[self highlightTextShadowColor]]; [_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
[_submenuIndicatorView setColor:[CPColor whiteColor]];
[_submenuIndicatorView setShadowColor:[CPColor colorWithWhite:0.1 alpha:0.7]];
} }
else else
{ {
@@ -201,7 +180,60 @@
[_imageAndTextView setImage:[_menuItem image]]; [_imageAndTextView setImage:[_menuItem image]];
[_imageAndTextView setTextColor:[self textColor]]; [_imageAndTextView setTextColor:[self textColor]];
[_imageAndTextView setTextShadowColor:[self textShadowColor]]; [_imageAndTextView setTextShadowColor:[self textShadowColor]];
[_submenuIndicatorView setColor:[self textColor]];
[_submenuIndicatorView setShadowColor:[self textShadowColor]];
} }
} }
@end @end
@implementation _CPMenuItemMenuBarSubmenuIndicatorView : CPView
{
CPColor _color;
CPColor _shadowColor;
}
- (void)setColor:(CPColor)aColor
{
if (_color === aColor)
return;
_color = aColor;
[self setNeedsDisplay:YES];
}
- (void)setShadowColor:(CPColor)aColor
{
if (_shadowColor === aColor)
return;
_shadowColor = aColor;
[self setNeedsDisplay:YES];
}
- (void)drawRect:(CGRect)aRect
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
bounds = [self bounds];
bounds.size.height -= 1.0;
bounds.size.width -= 2.0;
bounds.origin.x += 1.0;
CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMinY(bounds));
CGContextAddLineToPoint(context, CGRectGetMidX(bounds), CGRectGetMaxY(bounds));
CGContextClosePath(context);
CGContextSetShadowWithColor(context, CGSizeMake(0.0, 1.0), 1.1, _shadowColor || [CPColor whiteColor]);
CGContextSetFillColor(context, _color || [CPColor blackColor]);
CGContextFillPath(context);
}
@end
+2 -25
View File
@@ -1,29 +1,6 @@
/*
* _CPMenuItemSeparatorView.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPView.j" @import "CPView.j"
@class _CPMenuItemStandardView
@implementation _CPMenuItemSeparatorView : CPView @implementation _CPMenuItemSeparatorView : CPView
{ {
@@ -54,8 +31,8 @@
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5); CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5); CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
CGContextSetStrokeColor(context, [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-item-separator-color" forClass:_CPMenuItemStandardView]); CGContextSetStrokeColor(context, [CPColor lightGrayColor]);
CGContextStrokePath(context); CGContextStrokePath(context);
} }
@end @end
+77 -153
View File
@@ -1,41 +1,32 @@
/*
* _CPMenuItemStandardView.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPControl.j" @import "CPControl.j"
@import "CPImageView.j"
@import "_CPImageAndTextView.j"
@class CPMenuItem
var LEFT_MARGIN = 3.0,
RIGHT_MARGIN = 14.0 + 3.0,
STATE_COLUMN_WIDTH = 14.0,
INDENTATION_WIDTH = 17.0,
VERTICAL_MARGIN = 4.0,
RIGHT_COLUMNS_MARGIN = 30.0,
KEY_EQUIVALENT_MARGIN = 10.0;
var SUBMENU_INDICATOR_COLOR = nil,
_CPMenuItemSelectionColor = nil,
_CPMenuItemTextShadowColor = nil,
_CPMenuItemDefaultStateImages = [],
_CPMenuItemDefaultStateHighlightedImages = [];
@implementation _CPMenuItemStandardView : CPView @implementation _CPMenuItemStandardView : CPView
{ {
CPMenuItem _menuItem @accessors(property=menuItem); CPMenuItem _menuItem @accessors(property=menuItem);
CPFont _font; CPFont _font;
CPColor _textColor;
CPColor _textShadowColor;
CGSize _minSize @accessors(readonly, property=minSize); CGSize _minSize @accessors(readonly, property=minSize);
BOOL _isDirty; BOOL _isDirty;
BOOL _highlighted;
CPImageView _stateView; CPImageView _stateView;
_CPImageAndTextView _imageAndTextView; _CPImageAndTextView _imageAndTextView;
@@ -43,33 +34,26 @@
CPView _submenuIndicatorView; CPView _submenuIndicatorView;
} }
+ (CPString)defaultThemeClass + (void)initialize
{ {
return "menu-item-standard-view"; if (self !== [_CPMenuItemStandardView class])
} return;
+ (CPDictionary)themeAttributes SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
{
return @{ _CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
@"submenu-indicator-color": [CPNull null], _CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
@"menu-item-selection-color": [CPNull null],
@"menu-item-text-shadow-color": [CPNull null], var bundle = [CPBundle bundleForClass:self];
@"menu-item-text-color": [CPNull null],
@"menu-item-disabled-text-color": [CPColor lightGrayColor], _CPMenuItemDefaultStateImages[CPOffState] = nil;
@"menu-item-default-off-state-image": [CPNull null], _CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
@"menu-item-default-off-state-highlighted-image": [CPNull null],
@"menu-item-default-on-state-image": [CPNull null], _CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
@"menu-item-default-on-state-highlighted-image": [CPNull null], _CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
@"menu-item-default-mixed-state-image": [CPNull null],
@"menu-item-default-mixed-state-highlighted-image": [CPNull null], _CPMenuItemDefaultStateImages[CPMixedState] = nil;
@"menu-item-separator-color": [CPNull null], _CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
@"left-margin": 3.0,
@"right-margin": 17.0,
@"state-column-width": 14.0,
@"indentation-width": 17.0,
@"vertical-margin": 4.0,
@"right-columns-margin": 30.0,
};
} }
+ (id)view + (id)view
@@ -79,7 +63,7 @@
+ (float)_standardLeftMargin + (float)_standardLeftMargin
{ {
return [[CPTheme defaultTheme] valueForAttributeWithName:@"left-margin" forClass:[self class]] + [[CPTheme defaultTheme] valueForAttributeWithName:@"state-column-width" forClass:[self class]]; return LEFT_MARGIN + STATE_COLUMN_WIDTH;
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
@@ -90,7 +74,7 @@
{ {
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)]; _stateView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
[_stateView setImageScaling:CPImageScaleNone]; [_stateView setImageScaling:CPScaleNone];
[self addSubview:_stateView]; [self addSubview:_stateView];
@@ -111,7 +95,7 @@
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)]; _submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]]; [_submenuIndicatorView setColor:SUBMENU_INDICATOR_COLOR];
[_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin]; [_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin];
[self addSubview:_submenuIndicatorView]; [self addSubview:_submenuIndicatorView];
@@ -125,12 +109,9 @@
- (CPColor)textColor - (CPColor)textColor
{ {
if (![_menuItem isEnabled]) if (![_menuItem isEnabled])
return [self valueForThemeAttribute:@"menu-item-disabled-text-color"]; return [CPColor lightGrayColor];
if (_highlighted) return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
return [CPColor whiteColor];
return [self valueForThemeAttribute:@"menu-item-text-color"];
} }
- (CPColor)textShadowColor - (CPColor)textShadowColor
@@ -138,10 +119,7 @@
if (![_menuItem isEnabled]) if (![_menuItem isEnabled])
return nil; return nil;
if (_highlighted) return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
return nil;
return [self valueForThemeAttribute:@"menu-item-text-shadow-color"];
} }
- (void)setFont:(CPFont)aFont - (void)setFont:(CPFont)aFont
@@ -151,34 +129,16 @@
- (void)update - (void)update
{ {
var x = [self valueForThemeAttribute:@"left-margin"] + [_menuItem indentationLevel] * [self valueForThemeAttribute:@"indentation-width"], var x = LEFT_MARGIN + [_menuItem indentationLevel] * INDENTATION_WIDTH,
height = 0.0, height = 0.0,
hasStateColumn = [[_menuItem menu] showsStateColumn]; hasStateColumn = [[_menuItem menu] showsStateColumn];
if (hasStateColumn) if (hasStateColumn)
{ {
[_stateView setHidden:NO]; [_stateView setHidden:NO];
//[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil]; [_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
switch ([_menuItem state]) x += STATE_COLUMN_WIDTH;
{
case CPOnState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image"]];
break;
case CPOffState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image"]];
break;
case CPMixedState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image"]];
break;
default:
break;
}
x += [self valueForThemeAttribute:@"state-column-width"];
} }
else else
[_stateView setHidden:YES]; [_stateView setHidden:YES];
@@ -202,7 +162,7 @@
hasSubmenu = [_menuItem hasSubmenu]; hasSubmenu = [_menuItem hasSubmenu];
if (hasKeyEquivalent || hasSubmenu) if (hasKeyEquivalent || hasSubmenu)
x += [self valueForThemeAttribute:@"right-columns-margin"]; x += RIGHT_COLUMNS_MARGIN;
if (hasKeyEquivalent) if (hasKeyEquivalent)
{ {
@@ -213,9 +173,9 @@
[_keyEquivalentView setTextColor:[self textColor]]; [_keyEquivalentView setTextColor:[self textColor]];
[_keyEquivalentView setTextShadowColor:[self textShadowColor]]; [_keyEquivalentView setTextShadowColor:[self textShadowColor]];
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0, 1)]; [_keyEquivalentView setTextShadowOffset:CGSizeMake(0, 1)];
[_keyEquivalentView setFrameOrigin:CGPointMake(x, [self valueForThemeAttribute:@"vertical-margin"])]; [_keyEquivalentView setFrameOrigin:CGPointMake(x, VERTICAL_MARGIN)];
[_keyEquivalentView sizeToFit]; [_keyEquivalentView sizeToFit];
var keyEquivalentViewFrame = [_keyEquivalentView frame]; var keyEquivalentViewFrame = [_keyEquivalentView frame];
keyEquivalentViewFrame.origin.x = x; keyEquivalentViewFrame.origin.x = x;
@@ -223,7 +183,7 @@
height = MAX(height, CGRectGetHeight(keyEquivalentViewFrame)); height = MAX(height, CGRectGetHeight(keyEquivalentViewFrame));
if (hasSubmenu) if (hasSubmenu)
x += [self valueForThemeAttribute:@"right-columns-margin"]; x += RIGHT_COLUMNS_MARGIN;
} }
else else
[_keyEquivalentView setHidden:YES]; [_keyEquivalentView setHidden:YES];
@@ -242,28 +202,28 @@
else else
[_submenuIndicatorView setHidden:YES]; [_submenuIndicatorView setHidden:YES];
height += 2.0 * [self valueForThemeAttribute:@"vertical-margin"]; height += 2.0 * VERTICAL_MARGIN;
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0); imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
[_imageAndTextView setFrame:imageAndTextViewFrame]; [_imageAndTextView setFrame:imageAndTextViewFrame];
if (hasStateColumn) if (hasStateColumn)
[_stateView setFrameSize:CGSizeMake([self valueForThemeAttribute:@"state-column-width"], height)]; [_stateView setFrameSize:CGSizeMake(STATE_COLUMN_WIDTH, height)];
if (hasKeyEquivalent) if (hasKeyEquivalent)
{ {
keyEquivalentViewFrame.origin.y = FLOOR((height - CGRectGetHeight(keyEquivalentViewFrame)) / 2.0); keyEquivalentViewFrame.origin.y = FLOOR((height - CGRectGetHeight(keyEquivalentViewFrame)) / 2.0);
[_keyEquivalentView setFrame:keyEquivalentViewFrame]; [_keyEquivalentView setFrame:keyEquivalentViewFrame];
} }
if (hasSubmenu) if (hasSubmenu)
{ {
submenuViewFrame.origin.y = FLOOR((height - CGRectGetHeight(submenuViewFrame)) / 2.0); submenuViewFrame.origin.y = FLOOR((height - CGRectGetHeight(submenuViewFrame)) / 2.0);
[_submenuIndicatorView setFrame:submenuViewFrame]; [_submenuIndicatorView setFrame:submenuViewFrame];
} }
_minSize = CGSizeMake(x + [self valueForThemeAttribute:@"right-margin"], height); _minSize = CGSizeMake(x + RIGHT_MARGIN, height);
[self setAutoresizesSubviews:NO]; [self setAutoresizesSubviews:NO];
[self setFrameSize:_minSize]; [self setFrameSize:_minSize];
[self setAutoresizesSubviews:YES]; [self setAutoresizesSubviews:YES];
@@ -273,78 +233,42 @@
{ {
// FIXME: This should probably be even throw. // FIXME: This should probably be even throw.
if (![_menuItem isEnabled]) if (![_menuItem isEnabled])
return; return;
_highlighted = shouldHighlight;
[_imageAndTextView setTextColor:[self textColor]];
[_keyEquivalentView setTextColor:[self textColor]];
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
if (shouldHighlight) if (shouldHighlight)
{ {
[self setBackgroundColor:[self valueForThemeAttribute:@"menu-item-selection-color"]]; [self setBackgroundColor:_CPMenuItemSelectionColor];
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]]; [_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
[_submenuIndicatorView setColor:[self textColor]]; [_imageAndTextView setTextColor:[CPColor whiteColor]];
[_keyEquivalentView setTextColor:[CPColor whiteColor]];
[_submenuIndicatorView setColor:[CPColor whiteColor]];
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
[_keyEquivalentView setTextShadowColor:_CPMenuItemTextShadowColor];
} }
else else
{ {
[self setBackgroundColor:nil]; [self setBackgroundColor:nil];
[_imageAndTextView setImage:[_menuItem image]];
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
}
[_imageAndTextView setImage:[_menuItem image]];
[_imageAndTextView setTextColor:[self textColor]];
[_keyEquivalentView setTextColor:[self textColor]];
[_submenuIndicatorView setColor:SUBMENU_INDICATOR_COLOR];
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
}
if ([[_menuItem menu] showsStateColumn]) if ([[_menuItem menu] showsStateColumn])
{ {
if (shouldHighlight) if (shouldHighlight)
{ [_stateView setImage:_CPMenuItemDefaultStateHighlightedImages[[_menuItem state]] || nil];
switch ([_menuItem state])
{
case CPOnState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-highlighted-image"]];
break;
case CPOffState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-highlighted-image"]];
break;
case CPMixedState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-highlighted-image"]];
break;
default:
break;
}
}
else else
{ [_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
switch ([_menuItem state])
{
case CPOnState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image"]];
break;
case CPOffState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image"]];
break;
case CPMixedState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image"]];
break;
default:
break;
}
}
} }
} }
- (BOOL)isHighlighted
{
return _highlighted;
}
@end @end
@implementation _CPMenuItemSubmenuIndicatorView : CPView @implementation _CPMenuItemSubmenuIndicatorView : CPView
@@ -366,15 +290,15 @@
{ {
var context = [[CPGraphicsContext currentContext] graphicsPort], var context = [[CPGraphicsContext currentContext] graphicsPort],
bounds = [self bounds]; bounds = [self bounds];
CGContextBeginPath(context); CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds)); CGContextMoveToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMidY(bounds)); CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMidY(bounds));
CGContextAddLineToPoint(context, CGRectGetMinX(bounds), CGRectGetMaxY(bounds)); CGContextAddLineToPoint(context, CGRectGetMinX(bounds), CGRectGetMaxY(bounds));
CGContextClosePath(context); CGContextClosePath(context);
CGContextSetFillColor(context, _color); CGContextSetFillColor(context, _color);
CGContextFillPath(context); CGContextFillPath(context);
} }
+37 -66
View File
@@ -1,33 +1,22 @@
/*
* _CPMenuItemView.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPControl.j" @import <AppKit/CPControl.j>
@import "_CPMenuItemSeparatorView.j" @import "_CPMenuItemSeparatorView.j"
@import "_CPMenuItemStandardView.j" @import "_CPMenuItemStandardView.j"
@import "_CPMenuItemMenuBarView.j" @import "_CPMenuItemMenuBarView.j"
@class CPMenuItem
@global CPApp var LEFT_MARGIN = 3.0,
RIGHT_MARGIN = 16.0,
STATE_COLUMN_WIDTH = 14.0,
INDENTATION_WIDTH = 17.0,
VERTICAL_MARGIN = 4.0;
var _CPMenuItemSelectionColor = nil,
_CPMenuItemTextShadowColor = nil,
_CPMenuItemDefaultStateImages = [],
_CPMenuItemDefaultStateHighlightedImages = [];
/* /*
@ignore @ignore
@@ -35,7 +24,7 @@
@implementation _CPMenuItemView : CPView @implementation _CPMenuItemView : CPView
{ {
CPMenuItem _menuItem; CPMenuItem _menuItem;
CPView _view @accessors(property=view, readonly); CPView _view;
CPFont _font; CPFont _font;
CPColor _textColor; CPColor _textColor;
@@ -49,22 +38,31 @@
CPView _submenuView; CPView _submenuView;
} }
+ (CPString)defaultThemeClass + (void)initialize
{ {
return "menu-item-view"; if (self != [_CPMenuItemView class])
return;
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
var bundle = [CPBundle bundleForClass:self];
_CPMenuItemDefaultStateImages[CPOffState] = nil;
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
} }
+ (CPDictionary)themeAttributes + (float)leftMargin
{ {
return @{}; return LEFT_MARGIN + STATE_COLUMN_WIDTH;
} }
// Not used in the Appkit
// + (float)leftMargin
// {
// return LEFT_MARGIN + STATE_COLUMN_WIDTH;
// }
- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem - (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem
{ {
self = [super initWithFrame:aFrame]; self = [super initWithFrame:aFrame];
@@ -113,6 +111,7 @@
_view = menuItemView; _view = menuItemView;
} }
} }
else if ([_menuItem menu] == [CPApp mainMenu]) else if ([_menuItem menu] == [CPApp mainMenu])
{ {
if (![_view isKindOfClass:[_CPMenuItemMenuBarView class]]) if (![_view isKindOfClass:[_CPMenuItemMenuBarView class]])
@@ -177,7 +176,7 @@
- (void)setFont:(CPFont)aFont - (void)setFont:(CPFont)aFont
{ {
if ([_font isEqual:aFont]) if (_font === aFont)
return; return;
_font = aFont; _font = aFont;
@@ -220,36 +219,6 @@
return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8]; return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8];
} }
- (void)setParentMenuHighlightColor:(CPColor)aColor
{
if ([_view respondsToSelector:@selector(setHighlightColor:)])
[_view setHighlightColor:aColor];
}
- (void)setParentMenuHighlightTextColor:(CPColor)aColor
{
if ([_view respondsToSelector:@selector(setHighlightTextColor:)])
[_view setHighlightTextColor:aColor];
}
- (void)setParentMenuHighlightTextShadowColor:(CPColor)aColor
{
if ([_view respondsToSelector:@selector(setHighlightTextShadowColor:)])
[_view setHighlightTextShadowColor:aColor];
}
- (void)setParentMenuTextColor:(CPColor)aColor
{
if ([_view respondsToSelector:@selector(setTextColor:)])
[_view setTextColor:aColor];
}
- (void)setParentMenuTextShadowColor:(CPColor)aColor
{
if ([_view respondsToSelector:@selector(setTextShadowColor:)])
[_view setTextShadowColor:aColor];
}
@end @end
@implementation _CPMenuItemArrowView : CPView @implementation _CPMenuItemArrowView : CPView
@@ -276,6 +245,8 @@
CGContextMoveToPoint(context, 1.0, 4.0); CGContextMoveToPoint(context, 1.0, 4.0);
CGContextAddLineToPoint(context, 9.0, 4.0); CGContextAddLineToPoint(context, 9.0, 4.0);
CGContextAddLineToPoint(context, 5.0, 8.0); CGContextAddLineToPoint(context, 5.0, 8.0);
CGContextAddLineToPoint(context, 1.0, 4.0);
CGContextClosePath(context); CGContextClosePath(context);
CGContextSetFillColor(context, _color); CGContextSetFillColor(context, _color);
+38 -168
View File
@@ -22,13 +22,8 @@
@import <Foundation/CPDictionary.j> @import <Foundation/CPDictionary.j>
@import <Foundation/CPCountedSet.j> @import <Foundation/CPCountedSet.j>
@import <Foundation/_CPCollectionKVCOperators.j>
@import "CPController.j" @import "CPController.j"
@import "CPKeyValueBinding.j"
@class _CPManagedProxy
@class CPPredicate;
/*! /*!
@class @class
@@ -50,18 +45,12 @@
BOOL _isEditable; BOOL _isEditable;
BOOL _automaticallyPreparesContent; BOOL _automaticallyPreparesContent;
BOOL _usesLazyFetching @accessors(getter=usesLazyFetching, setter=setUsesLazyFetching:);
BOOL _isUsingManagedProxy;
_CPManagedProxy _managedProxy;
CPCountedSet _observedKeys; CPCountedSet _observedKeys;
} }
+ (void)initialize + (id)initialize
{ {
if (self !== [CPObjectController class])
return;
[self exposeBinding:@"editable"]; [self exposeBinding:@"editable"];
[self exposeBinding:@"contentObject"]; [self exposeBinding:@"contentObject"];
} }
@@ -112,13 +101,11 @@
{ {
if (self = [super init]) if (self = [super init])
{ {
[self setContent:aContent];
[self setEditable:YES]; [self setEditable:YES];
[self setObjectClass:[CPMutableDictionary class]]; [self setObjectClass:[CPMutableDictionary class]];
_observedKeys = [[CPCountedSet alloc] init]; _observedKeys = [[CPCountedSet alloc] init];
_selection = [[CPControllerSelectionProxy alloc] initWithController:self];
[self setContent:aContent];
} }
return self; return self;
@@ -144,8 +131,8 @@
_contentObject = aContent; _contentObject = aContent;
[self _selectionDidChange];
[self didChangeValueForKey:@"contentObject"]; [self didChangeValueForKey:@"contentObject"];
[self _selectionDidChange];
} }
/*! /*!
@@ -185,52 +172,6 @@
return _automaticallyPreparesContent; return _automaticallyPreparesContent;
} }
/*!
Sets the entity name the controller handles.
@param CPString newEntityName - The new entity name.
*/
- (void)setEntityName:(CPString)newEntityName
{
if (!_managedProxy)
{
_managedProxy = [[_CPManagedProxy alloc] init];
_isUsingManagedProxy = YES;
}
[_managedProxy setEntityName:newEntityName];
}
/*!
Returns the entity name.
@return CPString - The name of the entity.
*/
- (CPString)entityName
{
return [_managedProxy entityName];
}
/*!
Sets the predicate used to fetch content.
@param CPPredicate newPredicate - The fetch predicate.
*/
- (void)setFetchPredicate:(CPPredicate)newPredicate
{
[_managedProxy setFetchPredicate:newPredicate];
}
/*!
Returns the fetch predicate.
@return CPPredicate - The predicate used to fetch content.
*/
- (CPPredicate)fetchPredicate
{
return [_managedProxy fetchPredicate];
}
/*! /*!
Overridden by a subclass that require control over the creation of new objects. Overridden by a subclass that require control over the creation of new objects.
*/ */
@@ -241,7 +182,6 @@
/*! /*!
Sets the object class when creating new objects. Sets the object class when creating new objects.
@param Class - the class of new objects that will be created. @param Class - the class of new objects that will be created.
*/ */
- (void)setObjectClass:(Class)aClass - (void)setObjectClass:(Class)aClass
@@ -416,10 +356,7 @@
var CPObjectControllerContentKey = @"CPObjectControllerContentKey", var CPObjectControllerContentKey = @"CPObjectControllerContentKey",
CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey", CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey", CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey", CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
CPObjectControllerUsesLazyFetchingKey = @"CPObjectControllerUsesLazyFetchingKey",
CPObjectControllerIsUsingManagedProxyKey = @"CPObjectControllerIsUsingManagedProxyKey",
CPObjectControllerManagedProxyKey = @"CPObjectControllerManagedProxyKey";
@implementation CPObjectController (CPCoding) @implementation CPObjectController (CPCoding)
@@ -430,18 +367,12 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
if (self) if (self)
{ {
var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey], var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
objectClass = CPClassFromString(objectClassName), objectClass = CPClassFromString(objectClassName);
content = [aCoder decodeObjectForKey:CPObjectControllerContentKey];
[self setObjectClass:objectClass || [CPMutableDictionary class]]; [self setObjectClass:objectClass || [CPMutableDictionary class]];
[self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]]; [self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey]]; [self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey] || NO];
[self setUsesLazyFetching:[aCoder decodeBoolForKey:CPObjectControllerUsesLazyFetchingKey]]; [self setContent:[aCoder decodeObjectForKey:CPObjectControllerContentKey]];
_isUsingManagedProxy = [aCoder decodeBoolForKey:CPObjectControllerIsUsingManagedProxyKey];
_managedProxy = [aCoder decodeObjectForKey:CPObjectControllerManagedProxyKey];
if (content != nil)
[self setContent:content];
_observedKeys = [[CPCountedSet alloc] init]; _observedKeys = [[CPCountedSet alloc] init];
} }
@@ -460,11 +391,6 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
[aCoder encodeBool:[self isEditable] forKey:CPObjectControllerIsEditableKey]; [aCoder encodeBool:[self isEditable] forKey:CPObjectControllerIsEditableKey];
[aCoder encodeBool:[self automaticallyPreparesContent] forKey:CPObjectControllerAutomaticallyPreparesContentKey]; [aCoder encodeBool:[self automaticallyPreparesContent] forKey:CPObjectControllerAutomaticallyPreparesContentKey];
[aCoder encodeBool:[self usesLazyFetching] forKey:CPObjectControllerUsesLazyFetchingKey];
[aCoder encodeBool:_isUsingManagedProxy forKey:CPObjectControllerIsUsingManagedProxyKey];
if (_managedProxy)
[aCoder encodeObject:_managedProxy forKey:CPObjectControllerManagedProxyKey];
} }
- (void)awakeFromCib - (void)awakeFromCib
@@ -526,13 +452,13 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (BOOL)isEqual:(id)anObject - (BOOL)isEqual:(id)anObject
{ {
if (self === anObject) if ([anObject class] === [self class])
return YES; {
if (anObject._observer === _observer && [anObject._keyPath isEqual:_keyPath] && [anObject._object isEqual:_object])
return YES;
}
if (!anObject || [anObject class] !== [self class] || anObject._observer !== _observer || anObject._keyPath !== _keyPath || anObject._object !== _object) return NO;
return NO;
return YES;
} }
- (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
@@ -587,12 +513,8 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context - (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
{ {
if (aKeyPath.charAt(0) === "@") if (aKeyPath.indexOf("@") === 0)
{ {
// Simple collection operators are scalar and can't be proxied
if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
return;
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self]; var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self];
proxy._options = options; proxy._options = options;
@@ -615,12 +537,8 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath - (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
{ {
if (aKeyPath.charAt(0) === "@") if (aKeyPath.indexOf("@") === 0)
{ {
// Simple collection operators are scalar and can't be proxied
if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
return;
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self], var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self],
index = [_observationProxies indexOfObject:proxy]; index = [_observationProxies indexOfObject:proxy];
@@ -639,13 +557,13 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
} }
} }
- (void)insertObject:(id)anObject atIndex:(CPUInteger)anIndex - (void)insertObject:(id)anObject atIndex:(unsigned)anIndex
{ {
for (var i = 0, count = [_observationProxies count]; i < count; i++) for (var i = 0, count = [_observationProxies count]; i < count; i++)
{ {
var proxy = [_observationProxies objectAtIndex:i], var proxy = [_observationProxies objectAtIndex:i],
keyPath = [proxy keyPath], keyPath = [proxy keyPath],
operator = keyPath.charAt(0) === "."; operator = keyPath.indexOf(".") === 0;
if (operator) if (operator)
[self willChangeValueForKey:keyPath]; [self willChangeValueForKey:keyPath];
@@ -659,20 +577,18 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
[super insertObject:anObject atIndex:anIndex]; [super insertObject:anObject atIndex:anIndex];
} }
- (void)removeObjectAtIndex:(CPUInteger)anIndex - (void)removeObjectAtIndex:(unsigned)anIndex
{ {
var currentObject = [self objectAtIndex:anIndex];
for (var i = 0, count = [_observationProxies count]; i < count; i++) for (var i = 0, count = [_observationProxies count]; i < count; i++)
{ {
var proxy = [_observationProxies objectAtIndex:i], var proxy = [_observationProxies objectAtIndex:i],
keyPath = [proxy keyPath], keyPath = [proxy keyPath],
operator = keyPath.charAt(0) === "."; operator = keyPath.indexOf(".") === 0;
if (operator) if (operator)
[self willChangeValueForKey:keyPath]; [self willChangeValueForKey:keyPath];
[currentObject removeObserver:proxy forKeyPath:keyPath]; [anObject removeObserver:proxy forKeyPath:keyPath];
if (operator) if (operator)
[self didChangeValueForKey:keyPath]; [self didChangeValueForKey:keyPath];
@@ -681,7 +597,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
[super removeObjectAtIndex:anIndex]; [super removeObjectAtIndex:anIndex];
} }
- (CPArray)objectsAtIndexes:(CPIndexSet)theIndexes - (_CPObservableArray)objectsAtIndexes:(CPIndexSet)theIndexes
{ {
return [_CPObservableArray arrayWithArray:[super objectsAtIndexes:theIndexes]]; return [_CPObservableArray arrayWithArray:[super objectsAtIndexes:theIndexes]];
} }
@@ -696,7 +612,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
[self removeObjectAtIndex:[self count]]; [self removeObjectAtIndex:[self count]];
} }
- (void)replaceObjectAtIndex:(CPUInteger)anIndex withObject:(id)anObject - (void)replaceObjectAtIndex:(unsigned)anIndex withObject:(id)anObject
{ {
var currentObject = [self objectAtIndex:anIndex]; var currentObject = [self objectAtIndex:anIndex];
@@ -704,7 +620,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
{ {
var proxy = [_observationProxies objectAtIndex:i], var proxy = [_observationProxies objectAtIndex:i],
keyPath = [proxy keyPath], keyPath = [proxy keyPath],
operator = keyPath.charAt(0) === "."; operator = keyPath.indexOf(".") === 0;
if (operator) if (operator)
[self willChangeValueForKey:keyPath]; [self willChangeValueForKey:keyPath];
@@ -736,7 +652,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
{ {
if (self = [super init]) if (self = [super init])
{ {
_cachedValues = @{}; _cachedValues = [CPDictionary dictionary];
_observationProxies = [CPArray array]; _observationProxies = [CPArray array];
_controller = aController; _controller = aController;
_observedObjectsByKeyPath = {}; _observedObjectsByKeyPath = {};
@@ -747,8 +663,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (id)_controllerMarkerForValues:(CPArray)theValues - (id)_controllerMarkerForValues:(CPArray)theValues
{ {
var count = [theValues count], var count = [theValues count];
value;
if (!count) if (!count)
value = CPNoSelectionMarker; value = CPNoSelectionMarker;
@@ -762,7 +677,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
{ {
value = [theValues objectAtIndex:0]; value = [theValues objectAtIndex:0];
for (var i = 0, count = [theValues count]; i < count && value != CPMultipleValuesMarker; i++) for (var i = 0, count= [theValues count]; i < count && value != CPMultipleValuesMarker; i++)
{ {
if (![value isEqual:[theValues objectAtIndex:i]]) if (![value isEqual:[theValues objectAtIndex:i]])
value = CPMultipleValuesMarker; value = CPMultipleValuesMarker;
@@ -779,17 +694,11 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (id)valueForKeyPath:(CPString)theKeyPath - (id)valueForKeyPath:(CPString)theKeyPath
{ {
var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath]; var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath];
value = [self _controllerMarkerForValues:values];
// Simple collection operators like @count return a scalar value, not an array or set [_cachedValues setObject:value forKey:theKeyPath];
if ([values isKindOfClass:CPArray] || [values isKindOfClass:CPSet])
{
var value = [self _controllerMarkerForValues:values];
[_cachedValues setObject:value forKey:theKeyPath];
return value; return value;
}
else
return values;
} }
- (id)valueForKey:(CPString)theKeyPath - (id)valueForKey:(CPString)theKeyPath
@@ -816,7 +725,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (void)setValue:(id)theValue forKey:(CPString)theKeyPath - (void)setValue:(id)theValue forKey:(CPString)theKeyPath
{ {
[self setValue:theValue forKeyPath:theKeyPath]; [self setValue:theKeyPath forKeyPath:theKeyPath];
} }
- (unsigned)count - (unsigned)count
@@ -867,7 +776,8 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
[proxy setNotifyObject:YES]; [proxy setNotifyObject:YES];
[_observationProxies addObject:proxy]; [_observationProxies addObject:proxy];
// We keep a reference to the observed objects because removeObserver: will be called after the selection changes. // We keep are reference to the observed objects
// because the removeObserver: will be called after the selection changes
var observedObjects = [_controller selectedObjects]; var observedObjects = [_controller selectedObjects];
_observedObjectsByKeyPath[aKeyPath] = observedObjects; _observedObjectsByKeyPath[aKeyPath] = observedObjects;
[observedObjects addObserver:proxy forKeyPath:aKeyPath options:options context:context]; [observedObjects addObserver:proxy forKeyPath:aKeyPath options:options context:context];
@@ -875,55 +785,15 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (void)removeObserver:(id)anObject forKeyPath:(CPString)aKeyPath - (void)removeObserver:(id)anObject forKeyPath:(CPString)aKeyPath
{ {
[_observationProxies enumerateObjectsUsingBlock:function(aProxy, idx, stop) var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObject object:self],
{ index = [_observationProxies indexOfObject:proxy];
if (aProxy._object === self && aProxy._keyPath == aKeyPath && aProxy._observer === anObject)
{
var observedObjects = _observedObjectsByKeyPath[aKeyPath];
[observedObjects removeObserver:aProxy forKeyPath:aKeyPath]; var observedObjects = _observedObjectsByKeyPath[aKeyPath];
[_observationProxies removeObjectAtIndex:idx]; [observedObjects removeObserver:[_observationProxies objectAtIndex:index] forKeyPath:aKeyPath];
_observedObjectsByKeyPath[aKeyPath] = nil; [_observationProxies removeObjectAtIndex:index];
stop(YES); _observedObjects = nil;
}
}];
} }
@end @end
@implementation _CPManagedProxy : CPObject
{
CPString _entityName @accessors(getter=entityName, setter=setEntityName:);
CPPredicate _fetchPredicate @accessors(getter=fetchPredicate, setter=setFetchPredicate:);
}
@end
var CPManagedProxyEntityNameKey = @"CPManagedProxyEntityNameKey",
CPManagedProxyFetchPredicateKey = @"CPManagedProxyFetchPredicateKey";
@implementation _CPManagedProxy (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super init];
if (self)
{
[self setEntityName:[aCoder decodeObjectForKey:CPManagedProxyEntityNameKey]];
[self setFetchPredicate:[aCoder decodeObjectForKey:CPManagedProxyFetchPredicateKey]];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:[self entityName] forKey:CPManagedProxyEntityNameKey];
[aCoder encodeObject:[self fetchPredicate] forKey:CPManagedProxyFetchPredicateKey];
}
@end
+2 -2
View File
@@ -47,9 +47,9 @@
var options = { directoryURL: [self directoryURL], var options = { directoryURL: [self directoryURL],
canChooseFiles: [self canChooseFiles], canChooseFiles: [self canChooseFiles],
canChooseDirectories: [self canChooseDirectories], canChooseDirectories: [self canChooseDirectories],
allowsMultipleSelection: [self allowsMultipleSelection] }, allowsMultipleSelection: [self allowsMultipleSelection] };
result = window.cpOpenPanel(options); var result = window.cpOpenPanel(options);
_URLs = result.URLs; _URLs = result.URLs;
+430 -723
View File
File diff suppressed because it is too large Load Diff
+2 -28
View File
@@ -22,13 +22,10 @@
@import "CPWindow.j" @import "CPWindow.j"
@global CPApp
CPOKButton = 1; CPOKButton = 1;
CPCancelButton = 0; CPCancelButton = 0;
CPDocModalWindowMask = 1 << 6;
/*! /*!
@ingroup appkit @ingroup appkit
@class CPPanel @class CPPanel
@@ -54,6 +51,8 @@ CPDocModalWindowMask = 1 << 6;
@global @global
@class CPWindow @class CPWindow
*/ */
CPDocModalWindowMask = 1 << 6;
@implementation CPPanel : CPWindow @implementation CPPanel : CPWindow
{ {
BOOL _becomesKeyOnlyIfNeeded; BOOL _becomesKeyOnlyIfNeeded;
@@ -115,29 +114,4 @@ CPDocModalWindowMask = 1 << 6;
return NO; return NO;
} }
- (BOOL)canBecomeKeyWindow
{
return YES;
}
#pragma mark -
#pragma mark Overrides
/*!
@ignore
*/
- (void)cancelOperation:(id)sender
{
if ([[CPApp currentEvent] _couldBeKeyEquivalent] && [self performKeyEquivalent:[CPApp currentEvent]])
return;
[[self firstResponder] tryToPerform:@selector(cancel:) with:self];
}
- (void)cancel:(id)sender
{
[self performClose:sender];
}
@end @end
+80 -25
View File
@@ -22,13 +22,9 @@
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPArray.j> @import <Foundation/CPArray.j>
@import <Foundation/CPData.j>
@import <Foundation/CPDictionary.j> @import <Foundation/CPDictionary.j>
@import <Foundation/CPPropertyListSerialization.j> @import <Foundation/CPPropertyListSerialization.j>
@class CPWebScriptObject
@typedef DataTransfer
CPGeneralPboard = @"CPGeneralPboard"; CPGeneralPboard = @"CPGeneralPboard";
CPFontPboard = @"CPFontPboard"; CPFontPboard = @"CPFontPboard";
@@ -44,8 +40,6 @@ CPStringPboardType = @"CPStringPboardType";
CPURLPboardType = @"CPURLPboardType"; CPURLPboardType = @"CPURLPboardType";
CPImagesPboardType = @"CPImagesPboardType"; CPImagesPboardType = @"CPImagesPboardType";
CPVideosPboardType = @"CPVideosPboardType"; CPVideosPboardType = @"CPVideosPboardType";
CPRTFPboardType = @"CPRTFPboardType";
_CPSmartPboardType = @"_CPSmartPboardType";
UTF8PboardType = @"public.utf8-plain-text"; UTF8PboardType = @"public.utf8-plain-text";
@@ -53,7 +47,8 @@ UTF8PboardType = @"public.utf8-plain-text";
CPImagePboardType = @"CPImagePboardType"; CPImagePboardType = @"CPImagePboardType";
var CPPasteboards = nil; var CPPasteboards = nil,
supportsNativePasteboard = NO;
/*! /*!
@ingroup appkit @ingroup appkit
@@ -69,6 +64,8 @@ var CPPasteboards = nil;
unsigned _changeCount; unsigned _changeCount;
CPString _stateUID; CPString _stateUID;
WebScriptObject _nativePasteboard;
} }
/* /*
@@ -76,12 +73,15 @@ var CPPasteboards = nil;
*/ */
+ (void)initialize + (void)initialize
{ {
if (self !== [CPPasteboard class]) if (self != [CPPasteboard class])
return; return;
[self setVersion:1.0]; [self setVersion:1.0];
CPPasteboards = @{}; CPPasteboards = [CPDictionary dictionary];
if (typeof window.cpPasteboardWithName !== "undefined")
supportsNativePasteboard = YES;
} }
/*! /*!
@@ -117,13 +117,19 @@ var CPPasteboards = nil;
if (self) if (self)
{ {
// _name = aName; _name = aName;
_types = []; _types = [];
_owners = @{}; _owners = [CPDictionary dictionary];
_provided = @{}; _provided = [CPDictionary dictionary];
_changeCount = 0; _changeCount = 0;
if (supportsNativePasteboard)
{
_nativePasteboard = window.cpPasteboardWithName(aName);
[self _synchronizePasteboard];
}
} }
return self; return self;
@@ -153,6 +159,15 @@ var CPPasteboards = nil;
[_owners setObject:anOwner forKey:type]; [_owners setObject:anOwner forKey:type];
} }
if (_nativePasteboard)
{
var nativeTypes = [types copy];
if ([types containsObject:CPStringPboardType])
nativeTypes.push(UTF8PboardType);
_nativePasteboard.addTypes_(nativeTypes);
}
return ++_changeCount; return ++_changeCount;
} }
@@ -163,19 +178,32 @@ var CPPasteboards = nil;
@return the pasteboard's change count @return the pasteboard's change count
*/ */
- (unsigned)declareTypes:(CPArray)types owner:(id)anOwner - (unsigned)declareTypes:(CPArray)types owner:(id)anOwner
{
[self _declareTypes:types owner:anOwner updateNativePasteboard:YES];
}
/*! @ignore */
- (unsigned)_declareTypes:(CPArray)types owner:(id)anOwner updateNativePasteboard:(BOOL)shouldUpdate
{ {
[_types setArray:types]; [_types setArray:types];
_owners = @{}; _owners = [CPDictionary dictionary];
_provided = @{}; _provided = [CPDictionary dictionary];
if (anOwner) var count = _types.length;
while (count--)
[_owners setObject:anOwner forKey:_types[count]];
if (_nativePasteboard && shouldUpdate)
{ {
var count = _types.length; var nativeTypes = [types copy];
while (count--) if ([types containsObject:CPStringPboardType])
[_owners setObject:anOwner forKey:_types[count]]; nativeTypes.push(UTF8PboardType);
}
_nativePasteboard.declareTypes_(nativeTypes);
_changeCount = _nativePasteboard.changeCount();
}
return ++_changeCount; return ++_changeCount;
} }
@@ -214,23 +242,19 @@ var CPPasteboards = nil;
*/ */
- (void)setString:(CPString)aString forType:(CPString)aType - (void)setString:(CPString)aString forType:(CPString)aType
{ {
// Putting a non-string on the string pasteboard can lead to strange crashes.
if (aString && aString.isa && ![aString isKindOfClass:CPString])
[CPException raise:CPInvalidArgumentException reason:"CPPasteboard setString:forType: must be called with a string."];
[self setPropertyList:aString forType:aType]; [self setPropertyList:aString forType:aType];
} }
// Determining Types // Determining Types
/*! /*!
Checks the pasteboard's types for a match with the types listed in the specified array. The array should Checks the pasteboard's types for a match with the types listen in the specified array. The array should
be ordered by the requestor's most preferred data type first. be ordered by the requestor's most preferred data type first.
@param anArray an array of requested types ordered by preference @param anArray an array of requested types ordered by preference
@return the highest match with the pasteboard's supported types or \c nil if no match was found @return the highest match with the pasteboard's supported types or \c nil if no match was found
*/ */
- (CPString)availableTypeFromArray:(CPArray)anArray - (CPString)availableTypeFromArray:(CPArray)anArray
{ {
return [anArray firstObjectCommonWithArray:[self types]]; return [[self types] firstObjectCommonWithArray:anArray];
} }
/*! /*!
@@ -238,6 +262,7 @@ var CPPasteboards = nil;
*/ */
- (CPArray)types - (CPArray)types
{ {
[self _synchronizePasteboard];
return _types; return _types;
} }
@@ -276,6 +301,36 @@ var CPPasteboards = nil;
return nil; return nil;
} }
- (void)_synchronizePasteboard
{
if (_nativePasteboard && _nativePasteboard.changeCount() > _changeCount)
{
var nativeTypes = [_nativePasteboard.types() copy];
if ([nativeTypes containsObject:UTF8PboardType])
nativeTypes.push(CPStringPboardType);
[self _declareTypes:nativeTypes owner:self updateNativePasteboard:NO];
_changeCount = _nativePasteboard.changeCount();
}
}
/*! @ignore
method provided for integration with native pasteboard
*/
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
{
if (aType === CPStringPboardType)
{
var string = _nativePasteboard.stringForType_(UTF8PboardType);
[self setString:string forType:CPStringPboardType];
[self setString:string forType:UTF8PboardType];
}
else
[self setString:_nativePasteboard.stringForType_(aType) forType:aType];
}
/*! /*!
Returns the property list for the specified data type Returns the property list for the specified data type
@param aType the requested data type @param aType the requested data type
+40 -316
View File
@@ -20,12 +20,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "CGGeometry.j"
@import "CPButton.j" @import "CPButton.j"
@import "CPKeyValueBinding.j" @import "CPGeometry.j"
@import "CPMenu.j" @import "CPMenu.j"
@import "CPMenuItem.j" @import "CPMenuItem.j"
var VISIBLE_MARGIN = 7.0;
CPPopUpButtonStatePullsDown = CPThemeState("pulls-down"); CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
/*! /*!
@@ -84,7 +86,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
[self setPullsDown:shouldPullDown]; [self setPullsDown:shouldPullDown];
var options = CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld; // | CPKeyValueObservingOptionInitial; var options = CPKeyValueObservingOptionNew |CPKeyValueObservingOptionOld;/* |CPKeyValueObservingOptionInitial;*/
[self addObserver:self forKeyPath:@"menu.items" options:options context:nil]; [self addObserver:self forKeyPath:@"menu.items" options:options context:nil];
[self addObserver:self forKeyPath:@"_firstItem.changeCount" options:options context:nil]; [self addObserver:self forKeyPath:@"_firstItem.changeCount" options:options context:nil];
[self addObserver:self forKeyPath:@"selectedItem.changeCount" options:options context:nil]; [self addObserver:self forKeyPath:@"selectedItem.changeCount" options:options context:nil];
@@ -191,7 +193,6 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
- (void)removeAllItems - (void)removeAllItems
{ {
[[self menu] removeAllItems]; [[self menu] removeAllItems];
[self synchronizeTitleAndSelectedItem];
} }
/*! /*!
@@ -244,6 +245,22 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
return _selectedIndex; return _selectedIndex;
} }
/*!
@ignore
*/
- (int)selectedTag
{
return [[self selectedItem] tag];
}
/*!
@ignore
*/
- (void)_setSelectedTag:(int)aTag
{
[self selectItemWithTag:aTag];
}
// Setting the Current Selection // Setting the Current Selection
/*! /*!
Selects the specified menu item. Selects the specified menu item.
@@ -277,7 +294,7 @@ 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)setObjectValue:(id)anIndex - (void)setObjectValue:(int)anIndex
{ {
var indexOfSelectedItem = [self objectValue]; var indexOfSelectedItem = [self objectValue];
@@ -342,7 +359,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
Returns the item at the specified index or \c nil if the item does not exist. Returns the item at the specified index or \c nil if the item does not exist.
@param anIndex the index of the item to obtain @param anIndex the index of the item to obtain
*/ */
- (CPMenuItem)itemAtIndex:(CPUInteger)anIndex - (CPMenuItem)itemAtIndex:(unsigned)anIndex
{ {
return [[self menu] itemAtIndex:anIndex]; return [[self menu] itemAtIndex:anIndex];
} }
@@ -351,7 +368,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
Returns the title of the item at the specified index or \c nil if no item exists. Returns the title of the item at the specified index or \c nil if no item exists.
@param anIndex the index of the item @param anIndex the index of the item
*/ */
- (CPString)itemTitleAtIndex:(CPUInteger)anIndex - (CPString)itemTitleAtIndex:(unsigned)anIndex
{ {
return [[[self menu] itemAtIndex:anIndex] title]; return [[[self menu] itemAtIndex:anIndex] title];
} }
@@ -361,10 +378,15 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
*/ */
- (CPArray)itemTitles - (CPArray)itemTitles
{ {
return [[self itemArray] arrayByApplyingBlock:function(item) var titles = [],
{ items = [self itemArray],
return [item title]; index = 0,
}]; count = [items count];
for (; index < count; ++index)
titles.push([items[index] title]);
return titles;
} }
/*! /*!
@@ -564,18 +586,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
if ([indexes containsIndex:0] && [self pullsDown]) if ([indexes containsIndex:0] && [self pullsDown])
[self _firstItemDidChange]; [self _firstItemDidChange];
if (![self pullsDown] && [indexes containsIndex:indexOfSelectedItem]) // See whether the index has changed, despite the actual item not changing.
{ while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound &&
// If the selected item is removed the first item becomes selected. index <= indexOfSelectedItem)
indexOfSelectedItem = 0; --indexOfSelectedItem;
}
else
{
// See whether the index has changed, despite the actual item not changing.
while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound &&
index <= indexOfSelectedItem)
--indexOfSelectedItem;
}
[self selectItemAtIndex:indexOfSelectedItem]; [self selectItemAtIndex:indexOfSelectedItem];
} }
@@ -660,15 +674,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
if (![self isEnabled] || ![self numberOfItems]) if (![self isEnabled] || ![self numberOfItems])
return; return;
var menu = [self menu];
// Don't reopen the menu based on the same click which caused it to close, e.g. a click on this button.
if (menu._lastCloseEvent === anEvent)
return;
[self highlight:YES]; [self highlight:YES];
var bounds = [self bounds], var menu = [self menu],
bounds = [self bounds],
minimumWidth = CGRectGetWidth(bounds); minimumWidth = CGRectGetWidth(bounds);
// FIXME: setFont: should set the font on the menu. // FIXME: setFont: should set the font on the menu.
@@ -677,7 +686,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
if ([self pullsDown]) if ([self pullsDown])
{ {
var positionedItem = nil, var positionedItem = nil,
location = CGPointMake(0.0, CGRectGetMaxY(bounds) - 1); location = CGPointMake(0.0, CGRectGetMaxY(bounds));
} }
else else
{ {
@@ -768,291 +777,6 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
@end @end
@implementation CPPopUpButton (BindingSupport)
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding == CPSelectedIndexBinding ||
aBinding == CPSelectedObjectBinding ||
aBinding == CPSelectedTagBinding ||
aBinding == CPSelectedValueBinding ||
aBinding == CPContentBinding ||
aBinding == CPContentObjectsBinding ||
aBinding == CPContentValuesBinding)
{
var capitalizedBinding = aBinding.charAt(0).toUpperCase() + aBinding.substr(1);
return [CPClassFromString(@"_CPPopUpButton" + capitalizedBinding + "Binder") class];
}
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return (aBinding == CPSelectedIndexBinding ||
aBinding == CPSelectedTagBinding ||
aBinding == CPSelectedValueBinding);
}
- (void)_reverseSetBinding
{
[_CPPopUpButtonSelectionBinder _reverseSetValueFromExclusiveBinderForObject:self];
[super _reverseSetBinding];
}
@end
@implementation _CPPopUpButtonContentBinder : CPBinder
{
}
- (CPInteger)_getInsertNullOffset
{
var options = [_info objectForKey:CPOptionsKey];
return [options objectForKey:CPInsertsNullPlaceholderBindingOption] ? 1 : 0;
}
- (CPString)_getNullPlaceholder
{
var options = [_info objectForKey:CPOptionsKey],
placeholder = [options objectForKey:CPNullPlaceholderBindingOption] || @"";
if (placeholder === [CPNull null])
placeholder = @"";
return placeholder;
}
- (id)transformValue:(CPArray)contentArray withOptions:(CPDictionary)options
{
// Desactivate the full array transformation forced by super because we don't want this. We want individual transformations (see below).
return contentArray;
}
- (void)setValue:(CPArray)contentArray forBinding:(CPString)aBinding
{
[self _setContent:contentArray];
[self _setContentValuesIfNeeded:contentArray];
}
- (id)valueForBinding:(CPString)aBinding
{
return [self _content];
}
- (void)_setContent:(CPArray)aValue
{
var count = [aValue count],
options = [_info objectForKey:CPOptionsKey],
offset = [self _getInsertNullOffset],
selectedBindingInfo = [_source infoForBinding:CPSelectedObjectBinding],
selectedObject = nil;
if (selectedBindingInfo)
{
var destination = [selectedBindingInfo objectForKey:CPObservedObjectKey],
keyPath = [selectedBindingInfo objectForKey:CPObservedKeyPathKey];
selectedObject = [destination valueForKeyPath:keyPath];
}
if (count + offset != [_source numberOfItems])
{
[_source removeAllItems];
if (offset)
[_source addItemWithTitle:[self _getNullPlaceholder]];
for (var i = 0; i < count; i++)
{
var item = [[CPMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:nil],
itemValue = [aValue objectAtIndex:i];
[self _setValue:itemValue forItem:item withOptions:options];
[_source addItem:item];
// Select this item if it is the one selected by the selected object binding
// This is needed if the selected object binding is set before the items
// from the content binding
if (itemValue === selectedObject)
{
[_source setSelectedIndex:[_source numberOfItems] - 1];
}
}
}
else
{
for (var i = 0; i < count; i++)
{
[self _setValue:[aValue objectAtIndex:i] forItem:[_source itemAtIndex:i + offset] withOptions:options];
}
}
}
- (void)_setContentValuesIfNeeded:(CPArray)values
{
var offset = [self _getInsertNullOffset];
if (![_source infoForBinding:CPContentValuesBinding])
{
if (offset)
[[_source itemAtIndex:0] setTitle:[self _getNullPlaceholder]];
var count = [values count];
for (var i = 0; i < count; i++)
[[_source itemAtIndex:i + offset] setTitle:[[values objectAtIndex:i] description]];
}
}
- (void)_setValue:(id)aValue forItem:(CPMenuItem)aMenuItem withOptions:(CPDictionary)options
{
var value = [self _transformValue:aValue withOptions:options];
[aMenuItem setRepresentedObject:value];
}
- (id)_transformValue:(id)aValue withOptions:(CPDictionary)options
{
return [super transformValue:aValue withOptions:options];
}
- (CPArray)_content
{
return [_source valueForKeyPath:@"itemArray.representedObject"];
}
@end
@implementation _CPPopUpButtonContentValuesBinder : _CPPopUpButtonContentBinder
{
}
- (void)setValue:(CPArray)aValue forBinding:(CPString)aBinding
{
[super _setContent:aValue];
}
- (void)_setValue:(id)aValue forItem:(CPMenuItem)aMenuItem withOptions:(CPDictionary)options
{
if (aValue === [CPNull null])
aValue = nil;
var value = [self _transformValue:aValue withOptions:options];
[aMenuItem setTitle:value];
}
- (CPArray)_content
{
return [_source valueForKeyPath:@"itemArray.title"];
}
@end
@implementation _CPPopUpButtonSelectionBinder : CPBinder
{
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
[self setValue:aValue forBinding:aBinding];
}
- (CPInteger)_getInsertNullOffset
{
var options = [[CPBinder infoForBinding:CPContentBinding forObject:_source] objectForKey:CPOptionsKey];
return [options objectForKey:CPInsertsNullPlaceholderBindingOption] ? 1 : 0;
}
@end
@implementation _CPPopUpButtonSelectedIndexBinder : _CPPopUpButtonSelectionBinder
{
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source selectItemAtIndex:aValue + [self _getInsertNullOffset]];
}
- (id)valueForBinding:(CPString)aBinding
{
return [_source indexOfSelectedItem] - [self _getInsertNullOffset];
}
@end
@implementation _CPPopUpButtonSelectedObjectBinder : _CPPopUpButtonSelectionBinder
{
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
var index = [_source indexOfItemWithRepresentedObject:aValue],
offset = [self _getInsertNullOffset];
// If the content binding has the option CPNullPlaceholderBindingOption and the object to select is nil, select the first item (i.e., the placeholder).
// Other cases to consider:
// 1. no binding:
// 1.1 there's no item with a represented object matching the object to select.
// 1.2 the object to select is nil/CPNull
// 2. there's a binding:
// 2.1 there's a CPNullPlaceholderBindingOption:
// 2.1.1 there's no item with a represented object matching the object to select?
// 2.1.2 the object to select is nil/CPNull
// 2.2 there's no CPNullPlaceholderBindingOption:
// 2.2.1 there's no item with a represented object matching the object to select?
// 2.2.2 the object to select is nil/CPNull
// More cases? Behaviour that depends on array controller settings?
if (offset === 1 && index === CPNotFound)
index = 0;
[_source selectItemAtIndex:index];
}
- (id)valueForBinding:(CPString)aBinding
{
return [[_source selectedItem] representedObject];
}
@end
@implementation _CPPopUpButtonSelectedTagBinder : _CPPopUpButtonSelectionBinder
{
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source selectItemWithTag:aValue];
}
- (id)valueForBinding:(CPString)aBinding
{
return [[_source selectedItem] tag];
}
@end
@implementation _CPPopUpButtonSelectedValueBinder : _CPPopUpButtonSelectionBinder
{
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source selectItemWithTitle:aValue];
}
- (id)valueForBinding:(CPString)aBinding
{
return [_source titleOfSelectedItem];
}
@end
var DEPRECATED_CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey", var DEPRECATED_CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
DEPRECATED_CPPopUpButtonSelectedIndexKey = @"CPPopUpButtonSelectedIndexKey"; DEPRECATED_CPPopUpButtonSelectedIndexKey = @"CPPopUpButtonSelectedIndexKey";
-420
View File
@@ -1,420 +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 "CPViewController.j"
@import "_CPPopoverWindow.j"
@protocol CPPopoverDelegate <CPObject>
@optional
- (BOOL)popoverShouldClose:(CPPopover)aPopover;
- (void)popoverWillClose:(CPPopover)aPopover;
- (void)popoverDidClose:(CPPopover)aPopover;
- (void)popoverWillShow:(CPPopover)aPopover;
- (void)popoverDidShow:(CPPopover)aPopover;
@end
CPPopoverBehaviorApplicationDefined = 0;
CPPopoverBehaviorTransient = 1;
CPPopoverBehaviorSemitransient = 2;
var CPPopoverDelegate_popover_willShow_ = 1 << 0,
CPPopoverDelegate_popover_didShow_ = 1 << 1,
CPPopoverDelegate_popover_shouldClose_ = 1 << 2,
CPPopoverDelegate_popover_willClose_ = 1 << 3,
CPPopoverDelegate_popover_didClose_ = 1 << 4;
/*! @ingroup appkit
@class CPPopover
This class represent a widget that displays a popover
view relative to another one.
Delegate can implement:
@code
popoverShouldClose:(CPPopover)aPopOver
popoverWillShow:(CPPopover)aPopOver
popoverDidShow:(CPPopover)aPopOver
popoverWillClose:(CPPopover)aPopOver
popoverDidClose:(CPPopover)aPopOver
@endcode
*/
@implementation CPPopover : CPResponder
{
@outlet CPViewController _contentViewController @accessors(property=contentViewController);
@outlet id <CPPopoverDelegate> _delegate @accessors(getter=delegate);
BOOL _animates @accessors(getter=animates);
int _appearance @accessors(property=appearance);
int _behavior @accessors(getter=behavior);
_CPPopoverWindow _popoverWindow;
CPView _positioningView;
int _implementedDelegateMethods;
}
#pragma mark -
#pragma mark Initialization
/*!
Initialize the CPPopover witn default values
@returns an initialized CPPopover
*/
- (CPPopover)init
{
if (self = [super init])
{
_animates = YES;
_appearance = CPPopoverAppearanceMinimal;
_behavior = CPPopoverBehaviorApplicationDefined;
}
return self;
}
#pragma mark -
#pragma mark Getters / Setters
/*!
Returns the current rect of the popover
@return CGRect representing the frame of the popover
*/
- (CGRect)positioningRect
{
if (![_popoverWindow isVisible])
return CGRectMakeZero();
return [_popoverWindow frame];
}
/*! Sets the frame of the popover
@param aRect the desired frame
*/
- (void)setPositioningRect:(CGRect)aRect
{
if (![_popoverWindow isVisible])
return;
[_popoverWindow setFrame:aRect];
}
/*!
Returns the size of the popover's view
@return CGSize representing the size of the popover's view
*/
- (CGSize)contentSize
{
return [[_contentViewController view] frameSize];
}
/*!
Sets the size of of the popover's view
@param aSize the desired size
*/
- (void)setContentSize:(CGSize)aSize
{
if (!_popoverWindow)
[[_contentViewController view] setFrameSize:aSize];
else
[_popoverWindow updateFrameWithSize:aSize];
}
/*!
Indicates if CPPopover is visible
@returns \c YES if visible
*/
- (BOOL)isShown
{
return [_popoverWindow isVisible];
}
/*!
Set if the popover should animate for open/close actions.
@param shouldAnimate if YES, the popover will be animated.
*/
- (void)setAnimates:(BOOL)shouldAnimate
{
if (_animates == shouldAnimate)
return;
_animates = shouldAnimate;
[_popoverWindow setAnimates:_animates];
}
/*!
Set the behavior of the CPPopover. It can be:
- \c CPPopoverBehaviorTransient: the popover will close if another control outside the popover becomes the responder
- \c CPPopoverBehaviorApplicationDefined: (DEFAULT) the application is responsible for closing the popover
@param aBehavior the desired behavior
*/
- (void)setBehavior:(int)aBehavior
{
if (_behavior == aBehavior)
return;
_behavior = aBehavior;
[_popoverWindow setStyleMask:[self _styleMaskForBehavior]];
}
- (void)setDelegate:(id <CPPopoverDelegate>)aDelegate
{
if (_delegate === aDelegate)
return;
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(popoverWillShow:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_willShow_;
if ([_delegate respondsToSelector:@selector(popoverDidShow:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_didShow_;
if ([_delegate respondsToSelector:@selector(popoverShouldClose:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_shouldClose_;
if ([_delegate respondsToSelector:@selector(popoverWillClose:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_willClose_;
if ([_delegate respondsToSelector:@selector(popoverDidClose:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_didClose_;
}
#pragma mark -
#pragma mark Positioning
/*!
Show the popover
@param positioningRect if set, the popover will be positionned to a random rect relative to the window
@param positioningView if set, the popover will be positioned relative to this view
@param preferredEdge: \c CPRectEdge representing the preferred positioning.
*/
- (void)showRelativeToRect:(CGRect)positioningRect ofView:(CPView)positioningView preferredEdge:(CPRectEdge)preferredEdge
{
if (!positioningView)
[CPException raise:CPInvalidArgumentException reason:"positionView must not be nil"];
if (!_contentViewController)
[CPException raise:CPInternalInconsistencyException reason:@"contentViewController must not be nil"];
// If the popover is currently closing or opening, do nothing. That is what Cocoa does.
if ([_popoverWindow isClosing] || [_popoverWindow isOpening])
return;
_positioningView = positioningView;
if (!_popoverWindow)
_popoverWindow = [[_CPPopoverWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:[self _styleMaskForBehavior]];
[_popoverWindow setPlatformWindow:[[positioningView window] platformWindow]];
[_popoverWindow setAppearance:_appearance];
[_popoverWindow setAnimates:_animates];
[_popoverWindow setDelegate:self];
[_popoverWindow setMovableByWindowBackground:NO];
[_popoverWindow setFrame:[_popoverWindow frameRectForContentRect:[[_contentViewController view] frame]]];
[_popoverWindow setContentView:[_contentViewController view]];
if (![self isShown])
[self _popoverWillShow];
[_popoverWindow positionRelativeToRect:positioningRect ofView:positioningView preferredEdge:preferredEdge];
if (![self isShown])
[self _popoverWindowDidShow];
}
- (unsigned)_styleMaskForBehavior
{
if (_behavior == CPPopoverBehaviorApplicationDefined)
return 0;
return CPClosableOnBlurWindowMask
}
/*!
Closes the popover
*/
- (void)close
{
[self _close];
}
/*!
Closes the popover
*/
- (void)_close
{
if ([_popoverWindow isClosing] || ![self isShown])
return;
[self _popoverWillClose];
_positioningView = nil;
[_popoverWindow close];
// popoverDidClose will be sent from popoverWindowDidClose, since
// the popover window will close asynchronously when animating.
}
#pragma mark -
#pragma mark Action
/*!
Close the popover
@param sender the sender of the action
*/
- (IBAction)performClose:(id)sender
{
if ([_popoverWindow isClosing])
return;
if (![self _popoverShouldClose])
return;
[self _close];
}
#pragma mark -
#pragma mark Delegates
/*! @ignore */
- (BOOL)_popoverWindowShouldClose
{
[self performClose:self];
// We return NO, because we want the CPPopover to determine
// if the popover window can be closed and to give us a chance
// to send delegate messages.
return NO;
}
/*! @ignore */
- (void)_popoverWindowDidClose
{
if (_implementedDelegateMethods & CPPopoverDelegate_popover_didClose_)
[_delegate popoverDidClose:self];
}
/*! @ignore */
- (void)_popoverWindowDidShow
{
if (_implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
[_delegate popoverDidShow:self];
}
/*! @ignore */
- (BOOL)_popoverShouldClose
{
if (_implementedDelegateMethods & CPPopoverDelegate_popover_shouldClose_)
return [_delegate popoverShouldClose:self];
return YES;
}
/*! @ignore */
- (void)_popoverWillClose
{
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_)
[_delegate popoverWillClose:self];
}
/*! @ignore */
- (void)_popoverWillShow
{
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_)
[_delegate popoverWillShow:self];
}
@end
@implementation CPPopover (Deprecated)
- (void)setBehaviour:(int)aBehavior
{
_CPReportLenientDeprecation(self, _cmd, @selector(setBehavior:));
[self setBehavior:aBehavior];
}
@end
var CPPopoverNeedsNewPopoverWindowKey = @"CPPopoverNeedsNewPopoverWindowKey",
CPPopoverAppearanceKey = @"CPPopoverAppearanceKey",
CPPopoverAnimatesKey = @"CPPopoverAnimatesKey",
CPPopoverContentViewControllerKey = @"CPPopoverContentViewControllerKey",
CPPopoverDelegateKey = @"CPPopoverDelegateKey",
CPPopoverBehaviorKey = @"CPPopoverBehaviorKey";
@implementation CPPopover (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_appearance = [aCoder decodeIntForKey:CPPopoverAppearanceKey];
_animates = [aCoder decodeBoolForKey:CPPopoverAnimatesKey];
_contentViewController = [aCoder decodeObjectForKey:CPPopoverContentViewControllerKey];
[self setDelegate:[aCoder decodeObjectForKey:CPPopoverDelegateKey]];
[self setBehavior:[aCoder decodeIntForKey:CPPopoverBehaviorKey]];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeInt:_appearance forKey:CPPopoverAppearanceKey];
[aCoder encodeBool:_animates forKey:CPPopoverAnimatesKey];
[aCoder encodeObject:_contentViewController forKey:CPPopoverContentViewControllerKey];
[aCoder encodeObject:_delegate forKey:CPPopoverDelegateKey];
[aCoder encodeInt:_behavior forKey:CPPopoverBehaviorKey];
}
@end
+109 -195
View File
@@ -23,27 +23,29 @@
@import "CGGeometry.j" @import "CGGeometry.j"
@import "CPImageView.j" @import "CPImageView.j"
@import "CPView.j" @import "CPView.j"
@import "CPWindow_Constants.j"
@typedef CPProgressIndicatorStyle
/* /*
@global @global
@group CPProgressIndicatorStyle @group CPProgressIndicatorStyle
*/ */
CPProgressIndicatorBarStyle = 0; CPProgressIndicatorBarStyle = 0;
/* /*
@global @global
@group CPProgressIndicatorStyle @group CPProgressIndicatorStyle
*/ */
CPProgressIndicatorSpinningStyle = 1; CPProgressIndicatorSpinningStyle = 1;
/* /*
@global @global
@group CPProgressIndicatorStyle @group CPProgressIndicatorStyle
*/ */
CPProgressIndicatorHUDBarStyle = 2; CPProgressIndicatorHUDBarStyle = 2;
var CPProgressIndicatorSpinningStyleColors = []; var CPProgressIndicatorSpinningStyleColors = nil,
CPProgressIndicatorClassName = nil,
CPProgressIndicatorStyleIdentifiers = nil,
CPProgressIndicatorStyleSizes = nil;
/*! /*!
@ingroup appkit @ingroup appkit
@@ -62,42 +64,77 @@ var CPProgressIndicatorSpinningStyleColors = [];
CPControlSize _controlSize; CPControlSize _controlSize;
BOOL _indeterminate; BOOL _isIndeterminate;
CPProgressIndicatorStyle _style; CPProgressIndicatorStyle _style;
BOOL _isAnimating; BOOL _isAnimating;
BOOL _isDisplayedWhenStoppedSet; BOOL _isDisplayedWhenStoppedSet;
BOOL _isDisplayedWhenStopped; BOOL _isDisplayedWhenStopped;
CPView _barView;
} }
+ (CPString)defaultThemeClass /*
@ignore
*/
+ (void)initialize
{ {
return @"progress-indicator"; if (self != [CPProgressIndicator class])
} return;
+ (CPDictionary)themeAttributes var bundle = [CPBundle bundleForClass:self];
{
return @{
@"indeterminate-bar-color": [CPNull null],
@"bar-color": [CPNull null],
@"default-height": 20,
@"bezel-color": [CPNull null],
@"spinning-mini-gif": [CPNull null],
@"spinning-small-gif": [CPNull null],
@"spinning-regular-gif": [CPNull null],
@"circular-border-color": [CPNull null],
@"circular-border-size": 1,
@"circular-color": [CPNull null]
};
}
+ (Class)_binderClassForBinding:(CPString)aBinding CPProgressIndicatorSpinningStyleColors = [];
{
if (aBinding === CPValueBinding || aBinding === @"isIndeterminate")
return [_CPProgressIndicatorBinder class];
return [super _binderClassForBinding:aBinding]; CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:
[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]];
CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:
[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]];
CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:
[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]];
CPProgressIndicatorBezelBorderViewPool = [];
var start = CPProgressIndicatorBarStyle,
end = CPProgressIndicatorHUDBarStyle;
for (; start <= end; ++start)
{
CPProgressIndicatorBezelBorderViewPool[start] = [];
CPProgressIndicatorBezelBorderViewPool[start][CPMiniControlSize] = [];
CPProgressIndicatorBezelBorderViewPool[start][CPSmallControlSize] = [];
CPProgressIndicatorBezelBorderViewPool[start][CPRegularControlSize] = [];
}
CPProgressIndicatorClassName = [self className];
CPProgressIndicatorStyleIdentifiers = [];
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] = @"Bar";
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorSpinningStyle] = @"Spinny";
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle] = @"HUDBar";
var regularIdentifier = _CPControlIdentifierForControlSize(CPRegularControlSize),
smallIdentifier = _CPControlIdentifierForControlSize(CPSmallControlSize),
miniIdentifier = _CPControlIdentifierForControlSize(CPMiniControlSize);
CPProgressIndicatorStyleSizes = [];
// Bar Style
var prefixes = [
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle],
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle]
];
for (var i = 0, count = prefixes.length; i < count; i++)
{
var prefix = prefixes[i];
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
}
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
@@ -116,7 +153,8 @@ var CPProgressIndicatorSpinningStyleColors = [];
_controlSize = CPRegularControlSize; _controlSize = CPRegularControlSize;
[self setNeedsLayout]; [self updateBackgroundColor];
[self drawBar];
} }
return self; return self;
@@ -278,12 +316,12 @@ var CPProgressIndicatorSpinningStyleColors = [];
Specifies whether this progress indicator should be indeterminate or display progress based on it's max and min. Specifies whether this progress indicator should be indeterminate or display progress based on it's max and min.
@param isDeterminate \c YES makes the indicator indeterminate @param isDeterminate \c YES makes the indicator indeterminate
*/ */
- (void)setIndeterminate:(BOOL)indeterminate - (void)setIndeterminate:(BOOL)isIndeterminate
{ {
if (_indeterminate == indeterminate) if (_isIndeterminate == isIndeterminate)
return; return;
_indeterminate = indeterminate; _isIndeterminate = isIndeterminate;
[self updateBackgroundColor]; [self updateBackgroundColor];
} }
@@ -293,7 +331,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
*/ */
- (BOOL)isIndeterminate - (BOOL)isIndeterminate
{ {
return _indeterminate; return _isIndeterminate;
} }
/*! /*!
@@ -307,8 +345,6 @@ var CPProgressIndicatorSpinningStyleColors = [];
_style = aStyle; _style = aStyle;
[self setTheme:(_style === CPProgressIndicatorHUDBarStyle) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
[self updateBackgroundColor]; [self updateBackgroundColor];
} }
@@ -320,7 +356,8 @@ var CPProgressIndicatorSpinningStyleColors = [];
if (_style == CPProgressIndicatorSpinningStyle) if (_style == CPProgressIndicatorSpinningStyle)
[self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]]; [self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]];
else else
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), [self valueForThemeAttribute:@"default-height"])]; [self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), CPProgressIndicatorStyleSizes[
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] + _CPControlIdentifierForControlSize(_controlSize)][0].height)];
} }
/*! /*!
@@ -370,129 +407,62 @@ var CPProgressIndicatorSpinningStyleColors = [];
/* @ignore */ /* @ignore */
- (void)drawBar - (void)drawBar
{ {
[self setNeedsLayout]; if (_style == CPProgressIndicatorSpinningStyle)
[self setNeedsDisplay:YES]; return;
}
- (CPView)createEphemeralSubviewNamed:(CPString)aName if (!_barView)
{
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
}
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
{
if (aViewName === @"bar-view" && _style !== CPProgressIndicatorSpinningStyle)
{ {
var width = CGRectGetWidth([self bounds]), _barView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 16.0)];
barWidth = width * ((_doubleValue - _minValue) / (_maxValue - _minValue)); [self addSubview:_barView];
if (barWidth > 0.0 && barWidth < 4.0)
barWidth = 4.0;
if (_indeterminate)
barWidth = width;
return CGRectMake(0, 0, barWidth, [self valueForThemeAttribute:@"default-height"]);
} }
return nil; [_barView setBackgroundColor:_CPControlThreePartImagePattern(
NO,
CPProgressIndicatorStyleSizes,
CPProgressIndicatorClassName,
@"Bar",
CPProgressIndicatorStyleIdentifiers[_style],
_CPControlIdentifierForControlSize(_controlSize))];
var width = CGRectGetWidth([self bounds]),
barWidth = width * ((_doubleValue - _minValue) / (_maxValue - _minValue));
if (barWidth > 0.0 && barWidth < 4.0)
barWidth = 4.0;
[_barView setFrameSize:CGSizeMake(barWidth, 16.0)];
} }
/* @ignore */ /* @ignore */
- (void)updateBackgroundColor - (void)updateBackgroundColor
{
if ([CPProgressIndicatorSpinningStyleColors count] === 0)
{
CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [self valueForThemeAttribute:@"spinning-mini-gif"];
CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [self valueForThemeAttribute:@"spinning-small-gif"];
CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [self valueForThemeAttribute:@"spinning-regular-gif"];
}
[self setNeedsLayout];
}
- (void)layoutSubviews
{ {
if (YES)//_isBezeled) if (YES)//_isBezeled)
{ {
if (_style == CPProgressIndicatorSpinningStyle) if (_style == CPProgressIndicatorSpinningStyle)
{ {
if (!_indeterminate) [_barView removeFromSuperview];
return;
// This will cause the bar view to go away due to having a nil rect when _style == CPProgressIndicatorSpinningStyle. _barView = nil;
[self layoutEphemeralSubviewNamed:"bar-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:nil];
[self setBackgroundColor:CPProgressIndicatorSpinningStyleColors[_controlSize]]; [self setBackgroundColor:CPProgressIndicatorSpinningStyleColors[_controlSize]];
} }
else else
{ {
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]]; [self setBackgroundColor:_CPControlThreePartImagePattern(
NO,
CPProgressIndicatorStyleSizes,
CPProgressIndicatorClassName,
@"BezelBorder",
CPProgressIndicatorStyleIdentifiers[_style],
_CPControlIdentifierForControlSize(_controlSize))];
var barView = [self layoutEphemeralSubviewNamed:"bar-view" [self drawBar];
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:nil];
if (_indeterminate)
[barView setBackgroundColor:[self currentValueForThemeAttribute:@"indeterminate-bar-color"]];
else
[barView setBackgroundColor:[self currentValueForThemeAttribute:@"bar-color"]];
} }
} }
else else
[self setBackgroundColor:nil]; [self setBackgroundColor:nil];
} }
- (void)drawRect:(CGRect)aRect
{
if (_style == CPProgressIndicatorSpinningStyle && !_indeterminate)
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
rect = CGRectMakeCopy(aRect),
borderSize = [self currentValueForThemeAttribute:@"circular-border-size"];
rect.origin.x += borderSize;
rect.origin.y += borderSize;
rect.size.width = rect.size.width - borderSize * 2;
rect.size.height = rect.size.height - borderSize * 2;
if ([self doubleValue] > [self minValue] && [self doubleValue] < [self maxValue])
{
var midX = CGRectGetMidX(rect),
midY = CGRectGetMidY(rect),
endAngle = Math.PI * 2 * (([self doubleValue] - [self minValue]) / ([self maxValue] - [self minValue])) - Math.PI / 2,
radius = MIN(rect.size.width / 2, rect.size.height / 2);
CGContextBeginPath(context);
CGContextSetLineWidth(context, borderSize);
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
CGContextMoveToPoint(context, midX, midY);
CGContextAddArc(context, midX, midY, radius, 3 * Math.PI / 2, endAngle, YES);
CGContextAddLineToPoint(context, midX, midY);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextStrokePath(context);
}
else if ([self doubleValue] == [self maxValue])
{
CGContextBeginPath(context);
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
CGContextAddEllipseInRect(context, rect);
CGContextClosePath(context);
CGContextFillPath(context);
}
CGContextBeginPath(context);
CGContextSetStrokeColor(context , [self currentValueForThemeAttribute:@"circular-border-color"]);
CGContextSetLineWidth(context, borderSize);
CGContextAddEllipseInRect(context, rect);
CGContextClosePath(context);
CGContextStrokePath(context);
}
}
@end @end
@@ -506,13 +476,11 @@ var CPProgressIndicatorSpinningStyleColors = [];
_maxValue = [aCoder decodeObjectForKey:@"_maxValue"]; _maxValue = [aCoder decodeObjectForKey:@"_maxValue"];
_doubleValue = [aCoder decodeObjectForKey:@"_doubleValue"]; _doubleValue = [aCoder decodeObjectForKey:@"_doubleValue"];
_controlSize = [aCoder decodeObjectForKey:@"_controlSize"]; _controlSize = [aCoder decodeObjectForKey:@"_controlSize"];
_indeterminate = [aCoder decodeObjectForKey:@"_indeterminate"]; _isIndeterminate = [aCoder decodeObjectForKey:@"_isIndeterminate"];
_style = [aCoder decodeIntForKey:@"_style"]; _style = [aCoder decodeObjectForKey:@"_style"];
_isAnimating = [aCoder decodeObjectForKey:@"_isAnimating"]; _isAnimating = [aCoder decodeObjectForKey:@"_isAnimating"];
_isDisplayedWhenStoppedSet = [aCoder decodeObjectForKey:@"_isDisplayedWhenStoppedSet"]; _isDisplayedWhenStoppedSet = [aCoder decodeObjectForKey:@"_isDisplayedWhenStoppedSet"];
_isDisplayedWhenStopped = [aCoder decodeObjectForKey:@"_isDisplayedWhenStopped"]; _isDisplayedWhenStopped = [aCoder decodeObjectForKey:@"_isDisplayedWhenStopped"];
[self updateBackgroundColor];
} }
return self; return self;
@@ -520,71 +488,17 @@ var CPProgressIndicatorSpinningStyleColors = [];
- (void)encodeWithCoder:(CPCoder)aCoder - (void)encodeWithCoder:(CPCoder)aCoder
{ {
// Don't encode the background colour. It can be recreated based on the flags
// and if encoded causes hardcoded image paths in the cib while just wasting space.
var backgroundColor = [self backgroundColor];
[self setBackgroundColor:nil];
[super encodeWithCoder:aCoder]; [super encodeWithCoder:aCoder];
[self setBackgroundColor:backgroundColor];
[aCoder encodeObject:_minValue forKey:@"_minValue"]; [aCoder encodeObject:_minValue forKey:@"_minValue"];
[aCoder encodeObject:_maxValue forKey:@"_maxValue"]; [aCoder encodeObject:_maxValue forKey:@"_maxValue"];
[aCoder encodeObject:_doubleValue forKey:@"_doubleValue"]; [aCoder encodeObject:_doubleValue forKey:@"_doubleValue"];
[aCoder encodeObject:_controlSize forKey:@"_controlSize"]; [aCoder encodeObject:_controlSize forKey:@"_controlSize"];
[aCoder encodeObject:_indeterminate forKey:@"_indeterminate"]; [aCoder encodeObject:_isIndeterminate forKey:@"_isIndeterminate"];
[aCoder encodeInt:_style forKey:@"_style"]; [aCoder encodeObject:_style forKey:@"_style"];
[aCoder encodeObject:_isAnimating forKey:@"_isAnimating"]; [aCoder encodeObject:_isAnimating forKey:@"_isAnimating"];
[aCoder encodeObject:_isDisplayedWhenStoppedSet forKey:@"_isDisplayedWhenStoppedSet"]; [aCoder encodeObject:_isDisplayedWhenStoppedSet forKey:@"_isDisplayedWhenStoppedSet"];
[aCoder encodeObject:_isDisplayedWhenStopped forKey:@"_isDisplayedWhenStopped"]; [aCoder encodeObject:_isDisplayedWhenStopped forKey:@"_isDisplayedWhenStopped"];
} }
@end @end
@implementation _CPProgressIndicatorBinder : CPBinder
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
{
var value = aBinding === CPValueBinding ? 0.0 : YES;
[self _setPlaceholder:value forMarker:CPMultipleValuesMarker isDefault:YES];
[self _setPlaceholder:value forMarker:CPNoSelectionMarker isDefault:YES];
[self _setPlaceholder:value forMarker:CPNotApplicableMarker isDefault:YES];
[self _setPlaceholder:value forMarker:CPNullMarker isDefault:YES];
}
- (id)valueForBinding:(CPString)aBinding
{
if (aBinding === CPValueBinding)
return [_source doubleValue];
else if (aBinding === @"isIndeterminate")
[_source isIndeterminate];
else
return [super valueForBinding:aBinding];
}
- (BOOL)_setValue:(id)aValue forBinding:(CPString)aBinding
{
if (aBinding === CPValueBinding)
[_source setDoubleValue:aValue];
else if (aBinding === @"isIndeterminate")
[_source setIndeterminate:aValue];
else
return NO;
return YES;
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
if (![self _setValue:aValue forBinding:aBinding])
[super setValue:aValue forBinding:aBinding];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
if (![self _setValue:aValue forBinding:aBinding])
[super setPlaceholderValue:aValue withMarker:aMarker forBinding:aBinding];
}
@end
+18 -225
View File
@@ -25,11 +25,6 @@
@import "CPButton.j" @import "CPButton.j"
@class CPRadioGroup
@global CPApp
CPRadioImageOffset = 4.0;
/*! /*!
@ingroup appkit @ingroup appkit
@@ -68,6 +63,9 @@ CPRadioImageOffset = 4.0;
option. option.
*/ */
CPRadioImageOffset = 4.0;
@implementation CPRadio : CPButton @implementation CPRadio : CPButton
{ {
CPRadioGroup _radioGroup; CPRadioGroup _radioGroup;
@@ -152,14 +150,6 @@ CPRadioImageOffset = 4.0;
[_radioGroup _setSelectedRadio:self]; [_radioGroup _setSelectedRadio:self];
} }
- (BOOL)sendAction:(SEL)anAction to:(id)anObject
{
[super sendAction:anAction to:anObject];
if (_radioGroup)
[CPApp sendAction:[_radioGroup action] to:[_radioGroup target] from:_radioGroup];
}
@end @end
var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey"; var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
@@ -183,143 +173,33 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
[aCoder encodeObject:_radioGroup forKey:CPRadioRadioGroupKey]; [aCoder encodeObject:_radioGroup forKey:CPRadioRadioGroupKey];
} }
- (CPImage)image
{
return [self currentValueForThemeAttribute:@"image"];
}
- (CPImage)alternateImage
{
return [self currentValueForThemeAttribute:@"image"];
}
- (BOOL)startTrackingAt:(CGPoint)aPoint
{
var startedTracking = [super startTrackingAt:aPoint];
[self highlight:YES];
return startedTracking;
}
@end @end
@implementation CPRadioGroup : CPObject @implementation CPRadioGroup : CPObject
{ {
CPArray _radios; CPSet _radios;
CPRadio _selectedRadio; CPRadio _selectedRadio;
BOOL _enabled @accessors(getter=enabled);
BOOL _hidden @accessors(getter=hidden);
id _target @accessors(property=target); id _target @accessors(property=target);
SEL _action @accessors(property=action); SEL _action @accessors(property=action);
} }
+ (void)initialize
{
if (self !== [CPRadioGroup class])
return;
[self exposeBinding:CPSelectedValueBinding];
[self exposeBinding:CPSelectedTagBinding];
[self exposeBinding:CPSelectedIndexBinding];
[self exposeBinding:CPEnabledBinding];
[self exposeBinding:CPHiddenBinding];
}
- (id)init - (id)init
{ {
self = [super init]; self = [super init];
if (self) if (self)
{ {
_radios = []; _radios = [CPSet set];
_selectedRadio = nil; _selectedRadio = nil;
_enabled = YES;
_hidden = NO;
} }
return self; return self;
} }
/*!
Selects the radio button at the given index within the group.
If index is -1, all of the radio buttons are turned off.
*/
- (void)selectRadioAtIndex:(int)index
{
if (index === -1)
[self _setSelectedRadio:nil];
else
{
var radio = [_radios objectAtIndex:index];
[self _setSelectedRadio:radio];
[radio setState:CPOnState];
}
}
/*!
Selects the first radio button within the group with the given tag.
If a radio button with the given tag is found, selects it
and returns YES. Otherwise returns NO.
*/
- (BOOL)selectRadioWithTag:(int)tag
{
var index = [_radios indexOfObjectPassingTest:function(radio)
{
return [radio tag] === tag;
}];
if (index !== CPNotFound)
{
[self selectRadioAtIndex:index];
return YES;
}
else
return NO;
}
/*!
Returns the CPRadio that is currently selected within the group,
or nil if none are selected.
*/
- (CPRadio)selectedRadio
{
return _selectedRadio;
}
/*!
Returns the index of the selected radio within the array of radio buttons.
Buttons are numbered going left to right and top to bottom. Returns -1
if no radio within the group is selected.
*/
- (int)selectedRadioIndex
{
return [_radios indexOfObject:_selectedRadio];
}
- (CPArray)radios
{
return _radios;
}
- (void)setEnabled:(BOOL)enabled
{
[_radios makeObjectsPerformSelector:@selector(setEnabled:) withObject:enabled];
}
- (void)setHidden:(BOOL)hidden
{
[_radios makeObjectsPerformSelector:@selector(setHidden:) withObject:hidden];
}
#pragma mark Private
- (void)_addRadio:(CPRadio)aRadio - (void)_addRadio:(CPRadio)aRadio
{ {
if ([_radios indexOfObject:aRadio] === CPNotFound) [_radios addObject:aRadio];
[_radios addObject:aRadio];
if ([aRadio state] === CPOnState) if ([aRadio state] === CPOnState)
[self _setSelectedRadio:aRadio]; [self _setSelectedRadio:aRadio];
@@ -333,30 +213,25 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
[_radios removeObject:aRadio]; [_radios removeObject:aRadio];
} }
/*!
Selects the first radio button within the group with the given tag.
If a radio button with the given tag is found, selects it
and returns YES. Otherwise returns NO.
*/
- (void)_selectRadioWithTitle:(CPString)aTitle
{
var index = [_radios indexOfObjectPassingTest:function(radio)
{
return [radio title] === aTitle;
}];
[self selectRadioAtIndex:index];
}
- (void)_setSelectedRadio:(CPRadio)aRadio - (void)_setSelectedRadio:(CPRadio)aRadio
{ {
if (_selectedRadio === aRadio) if (_selectedRadio === aRadio)
return; return;
[_selectedRadio setState:CPOffState]; [_selectedRadio setState:CPOffState];
_selectedRadio = aRadio; _selectedRadio = aRadio;
[_CPRadioGroupSelectionBinder _reverseSetValueFromExclusiveBinderForObject:self];
[CPApp sendAction:_action to:_target from:self];
}
- (CPRadio)selectedRadio
{
return _selectedRadio;
}
- (CPArray)radios
{
return [_radios allObjects];
} }
@end @end
@@ -386,85 +261,3 @@ var CPRadioGroupRadiosKey = @"CPRadioGroupRadiosKey",
} }
@end @end
@implementation CPRadioGroup (BindingSupport)
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding === CPSelectedValueBinding ||
aBinding === CPSelectedTagBinding ||
aBinding === CPSelectedIndexBinding)
{
var capitalizedBinding = aBinding.charAt(0).toUpperCase() + aBinding.substr(1);
return [CPClassFromString(@"_CPRadioGroup" + capitalizedBinding + "Binder") class];
}
else if ([aBinding hasPrefix:CPEnabledBinding])
return [CPMultipleValueAndBinding class];
else if ([aBinding hasPrefix:CPHiddenBinding])
return [CPMultipleValueOrBinding class];
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return (aBinding == CPSelectedIndexBinding ||
aBinding == CPSelectedTagBinding ||
aBinding == CPSelectedValueBinding);
}
@end
@implementation _CPRadioGroupSelectionBinder : CPBinder
{
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
[self setValue:aValue forBinding:aBinding];
}
@end
@implementation _CPRadioGroupSelectedIndexBinder : _CPRadioGroupSelectionBinder
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source selectRadioAtIndex:aValue];
}
- (id)valueForBinding:(CPString)aBinding
{
return [_source selectedRadioIndex];
}
@end
@implementation _CPRadioGroupSelectedTagBinder : _CPRadioGroupSelectionBinder
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source selectRadioWithTag:aValue];
}
- (id)valueForBinding:(CPString)aBinding
{
return [[_source selectedRadio] tag];
}
@end
@implementation _CPRadioGroupSelectedValueBinder : _CPRadioGroupSelectionBinder
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source _selectRadioWithTitle:aValue];
}
- (id)valueForBinding:(CPString)aBinding
{
return [[_source selectedRadio] title];
}
@end
+1 -19
View File
@@ -21,13 +21,7 @@
*/ */
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPObjJRuntime.j>
@import "CPEvent.j"
@import "CPCursor.j"
@class CPKeyBinding
@class CPMenu
CPDeleteKeyCode = 8; CPDeleteKeyCode = 8;
CPTabKeyCode = 9; CPTabKeyCode = 9;
@@ -201,18 +195,6 @@ CPDeleteForwardKeyCode = 46;
[_nextResponder performSelector:_cmd withObject:anEvent]; [_nextResponder performSelector:_cmd withObject:anEvent];
} }
/*!
Notifies the receiver that the mouse entered the receiver's area and that it can adapt the cursor.
@param anEvent contains information about the exit
*/
- (void)cursorUpdate:(CPEvent)anEvent
{
if (_nextResponder)
[_nextResponder performSelector:_cmd withObject:anEvent];
else
[[CPCursor arrowCursor] set];
}
/*! /*!
Notifies the receiver that the mouse scroll wheel has moved. Notifies the receiver that the mouse scroll wheel has moved.
@param anEvent information about the scroll @param anEvent information about the scroll
@@ -370,7 +352,7 @@ CPDeleteForwardKeyCode = 46;
var CPResponderNextResponderKey = @"CPResponderNextResponderKey", var CPResponderNextResponderKey = @"CPResponderNextResponderKey",
CPResponderMenuKey = @"CPResponderMenuKey"; CPResponderMenuKey = @"CPResponderMenuKey";
@implementation CPResponder (CPCoding) <CPCoding> @implementation CPResponder (CPCoding)
/*! /*!
Initializes the responder with data from a coder. Initializes the responder with data from a coder.
+33 -86
View File
@@ -82,22 +82,6 @@
} }
/*! @cond */ /*! @cond */
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding == CPValueBinding)
return [CPPredicateEditorValueBinder class];
return [super _binderClassForBinding:aBinding];
}
- (CPString)_replacementKeyPathForBinding:(CPString)aBinding
{
if (aBinding == CPValueBinding)
return @"predicate";
return [super _replacementKeyPathForBinding:aBinding];
}
- (void)_initRuleEditorShared - (void)_initRuleEditorShared
{ {
[super _initRuleEditorShared]; [super _initRuleEditorShared];
@@ -173,40 +157,28 @@
return trees; return trees;
} }
- (CPMutableArray)_mergeTree:(CPArray)aTree - (id)_mergeTree:(id)tree
{ {
var mergedTree = [CPMutableArray array]; var merged = [CPMutableArray array],
if (aTree == nil) titles = [CPMutableArray array],
return mergedTree; count = [tree count];
var icount = [aTree count]; for (var i = 0; i < count; i++)
for (var i = 0; i < icount; i++)
{ {
var anode = [aTree objectAtIndex:i], var t = tree[i],
jcount = [mergedTree count], title = [CPString stringWithString:[t title]];
merged = NO;
for (var j = 0; j < jcount; j++) if ([titles containsObject:title])
{ {
var mergednode = [mergedTree objectAtIndex:j]; CPLogConsole("CPPredicateEditor does not support templates merging yet. Ignoring duplicate template: " + [t description]);
continue;
if ([[mergednode title] isEqualToString:[anode title]])
{
var children1 = [mergednode children],
children2 = [anode children],
children12 = [children1 arrayByAddingObjectsFromArray:children2],
mergedChildren = [self _mergeTree:children12];
[mergednode setChildren:mergedChildren];
merged = YES;
}
} }
if (!merged) [merged addObject:t];
[mergedTree addObject:anode]; [titles addObject:title];
} }
return mergedTree; return merged;
} }
- (id)_constructTreeForTemplate:(CPPredicateEditorRowTemplate)aTemplate - (id)_constructTreeForTemplate:(CPPredicateEditorRowTemplate)aTemplate
@@ -220,7 +192,7 @@
var children = [CPArray array], var children = [CPArray array],
itemsCount = 0, itemsCount = 0,
menuIndex = -1, menuIndex = -1,
itemArray, itemsArray,
templateView = [templateViews objectAtIndex:count], templateView = [templateViews objectAtIndex:count],
isPopup = [templateView isKindOfClass:[CPPopUpButton class]]; isPopup = [templateView isKindOfClass:[CPPopUpButton class]];
@@ -228,7 +200,7 @@
if (isPopup) if (isPopup)
{ {
itemArray = [[templateView itemArray] valueForKey:@"title"]; itemArray = [[templateView itemArray] valueForKey:@"title"];
itemsCount = [itemArray count]; itemsCount = [itemArray count],
menuIndex = 0; menuIndex = 0;
} }
@@ -255,19 +227,14 @@
- (void)setObjectValue:(id)objectValue - (void)setObjectValue:(id)objectValue
{ {
var ov = [self objectValue]; if (![[objectValue predicateFormat] isEqualToString:[[super predicate] predicateFormat]]) // ??
if ((ov == nil) != (objectValue == nil) || ![ov isEqual:objectValue])
{
[self _setPredicate:objectValue];
[self _reflectPredicate:objectValue]; [self _reflectPredicate:objectValue];
}
} }
- (void)_reflectPredicate:(id)predicate - (void)_reflectPredicate:(id)predicate
{ {
var animation = _currentAnimation; var animation = _currentAnimation;
_currentAnimation = nil; _currentAnimation = nil;
_sendAction = NO;
if (predicate != nil) if (predicate != nil)
{ {
@@ -344,9 +311,9 @@
treeChild = [rootItems objectAtIndex:i]; treeChild = [rootItems objectAtIndex:i];
var currentView = [templateViews objectAtIndex:[treeChild indexIntoTemplate]], var currentView = [templateViews objectAtIndex:[treeChild indexIntoTemplate]],
title = [treeChild title]; menuItemIndex = [treeChild menuItemIndex];
if (title == nil || [title isEqual:[currentView titleOfSelectedItem]]) if (menuItemIndex == -1 || [[treeChild title] isEqual:[currentView titleOfSelectedItem]])
{ {
var node = [_CPPredicateEditorRowNode rowNodeFromTree:treeChild]; var node = [_CPPredicateEditorRowNode rowNodeFromTree:treeChild];
[node applyTemplate:aTemplate withViews:templateViews forOriginalTemplate:originalTemplate]; [node applyTemplate:aTemplate withViews:templateViews forOriginalTemplate:originalTemplate];
@@ -360,11 +327,7 @@
rootItems = [treeChild children]; rootItems = [treeChild children];
} }
var row = @{ var row = [CPDictionary dictionaryWithObjectsAndKeys:criteria, @"criteria", values, @"displayValues", rowType, @"rowType"];
@"criteria": criteria,
@"displayValues": values,
@"rowType": rowType,
};
return row; return row;
} }
@@ -373,15 +336,16 @@
- (void)_updatePredicate - (void)_updatePredicate
{ {
[self willChangeValueForKey:@"objectValue"];
[self _updatePredicateFromRows]; [self _updatePredicateFromRows];
[self didChangeValueForKey:@"objectValue"];
} }
- (void)_updatePredicateFromRows - (void)_updatePredicateFromRows
{ {
var rootRowsArray = [super _rootRowsArray], var rootRowsArray = [super _rootRowsArray],
subpredicates = [CPMutableArray array], subpredicates = [CPMutableArray array],
count, count = count2 = [rootRowsArray count],
count2 = count = [rootRowsArray count],
predicate; predicate;
while (count--) while (count--)
@@ -398,7 +362,7 @@
else else
predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:subpredicates]; predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:subpredicates];
[self _setPredicate:predicate]; [super _setPredicate:predicate];
} }
- (id)_predicateFromRowItem:(id)rowItem - (id)_predicateFromRowItem:(id)rowItem
@@ -409,13 +373,12 @@
if (rowType == CPRuleEditorRowTypeCompound) if (rowType == CPRuleEditorRowTypeCompound)
{ {
var subrows = [rowItem valueForKey:_subrowsArrayKeyPath], var subrows = [rowItem valueForKey:_subrowsArrayKeyPath],
count = [subrows count]; count = [subrows count];
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
var subrow = [subrows objectAtIndex:i], var subrow = [subrows objectAtIndex:i];
predicate = [self _predicateFromRowItem:subrow]; var predicate = [self _predicateFromRowItem:subrow];
[subpredicates addObject:predicate]; [subpredicates addObject:predicate];
} }
} }
@@ -451,6 +414,7 @@
- (void)_sendRuleAction - (void)_sendRuleAction
{ {
[self _updatePredicate];
[super _sendRuleAction]; [super _sendRuleAction];
} }
@@ -484,7 +448,7 @@
#pragma mark RuleEditor delegate methods #pragma mark RuleEditor delegate methods
- (int)_queryNumberOfChildrenOfItem:(id)rowItem withRowType:(CPRuleEditorRowType)type - (int)_queryNumberOfChildrenOfItem:(id)rowItem withRowType:(int)type
{ {
if (rowItem == nil) if (rowItem == nil)
{ {
@@ -494,7 +458,7 @@
return [[rowItem children] count]; return [[rowItem children] count];
} }
- (id)_queryChild:(int)childIndex ofItem:(id)rowItem withRowType:(CPRuleEditorRowType)type - (id)_queryChild:(int)childIndex ofItem:(id)rowItem withRowType:(int)type
{ {
if (rowItem == nil) if (rowItem == nil)
{ {
@@ -505,7 +469,7 @@
return [[rowItem children] objectAtIndex:childIndex]; return [[rowItem children] objectAtIndex:childIndex];
} }
- (id)_queryValueForItem:(id)rowItem inRow:(CPInteger)rowIndex - (id)_queryValueForItem:(id)rowItem inRow:(int)rowIndex
{ {
return [rowItem displayValue]; return [rowItem displayValue];
} }
@@ -516,14 +480,12 @@ var CPPredicateTemplatesKey = @"CPPredicateTemplates";
@implementation CPPredicateEditor (CPCoding) @implementation CPPredicateEditor (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder - (id)initWithCoder:(id)aCoder
{ {
self = [super initWithCoder:aCoder]; self = [super initWithCoder:aCoder];
if (self != nil) if (self != nil)
{ {
var nibTemplates = [aCoder decodeObjectForKey:CPPredicateTemplatesKey]; var nibTemplates = [aCoder decodeObjectForKey:CPPredicateTemplatesKey];
if (nibTemplates != nil) if (nibTemplates != nil)
[self setRowTemplates:nibTemplates]; [self setRowTemplates:nibTemplates];
} }
@@ -531,7 +493,7 @@ var CPPredicateTemplatesKey = @"CPPredicateTemplates";
return self; return self;
} }
- (void)encodeWithCoder:(CPCoder)aCoder - (void)encodeWithCoder:(id)aCoder
{ {
[super encodeWithCoder:aCoder]; [super encodeWithCoder:aCoder];
[aCoder encodeObject:_allTemplates forKey:CPPredicateTemplatesKey]; [aCoder encodeObject:_allTemplates forKey:CPPredicateTemplatesKey];
@@ -539,19 +501,4 @@ var CPPredicateTemplatesKey = @"CPPredicateTemplates";
@end @end
@implementation CPPredicateEditorValueBinder : CPBinder /*! @endcond */
{
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
[_source _reflectPredicate:nil];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source _reflectPredicate:aValue];
}
@end
/*! @endcond */
@@ -20,27 +20,17 @@
* 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/CPPredicate.j> CPUndefinedAttributeType = 0,
CPInteger16AttributeType = 100,
@import "CPCheckBox.j" CPInteger32AttributeType = 200,
@import "CPPopUpButton.j" CPInteger64AttributeType = 300,
@import "CPMenuItem.j" CPDecimalAttributeType = 400,
@import "CPTextField.j" CPDoubleAttributeType = 500,
CPFloatAttributeType = 600,
@import "CPDatePicker.j" CPStringAttributeType = 700,
CPBooleanAttributeType = 800,
CPDateAttributeType = 900,
CPUndefinedAttributeType = 0; CPBinaryDataAttributeType = 1000,
CPInteger16AttributeType = 100;
CPInteger32AttributeType = 200;
CPInteger64AttributeType = 300;
CPDecimalAttributeType = 400;
CPDoubleAttributeType = 500;
CPFloatAttributeType = 600;
CPStringAttributeType = 700;
CPBooleanAttributeType = 800;
CPDateAttributeType = 900;
CPBinaryDataAttributeType = 1000;
CPTransformableAttributeType = 1800; CPTransformableAttributeType = 1800;
@implementation CPPredicateEditorRowTemplate : CPObject @implementation CPPredicateEditorRowTemplate : CPObject
@@ -195,8 +185,7 @@ CPTransformableAttributeType = 1800;
if (![[self operators] containsObject:[predicate predicateOperatorType]]) if (![[self operators] containsObject:[predicate predicateOperatorType]])
return 0; return 0;
if (!_rightIsWildcard && ![[self rightExpressions] containsObject:[predicate rightExpression]]) if (!_rightIsWildcard && ![[self rightExpressions] containsObject:[predicate rightExpression]]) return 0;
return 0;
return 1; return 1;
} }
@@ -291,7 +280,7 @@ CPTransformableAttributeType = 1800;
*/ */
- (CPArray)leftExpressions - (CPArray)leftExpressions
{ {
if (_templateType == 1 && !_leftIsWildcard) if (_templateType ==1 && !_leftIsWildcard)
{ {
var view = [_views objectAtIndex:0]; var view = [_views objectAtIndex:0];
return [[view itemArray] valueForKey:@"representedObject"]; return [[view itemArray] valueForKey:@"representedObject"];
@@ -471,11 +460,9 @@ CPTransformableAttributeType = 1800;
var value; var value;
if (attributeType >= CPInteger16AttributeType && attributeType <= CPFloatAttributeType) if (attributeType >= CPInteger16AttributeType && attributeType <= CPFloatAttributeType)
value = [aView floatValue]; value = [aView intValue];
else if (attributeType == CPBooleanAttributeType) else if (attributeType == CPBooleanAttributeType)
value = [aView state]; value = [aView state];
else if (attributeType == CPDateAttributeType)
value = [aView dateValue];
else else
value = [aView stringValue]; value = [aView stringValue];
@@ -489,36 +476,7 @@ CPTransformableAttributeType = 1800;
- (id)copy - (id)copy
{ {
var views = [CPArray array], return [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:self]];
copy = [[[self class] alloc] init];
[copy _setTemplateType:_templateType];
[copy _setOptions:_predicateOptions];
[copy _setModifier:_predicateModifier];
[copy _setLeftAttributeType:_leftAttributeType];
[copy _setRightAttributeType:_rightAttributeType];
[copy setLeftIsWildcard:_leftIsWildcard];
[copy setRightIsWildcard:_rightIsWildcard];
[_views enumerateObjectsUsingBlock:function(aView, idx, stop)
{
var vcopy;
if ([aView implementsSelector:@selector(copy)])
{
vcopy = [aView copy];
}
else
{
vcopy = [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:aView]];
}
[views addObject:vcopy];
}];
[copy setTemplateViews:views];
return copy;
} }
+ (id)_operatorsForAttributeType:(CPAttributeType)attributeType + (id)_operatorsForAttributeType:(CPAttributeType)attributeType
@@ -624,7 +582,7 @@ CPTransformableAttributeType = 1800;
- (CPPopUpButton)_viewFromExpressions:(CPArray)expressions - (CPPopUpButton)_viewFromExpressions:(CPArray)expressions
{ {
var popup = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 100, 18)], var popup = [[CPPopUpButton alloc] initWithFrame:CPMakeRect(0, 0, 100, 18)],
count = [expressions count]; count = [expressions count];
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
@@ -710,10 +668,7 @@ CPTransformableAttributeType = 1800;
view = [[CPCheckBox alloc] initWithFrame:CGRectMake(0, 0, 50, 26)]; view = [[CPCheckBox alloc] initWithFrame:CGRectMake(0, 0, 50, 26)];
} }
else if (attributeType == CPDateAttributeType) else if (attributeType == CPDateAttributeType)
{ view = [[CPDatePicker alloc] initWithFrame:CGRectMake(0, 0, 150, 26)];
view = [[CPDatePicker alloc] initWithFrame:CGRectMake(0, 0, 180, 26)];
[view setDatePickerElements:CPYearMonthDayDatePickerElementFlag];
}
else else
return nil; return nil;
@@ -730,17 +685,16 @@ CPTransformableAttributeType = 1800;
[textField setBordered:YES]; [textField setBordered:YES];
[textField setEditable:YES]; [textField setEditable:YES];
[textField setFont:[CPFont systemFontOfSize:10]]; [textField setFont:[CPFont systemFontOfSize:10]];
[textField setSendsActionOnEndEditing:YES];
return textField; return textField;
} }
- (void)_setOptions:(unsigned)options - (void)_setOptions:(unsigned int)options
{ {
_predicateOptions = options; _predicateOptions = options;
} }
- (void)_setModifier:(unsigned)modifier - (void)_setModifier:(unsigned int)modifier
{ {
_predicateModifier = modifier; _predicateModifier = modifier;
} }
@@ -831,30 +785,4 @@ var CPPredicateTemplateTypeKey = @"CPPredicateTemplateType",
} }
@end @end
/*! @endcond */
// Copy support for built-in types
@implementation CPDatePicker (CPCopying)
- (id)copy
{
var ret = [[[self class] alloc] initWithFrame:[self frame]];
[ret setTextFont:[self textFont]];
[ret setMinDate:[self minDate]];
[ret setMaxDate:[self maxDate]];
[ret setTimeInterval:[self timeInterval]];
[ret setDatePickerMode:[self datePickerMode]];
[ret setDatePickerElements:[self datePickerElements]];
[ret setDatePickerStyle:[self datePickerStyle]];
[ret setLocale:[self locale]];
[ret setDateValue:[self dateValue]];
[ret setBackgroundColor:[self backgroundColor]];
[ret setDrawsBackground:[self drawsBackground]];
[ret setBordered:[self isBordered]];
[ret _init];
return ret;
}
@end
/*! @endcond */
File diff suppressed because it is too large Load Diff
@@ -1,42 +0,0 @@
/*
* CPRuleEditor_Constants.j
* AppKit
*
* Created by cacaodev.
* Copyright 2011, cacaodev.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
CPRuleEditorPredicateLeftExpression = "CPRuleEditorPredicateLeftExpression";
CPRuleEditorPredicateRightExpression = "CPRuleEditorPredicateRightExpression";
CPRuleEditorPredicateComparisonModifier = "CPRuleEditorPredicateComparisonModifier";
CPRuleEditorPredicateOptions = "CPRuleEditorPredicateOptions";
CPRuleEditorPredicateOperatorType = "CPRuleEditorPredicateOperatorType";
CPRuleEditorPredicateCustomSelector = "CPRuleEditorPredicateCustomSelector";
CPRuleEditorPredicateCompoundType = "CPRuleEditorPredicateCompoundType";
CPRuleEditorRowsDidChangeNotification = "CPRuleEditorRowsDidChangeNotification";
CPRuleEditorRulesDidChangeNotification = "CPRuleEditorRulesDidChangeNotification";
CPRuleEditorNestingModeSingle = 0; // Only a single row is allowed. Plus/minus buttons will not be shown
CPRuleEditorNestingModeList = 1; // Allows a single list, with no nesting and no compound rows
CPRuleEditorNestingModeCompound = 2; // Unlimited nesting and compound rows; this is the default
CPRuleEditorNestingModeSimple = 3; // One compound row at the top with subrows beneath it, and no further nesting allowed
@typedef CPRuleEditorRowType
CPRuleEditorRowTypeSimple = 0;
CPRuleEditorRowTypeCompound = 1;
+23 -36
View File
@@ -1,25 +1,9 @@
/* /*
* Created by cacaodev@gmail.com. * Created by cacaodev@gmail.com.
* Copyright (c) 2011 Pear, Inc. All rights reserved. * Copyright (c) 2011 Pear, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import <Foundation/CPObject.j> @class _CPPredicateEditorTree;
@import <Foundation/CPString.j>
@implementation _CPPredicateEditorRowNode : CPObject @implementation _CPPredicateEditorRowNode : CPObject
{ {
_CPPredicateEditorTree tree @accessors; _CPPredicateEditorTree tree @accessors;
@@ -30,21 +14,25 @@
+ (id)rowNodeFromTree:(id)aTree + (id)rowNodeFromTree:(id)aTree
{ {
return [_CPPredicateEditorRowNode _rowNodeFromTree:aTree withTemplateTable:{}]; var mapTable = {};
return [_CPPredicateEditorRowNode _rowNodeFromTree:aTree withTemplateTable:mapTable];
} }
+ (id)_rowNodeFromTree:(id)aTree withTemplateTable:(id)templateTable + (id)_rowNodeFromTree:(id)aTree withTemplateTable:(id)templateTable
{ {
var views, var node,
views,
copiedContainer; copiedContainer;
var uuid = [[aTree template] UID], node = [[_CPPredicateEditorRowNode alloc] init];
cachedNode = templateTable[uuid], node.tree = aTree;
node = [[_CPPredicateEditorRowNode alloc] init];
[node setTree:aTree]; var template = [aTree template],
uuid = [template UID];
if (!cachedNode) var cachedNode = templateTable[uuid];
if (cachedNode == nil)
{ {
views = [CPMutableArray array]; views = [CPMutableArray array];
copiedContainer = [CPMutableArray array]; copiedContainer = [CPMutableArray array];
@@ -56,8 +44,8 @@
copiedContainer = [cachedNode copiedTemplateContainer]; copiedContainer = [cachedNode copiedTemplateContainer];
} }
[node setTemplateViews:views]; node.templateViews = views;
[node setCopiedTemplateContainer:copiedContainer]; node.copiedTemplateContainer = copiedContainer;
var nodeChildren = [CPMutableArray array], var nodeChildren = [CPMutableArray array],
treeChildren = [aTree children], treeChildren = [aTree children],
@@ -78,9 +66,7 @@
- (BOOL)applyTemplate:(id)template withViews:(id)views forOriginalTemplate:(id)originalTemplate - (BOOL)applyTemplate:(id)template withViews:(id)views forOriginalTemplate:(id)originalTemplate
{ {
var t = [tree template], var t = [tree template];
count = [children count];
if (t !== template) if (t !== template)
{ {
[templateViews setArray:views]; [templateViews setArray:views];
@@ -88,7 +74,8 @@
[copiedTemplateContainer addObject:template]; [copiedTemplateContainer addObject:template];
} }
for (var i = 0; i < count; i++) var count = [children count];
for (var i; i < count; i++)
[children[i] applyTemplate:template withViews:views forOriginalTemplate:originalTemplate]; [children[i] applyTemplate:template withViews:views forOriginalTemplate:originalTemplate];
} }
@@ -102,8 +89,9 @@
- (void)copyTemplateIfNecessary - (void)copyTemplateIfNecessary
{ {
if ([copiedTemplateContainer count] === 0) if ([copiedTemplateContainer count] == 0)
{ {
CPLogConsole("COPYING TEMPLATE");
var copy = [[tree template] copy]; var copy = [[tree template] copy];
[copiedTemplateContainer addObject:copy]; [copiedTemplateContainer addObject:copy];
[templateViews addObjectsFromArray:[copy templateViews]]; [templateViews addObjectsFromArray:[copy templateViews]];
@@ -130,8 +118,7 @@
- (id)displayValue - (id)displayValue
{ {
var title = [self title]; var title = [self title];
if (title != nil)
if (title !== nil)
return title; return title;
return [self templateView]; return [self templateView];
@@ -142,4 +129,4 @@
return [CPString stringWithFormat:@"<%@ %@ %@ tree:%@ tviews:%@", [self className],[self UID], [self title], [tree UID], [templateViews description]]; return [CPString stringWithFormat:@"<%@ %@ %@ tree:%@ tviews:%@", [self className],[self UID], [self title], [tree UID], [templateViews description]];
} }
@end @end
+2 -21
View File
@@ -1,27 +1,8 @@
/* /*
* Created by cacaodev@gmail.com. * Created by cacaodev@gmail.com.
* Copyright (c) 2011 Pear, Inc. All rights reserved. * Copyright (c) 2011 Pear, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import <Foundation/CPObject.j>
@import <Foundation/CPString.j>
@class CPPredicateEditorRowTemplate
@implementation _CPPredicateEditorTree : CPObject @implementation _CPPredicateEditorTree : CPObject
{ {
CPPredicateEditorRowTemplate template @accessors; CPPredicateEditorRowTemplate template @accessors;
+13 -35
View File
@@ -1,35 +1,19 @@
/* /*
* Created by cacaodev@gmail.com. * Created by cacaodev@gmail.com.
* Copyright (c) 2011 Pear, Inc. All rights reserved. * Copyright (c) 2011 Pear, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPDictionary.j> @import <Foundation/CPDictionary.j>
@import <Foundation/CPString.j> @import <Foundation/CPString.j>
@import <Foundation/CPURLConnection.j>
@import <Foundation/CPURLRequest.j>
var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)?"); var regex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)?");
@implementation _CPRuleEditorLocalizer : CPObject @implementation _CPRuleEditorLocalizer : CPObject
{ {
CPDictionary _dictionary @accessors(property=dictionary); CPDictionary _dictionary @accessors(property=dictionary);
CPURLConnection connection; CPURLConnection connection;
CPURLRequest request; CPURLRequest resquest;
} }
- (void)loadContentOfURL:(CPURL)aURL - (void)loadContentOfURL:(CPURL)aURL
@@ -40,10 +24,9 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)
- (void)reloadIfNeeded - (void)reloadIfNeeded
{ {
if (connection !== nil) // Connection waiting if (connection != nil) // Connection waiting
{ {
connection = nil; connection = nil;
var data = [CPURLConnection sendSynchronousRequest:request returningResponse:NULL]; var data = [CPURLConnection sendSynchronousRequest:request returningResponse:NULL];
[self loadContent:[data rawString]]; [self loadContent:[data rawString]];
} }
@@ -51,7 +34,7 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)rawString - (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)rawString
{ {
if (connection !== nil && rawString !== nil) if (connection != nil && rawString != nil)
[self loadContent:rawString]; [self loadContent:rawString];
connection = nil; connection = nil;
@@ -59,19 +42,17 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)
- (void)loadContent:(CPString)aContent - (void)loadContent:(CPString)aContent
{ {
var dict = @{}, var dict = [CPDictionary dictionary],
lines = [aContent componentsSeparatedByString:"\n"], lines = [aContent componentsSeparatedByString:"\n"],
count = [lines count]; count = [lines count];
for (var i = 0 ; i < count ; i++) for (var i = 0 ; i < count ; i++)
{ {
var line = [lines objectAtIndex:i]; var line = [lines objectAtIndex:i];
if (line.length > 1) if (line.length > 1)
{ {
var match = LocalizerStringsRegex.exec(line); var match = regex.exec(line);
if (match.length >= 3)
if (match && match.length >= 3)
[dict setObject:match[2] forKey:match[1]]; [dict setObject:match[2] forKey:match[1]];
} }
} }
@@ -83,15 +64,12 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)
{ {
[self reloadIfNeeded]; [self reloadIfNeeded];
if (_dictionary !== nil && aString !== nil) if (_dictionary != nil && aString != nil)
{ {
var localized = [_dictionary objectForKey:aString]; var localized = [_dictionary objectForKey:aString];
if (localized != nil)
if (localized !== nil) return localized;
return localized;
} }
return aString; return aString;
} }
@end
@@ -0,0 +1,157 @@
/*
* 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
+21 -39
View File
@@ -1,35 +1,17 @@
/* /*
* Created by cacaodev@gmail.com. * Created by cacaodev@gmail.com.
* Copyright (c) 2011 Pear, Inc. All rights reserved. * Copyright (c) 2011 Pear, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "CPView.j"
@class CPRuleEditor
@implementation _CPRuleEditorViewSlice : CPView @implementation _CPRuleEditorViewSlice : CPView
{ {
CPRuleEditor _ruleEditor; CPRuleEditor _ruleEditor;
int _indentation @accessors(property=indentation); int _indentation @accessors(property=indentation);
int _rowIndex @accessors(property=rowIndex); int _rowIndex @accessors(property=rowIndex);
CGRect _animationTargetRect @accessors(property=_animationTargetRect); CPRect _animationTargetRect @accessors(property=_animationTargetRect);
BOOL _selected @accessors(getter=_isSelected, setter=_setSelected:); BOOL _selected @accessors(getter=_isSelected, setter=_setSelected:);
BOOL _lastSelected @accessors(getter=_isLastSelected, setter=_setLastSelected:); BOOL _lastSelected @accessors(getter=_isLastSelected, setter=_setLastSelected:);
BOOL _editable @accessors(getter=isEditable, setter=setEditable:); CPColor _backgroundColor @accessors(property=backgroundColor);
} }
- (void)removeFromSuperview - (void)removeFromSuperview
@@ -54,19 +36,19 @@
if (select == _selected) if (select == _selected)
return; return;
var selector = select ? @selector(setThemeState:) : @selector(unsetThemeState:); var selector = select ? @"setThemeState:" : @"unsetThemeState:";
[[self subviews] makeObjectsPerformSelector:selector withObject:CPThemeStateSelectedDataView]; [[self subviews] makeObjectsPerformSelector:CPSelectorFromString(selector) withObject:CPThemeStateSelectedDataView];
_selected = select; _selected = select;
} }
- (void)drawRect:(CGRect)rect - (void)drawRect:(CPRect)rect
{ {
var context = [[CPGraphicsContext currentContext] graphicsPort], var context = [[CPGraphicsContext currentContext] graphicsPort],
bounds = [self bounds], bounds = [self bounds],
maxX = CGRectGetWidth(bounds), maxX = CGRectGetWidth(bounds) - 2,
maxY = CGRectGetHeight(bounds); maxY = CGRectGetHeight(bounds);
// Draw background // Draw background
if ([self _isSelected]) if ([self _isSelected])
_backgroundColor = [_ruleEditor _selectedRowColor]; _backgroundColor = [_ruleEditor _selectedRowColor];
else else
@@ -79,33 +61,33 @@
CGContextSetFillColor(context, _backgroundColor); CGContextSetFillColor(context, _backgroundColor);
CGContextFillRect(context, rect); CGContextFillRect(context, rect);
// Draw Top Border // Draw Top Border
CGContextBeginPath(context); CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, 0); CGContextMoveToPoint(context, 1, 0);
CGContextAddLineToPoint(context, maxX, 0); CGContextAddLineToPoint(context, maxX, 0);
CGContextClosePath(context);
CGContextSetStrokeColor(context, [_ruleEditor _sliceTopBorderColor]); CGContextSetStrokeColor(context, [_ruleEditor _sliceTopBorderColor]);
CGContextStrokePath(context); CGContextStrokePath(context);
// Draw Bottom Border // Draw Bottom Border
CGContextBeginPath(context); CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, maxY); CGContextMoveToPoint(context, 1, maxY - 0.5);
CGContextAddLineToPoint(context, maxX, maxY); CGContextAddLineToPoint(context, maxX, maxY - 0.5);
CGContextClosePath(context);
var bottomColor = (_rowIndex == [_ruleEditor _lastRow]) ? [_ruleEditor _sliceLastBottomBorderColor] : [_ruleEditor _sliceBottomBorderColor]; var bottomColor = (_rowIndex == [_ruleEditor _lastRow]) ? [_ruleEditor _sliceLastBottomBorderColor] : [_ruleEditor _sliceBottomBorderColor];
CGContextSetStrokeColor(context, bottomColor); CGContextSetStrokeColor(context, bottomColor);
CGContextStrokePath(context); CGContextStrokePath(context);
} }
- (void)mouseDown:(CPEvent)theEvent - (void)mouseDown:(CPEvent)theEvent
{ {
if (_editable) if (editable)
[_ruleEditor _mouseDownOnSlice:self withEvent:theEvent]; [_ruleEditor _mouseDownOnSlice:self withEvent:theEvent];
} }
- (void)mouseUp:(CPEvent)theEvent - (void)mouseUp:(CPEvent)theEvent
{ {
if (_editable) if (editable)
[_ruleEditor _mouseUpOnSlice:self withEvent:theEvent]; [_ruleEditor _mouseUpOnSlice:self withEvent:theEvent];
} }
@@ -124,4 +106,4 @@
return [CPString stringWithFormat:@"<%@ %p index:%d indentation:%d>",[self className],self,[self rowIndex],[self indentation]]; return [CPString stringWithFormat:@"<%@ %p index:%d indentation:%d>",[self className],self,[self rowIndex],[self indentation]];
} }
@end @end
+119 -150
View File
@@ -1,45 +1,26 @@
/* /*
* Created by cacaodev@gmail.com. * Created by cacaodev@gmail.com.
* Copyright (c) 2011 Pear, Inc. All rights reserved. * Copyright (c) 2011 Pear, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "CPRuleEditor_Constants.j"
@import "_CPRuleEditorViewSlice.j" @import "_CPRuleEditorViewSlice.j"
@import "_CPRuleEditorPopUpButton.j"
@import "CPRuleEditor.j"
@import "CPButton.j" var CONTROL_HEIGHT = 16.,
@import "CPDatePicker.j" BUTTON_HEIGHT = 16.;
@import "CPPopUpButton.j"
@global CPApp
@global CPMiniControlSize
@global CPSmallControlSize
@implementation _CPRuleEditorViewSliceRow : _CPRuleEditorViewSlice @implementation _CPRuleEditorViewSliceRow : _CPRuleEditorViewSlice
{ {
CPButton _addButton; CPMutableArray _ruleOptionViews;
CPButton _subtractButton; CPMutableArray _ruleOptionFrames;
CPMutableArray _correspondingRuleItems; CPMutableArray _correspondingRuleItems;
CPMutableArray _ruleOptionFrames; CPMutableArray _ruleOptionInitialViewFrames;
CPMutableArray _ruleOptionInitialViewFrames; CPButton _addButton;
CPMutableArray _ruleOptionViews; CPButton _subtractButton;
BOOL editable;
CPRuleEditorRowType _rowType @accessors;
CPRuleEditorRowType _rowType @accessors; CPRuleEditorRowType _plusButtonRowType;
CPRuleEditorRowType _plusButtonRowType;
} }
- (id)initWithFrame:(CGRect)frame ruleEditorView:(id)editor - (id)initWithFrame:(CGRect)frame ruleEditorView:(id)editor
@@ -52,62 +33,47 @@
- (void)_initShared - (void)_initShared
{ {
_correspondingRuleItems = [[CPMutableArray alloc] init]; _correspondingRuleItems = [[CPMutableArray alloc] init];
_ruleOptionFrames = [[CPMutableArray alloc] init]; _ruleOptionFrames = [[CPMutableArray alloc] init];
_ruleOptionInitialViewFrames = [[CPMutableArray alloc] init]; _ruleOptionInitialViewFrames = [[CPMutableArray alloc] init];
_ruleOptionViews = [[CPMutableArray alloc] init]; _ruleOptionViews = [[CPMutableArray alloc] init];
_editable = [_ruleEditor isEditable]; editable = [_ruleEditor isEditable];
_addButton = [self _createAddRowButton];
_subtractButton = [self _createDeleteRowButton];
_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:_addButton];
[self addSubview:_subtractButton]; [self addSubview:_subtractButton];
[self setAutoresizingMask:CPViewWidthSizable]; [self setAutoresizingMask:CPViewWidthSizable];
}
- (CPButton)_createRowButton var center = [CPNotificationCenter defaultCenter];
{ [center addObserver:self selector:@selector(_textDidChange:) name:CPControlTextDidChangeNotification object:nil];
var button = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
[button setFont:[_ruleEditor font]];
[button setTextColor:[_ruleEditor _fontColor]];
[button setAlignment:CPCenterTextAlignment];
[button setAutoresizingMask:CPViewMinXMargin];
[button setButtonType:CPMomentaryChangeButton];
[button setBordered:NO];
[button setHidden:!_editable];
[button setImagePosition:CPImageOnly];
return button;
} }
- (CPButton)_createAddRowButton - (CPButton)_createAddRowButton
{ {
var button = [self _createRowButton]; var button = [[_CPRuleEditorButton alloc] initWithFrame:CGRectMakeZero()];
[button setImage:[_ruleEditor _addImage]];
[button setToolTip:[_ruleEditor _toolTipForAddSimpleRowButton]];
[button setValue:[_ruleEditor _imageAdd] forThemeAttribute:@"image" inState:CPThemeStateNormal];
[button setValue:[_ruleEditor _imageAddHighlighted] forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
[button setAction:@selector(_addOption:)]; [button setAction:@selector(_addOption:)];
[button setTarget:self]; [button setTarget:self];
[button setAutoresizingMask:CPViewMinXMargin];
return button; return button;
} }
- (CPButton)_createDeleteRowButton - (CPButton)_createDeleteRowButton
{ {
var button = [self _createRowButton]; var button = [[_CPRuleEditorButton alloc] initWithFrame:CGRectMakeZero()];
[button setImage:[_ruleEditor _removeImage]];
[button setToolTip:[_ruleEditor _toolTipForDeleteRowButton]];
[button setValue:[_ruleEditor _imageRemove] forThemeAttribute:@"image" inState:CPThemeStateNormal];
[button setValue:[_ruleEditor _imageRemoveHighlighted] forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
[button setAction:@selector(_deleteOption:)]; [button setAction:@selector(_deleteOption:)];
[button setTarget:self]; [button setTarget:self];
[button setAutoresizingMask:CPViewMinXMargin];
return button; return button;
} }
@@ -115,17 +81,18 @@
- (CPMenuItem)_createMenuItemWithTitle:(CPString )title - (CPMenuItem)_createMenuItemWithTitle:(CPString )title
{ {
title = [[_ruleEditor standardLocalizer] localizedStringForString:title]; title = [[_ruleEditor standardLocalizer] localizedStringForString:title];
return [[CPMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""]; var mItem = [[CPMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""];
return mItem;
} }
- (CPPopUpButton)_createPopUpButtonWithItems:(CPArray)itemsArray selectedItemIndex:(int)index - (CPPopUpButton)_createPopUpButtonWithItems:(CPArray)itemsArray selectedItemIndex:(int)index
{ {
var title = [[itemsArray objectAtIndex:index] title], var title = [[itemsArray objectAtIndex:index] title];
font = [_ruleEditor font], var font = [_ruleEditor font],
width = [title sizeWithFont:font].width + 35, // 35 for right arrows + margins width = [title sizeWithFont:font].width + 20,
rect = CGRectMake(0, 0, width, [_ruleEditor rowHeight]), rect = CGRectMake(0, 0, (width - width % 40) + 80, CONTROL_HEIGHT);
popup = [[CPPopUpButton alloc] initWithFrame:rect];
var popup = [[_CPRuleEditorPopUpButton alloc] initWithFrame:rect];
[popup setValue:font forThemeAttribute:@"font"]; [popup setValue:font forThemeAttribute:@"font"];
var count = [itemsArray count]; var count = [itemsArray count];
@@ -142,18 +109,16 @@
return [CPMenuItem separatorItem]; return [CPMenuItem separatorItem];
} }
- (CPTextField)_createStaticTextFieldWithStringValue:(CPString)text - (_CPRuleEditorTextField)_createStaticTextFieldWithStringValue:(CPString )text
{ {
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()], text = [[_ruleEditor standardLocalizer] localizedStringForString:text];
ruleEditorFont = [_ruleEditor font],
font = [CPFont fontWithName:[ruleEditorFont familyName] size:[ruleEditorFont size] + 2],
localizedText = [[_ruleEditor standardLocalizer] localizedStringForString:text],
size = [localizedText sizeWithFont:font];
[textField setFrameSize:CGSizeMake(size.width + 4, [_ruleEditor rowHeight])]; 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 setValue:font forThemeAttribute:@"font"];
[textField setValue:[_ruleEditor _verticalAlignment] forThemeAttribute:@"vertical-alignment"]; [textField setStringValue:text];
[textField setStringValue:localizedText]; [textField sizeToFit];
return textField; return textField;
} }
@@ -164,7 +129,6 @@
[self setNeedsDisplay:YES]; [self setNeedsDisplay:YES];
var type = _plusButtonRowType; var type = _plusButtonRowType;
if ([_ruleEditor nestingMode] == CPRuleEditorNestingModeCompound && ([[CPApp currentEvent] modifierFlags] & CPAlternateKeyMask)) if ([_ruleEditor nestingMode] == CPRuleEditorNestingModeCompound && ([[CPApp currentEvent] modifierFlags] & CPAlternateKeyMask))
type = CPRuleEditorRowTypeCompound; type = CPRuleEditorRowTypeCompound;
@@ -176,12 +140,12 @@
[_ruleEditor _deleteSlice:self]; [_ruleEditor _deleteSlice:self];
} }
- (void)_ruleOptionPopupChangedAction:(CPMenuItem)sender - (void)_ruleOptionPopupChangedAction:(CPMenuItem )sender
{ {
var layoutdict = [sender representedObject], var layoutdict = [sender representedObject],
newItem = [layoutdict objectForKey:@"item"], newItem = [layoutdict objectForKey:@"item"],
indexInCriteria = [layoutdict objectForKey:@"indexInCriteria"], indexInCriteria = [layoutdict objectForKey:@"indexInCriteria"],
oldItem = [_correspondingRuleItems objectAtIndex:indexInCriteria]; oldItem = [_correspondingRuleItems objectAtIndex:indexInCriteria];
if (![newItem isEqual:oldItem]) if (![newItem isEqual:oldItem])
{ {
@@ -209,7 +173,8 @@
- (void)_reconfigureSubviews - (void)_reconfigureSubviews
{ {
var criteria, var ruleItems,
criteria,
repObject, repObject,
menuItem, menuItem,
ruleView, ruleView,
@@ -217,10 +182,9 @@
parent, parent,
numberOfCriteria, numberOfCriteria,
numberOfChildren, numberOfChildren,
firstResponderIndex, firstResponderIndex;
ruleItems = [CPMutableArray array], var ruleItems = [CPMutableArray array];
responder = [[self window] firstResponder];
[self _emptyRulePartSubviews]; [self _emptyRulePartSubviews];
@@ -228,6 +192,7 @@
numberOfCriteria = [criteria count]; numberOfCriteria = [criteria count];
firstResponderIndex = numberOfCriteria - 1; firstResponderIndex = numberOfCriteria - 1;
var responder = [[self window] firstResponder];
if (responder) if (responder)
firstResponderIndex = [_ruleOptionViews indexOfObjectIdenticalTo:responder]; firstResponderIndex = [_ruleOptionViews indexOfObjectIdenticalTo:responder];
@@ -248,16 +213,16 @@
numberOfChildren = [childItems count]; numberOfChildren = [childItems count];
if (numberOfChildren > 1) if (numberOfChildren > 1)
{ {
var menuItems = [CPMutableArray arrayWithCapacity:numberOfChildren], var menuItems = [CPMutableArray arrayWithCapacity:numberOfChildren];
selectedIndex = [childItems indexOfObject:criterion];
var selectedIndex = [childItems indexOfObject:criterion];
if (selectedIndex == CPNotFound) if (selectedIndex == CPNotFound)
break; break;
for (var j = 0; j < numberOfChildren; ++j) for (var j = 0; j < numberOfChildren; ++j)
{ {
var childItem = [childItems objectAtIndex:j], var childItem = [childItems objectAtIndex:j];
childValue = [childValues objectAtIndex:j]; var childValue = [childValues objectAtIndex:j];
if ([childValue isKindOfClass:[CPMenuItem class]]) if ([childValue isKindOfClass:[CPMenuItem class]])
{ {
@@ -276,11 +241,7 @@
} }
} }
repObject = @{ repObject = [CPDictionary dictionaryWithObjectsAndKeys:childItem, @"item", childValue, @"value", i, @"indexInCriteria"];
@"item": childItem,
@"value": childValue,
@"indexInCriteria": i
};
[menuItem setRepresentedObject:repObject]; [menuItem setRepresentedObject:repObject];
[menuItems addObject:menuItem]; [menuItems addObject:menuItem];
} }
@@ -289,8 +250,8 @@
} }
else else
{ {
var value = [childValues objectAtIndex:0], var value = [childValues objectAtIndex:0];
type = [value valueType]; var type = [value valueType];
if (type === 0) if (type === 0)
ruleView = [self _createStaticTextFieldWithStringValue:value]; ruleView = [self _createStaticTextFieldWithStringValue:value];
@@ -312,16 +273,8 @@
if (ruleView != nil) if (ruleView != nil)
{ {
[ruleView setControlSize:CPSmallControlSize];
var minSize = [ruleView currentValueForThemeAttribute:@"min-size"],
frame = [ruleView frame];
// Force controls to their minimum size
frame.size.height = minSize.height;
[ruleView setFrame:frame];
[_ruleOptionViews addObject:ruleView]; [_ruleOptionViews addObject:ruleView];
var frame = [ruleView frame];
[_ruleOptionInitialViewFrames addObject:frame]; [_ruleOptionInitialViewFrames addObject:frame];
[_ruleOptionFrames addObject:frame]; [_ruleOptionFrames addObject:frame];
@@ -334,7 +287,7 @@
[_correspondingRuleItems setArray:ruleItems]; [_correspondingRuleItems setArray:ruleItems];
if (!_editable) if (!editable)
[self _updateEnabledStateForSubviews]; [self _updateEnabledStateForSubviews];
[self _relayoutSubviewsWidthChanged:YES]; [self _relayoutSubviewsWidthChanged:YES];
@@ -344,6 +297,8 @@
var aView = [_ruleOptionViews objectAtIndex:firstResponderIndex]; var aView = [_ruleOptionViews objectAtIndex:firstResponderIndex];
[[self window] makeFirstResponder:aView]; // This is not working. bug in CPPopUpButton firstResponder ? [[self window] makeFirstResponder:aView]; // This is not working. bug in CPPopUpButton firstResponder ?
} }
//[self setNeedsDisplay:YES];
} }
- (void)_updateEnabledStateForSubviews - (void)_updateEnabledStateForSubviews
@@ -353,6 +308,7 @@
- (void)layoutSubviews - (void)layoutSubviews
{ {
// CPLogConsole(_cmd);
[self _relayoutSubviewsWidthChanged:YES]; [self _relayoutSubviewsWidthChanged:YES];
} }
@@ -361,16 +317,14 @@
var optionViewOriginX, var optionViewOriginX,
leftHorizontalPadding, leftHorizontalPadding,
leftButtonMinX, leftButtonMinX,
rowHeight = [_ruleEditor rowHeight], rowHeight = [_ruleEditor rowHeight],
count = [_ruleOptionViews count], count = [_ruleOptionViews count],
sliceFrame = [self frame], sliceFrame = [self frame];
image = [_ruleEditor _imageAdd],
imageSize = image ? [image size] : CGSizeMake(0,0),
buttonFrame = CGRectMake(CGRectGetWidth(sliceFrame) - imageSize.width - [self _rowButtonsRightHorizontalPadding], ([_ruleEditor rowHeight] - imageSize.height) / 2 - 1, imageSize.width, imageSize.height);
var buttonFrame = CGRectMake(CGRectGetWidth(sliceFrame) - BUTTON_HEIGHT - [self _rowButtonsRightHorizontalPadding], ([_ruleEditor rowHeight] - BUTTON_HEIGHT)/2 - 2, BUTTON_HEIGHT, BUTTON_HEIGHT);
[_addButton setFrame:buttonFrame]; [_addButton setFrame:buttonFrame];
buttonFrame.origin.x -= imageSize.width + [self _rowButtonsInterviewHorizontalPadding];
buttonFrame.origin.x -= BUTTON_HEIGHT + [self _rowButtonsInterviewHorizontalPadding];
[_subtractButton setFrame:buttonFrame]; [_subtractButton setFrame:buttonFrame];
if (widthChanged) if (widthChanged)
@@ -385,9 +339,7 @@
var ruleOptionView = _ruleOptionViews[i], var ruleOptionView = _ruleOptionViews[i],
optionFrame = _ruleOptionFrames[i]; optionFrame = _ruleOptionFrames[i];
// Use a pixel correction to align controls optionFrame.origin.y = (rowHeight - CGRectGetHeight(optionFrame))/2 - 2;
optionFrame.origin.y = (rowHeight - CGRectGetHeight(optionFrame)) / 2;
if (widthChanged) if (widthChanged)
{ {
optionFrame.origin.x = optionViewOriginX; optionFrame.origin.x = optionViewOriginX;
@@ -417,9 +369,14 @@
[self _setRowTypeToAddFromPlusButton:type]; [self _setRowTypeToAddFromPlusButton:type];
} }
- (BOOL)isEditable
{
return editable;
}
- (void)setEditable:(BOOL)value - (void)setEditable:(BOOL)value
{ {
[super setEditable:value] editable = value;
// [self _updateEnabledStateForSubviews]; // [self _updateEnabledStateForSubviews];
[self _updateButtonVisibilities]; [self _updateButtonVisibilities];
} }
@@ -461,7 +418,7 @@
- (float)_rowButtonsRightHorizontalPadding - (float)_rowButtonsRightHorizontalPadding
{ {
return ([[_ruleEditor superview] isKindOfClass:[CPClipView class]]) ? 18. : 10.; return 10.;
} }
- (void)_setRowTypeToAddFromPlusButton:(int)type - (void)_setRowTypeToAddFromPlusButton:(int)type
@@ -495,49 +452,61 @@
[self layoutSubviews]; [self layoutSubviews];
} }
- (void)_addObservers - (void)drawRect:(CPRect)rect
{
if (_isObserving)
return;
[super _addObservers];
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_textDidChange:) name:CPControlTextDidChangeNotification object:nil];
}
- (void)_removeObservers
{
if (!_isObserving)
return;
[super _removeObservers];
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPControlTextDidChangeNotification object:nil];
}
- (void)drawRect:(CGRect)rect
{ {
[super drawRect:rect]; [super drawRect:rect];
} }
- (BOOL)_isRulePopup:(CPView)view - (BOOL)_isRulePopup:(CPView)view
{ {
if ([view isKindOfClass:[CPPopUpButton class]]) if ([view isKindOfClass:[_CPRuleEditorPopUpButton class]])
return YES; return YES;
return NO;
}
- (BOOL)_isRuleStaticTextField:(CPView)view
{
if ([view isKindOfClass:[_CPRuleEditorTextField class]])
return YES;
return NO; return NO;
} }
- (void)_sendRuleAction:(id)sender - (void)_sendRuleAction:(id)sender
{ {
[_ruleEditor _updatePredicate];
[_ruleEditor _sendRuleAction]; [_ruleEditor _sendRuleAction];
} }
- (void)_textDidChange:(CPNotification)aNotif - (void)_textDidChange:(CPNotification)aNotif
{ {
if ([[aNotif object] superview] == self && [_ruleEditor _sendsActionOnIncompleteTextChange]) if ([[aNotif object] superview] == self && [_ruleEditor _sendsActionOnIncompleteTextChange])
[self _sendRuleAction:nil]; [_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 @end
-5
View File
@@ -35,11 +35,6 @@
CPArray _allowedFileTypes @accessors(property=allowedFileTypes); CPArray _allowedFileTypes @accessors(property=allowedFileTypes);
} }
+ (CPURL)proposedFileURLWithDocumentName:(CPString)aDocumentName
{
return [CPURL URLWithString:aDocumentName];
}
+ (id)savePanel + (id)savePanel
{ {
return [[CPSavePanel alloc] init]; return [[CPSavePanel alloc] init];
+2 -2
View File
@@ -43,9 +43,9 @@
- (CGRect)visibleFrame - (CGRect)visibleFrame
{ {
#if PLATFORM(DOM) #if PLATFORM(DOM)
return CGRectMake(window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight); return _CGRectMake(window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight);
#else #else
return CGRectMakeZero(); return _CGRectMakeZero();
#endif #endif
} }
+627 -1132
View File
File diff suppressed because it is too large Load Diff
+139 -337
View File
@@ -5,9 +5,6 @@
* Created by Francisco Tolmasky. * Created by Francisco Tolmasky.
* Copyright 2008, 280 North, Inc. * Copyright 2008, 280 North, Inc.
* *
* Modified to match Lion style by Antoine Mercadal 2011
* <antoine.mercadal@archipelproject.org>
*
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
@@ -23,17 +20,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "../Foundation/Foundation.h"
@import "CPAnimation.j"
@import "CPControl.j" @import "CPControl.j"
@import "CPWindow_Constants.j"
@import "CPViewAnimation.j"
@global CPApp
// CPScroller Constants // CPScroller Constants
@typedef CPScrollerPart
CPScrollerNoPart = 0; CPScrollerNoPart = 0;
CPScrollerDecrementPage = 1; CPScrollerDecrementPage = 1;
CPScrollerKnob = 2; CPScrollerKnob = 2;
@@ -45,41 +35,28 @@ CPScrollerKnobSlot = 6;
CPScrollerIncrementArrow = 0; CPScrollerIncrementArrow = 0;
CPScrollerDecrementArrow = 1; CPScrollerDecrementArrow = 1;
@typedef CPUsableScrollerParts
CPNoScrollerParts = 0; CPNoScrollerParts = 0;
CPOnlyScrollerArrows = 1; CPOnlyScrollerArrows = 1;
CPAllScrollerParts = 2; CPAllScrollerParts = 2;
/*!
@ingroup appkit
@class CPScroller
*/
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob], var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
NAMES_FOR_PARTS = {}, NAMES_FOR_PARTS = {},
PARTS_FOR_NAMES = {}; PARTS_FOR_NAMES = {};
var _CACHED_THEME_SCROLLER = nil; // This is used by the class methods to pull the theme attributes.
NAMES_FOR_PARTS[CPScrollerDecrementLine] = @"decrement-line"; NAMES_FOR_PARTS[CPScrollerDecrementLine] = @"decrement-line";
NAMES_FOR_PARTS[CPScrollerIncrementLine] = @"increment-line"; NAMES_FOR_PARTS[CPScrollerIncrementLine] = @"increment-line";
NAMES_FOR_PARTS[CPScrollerKnobSlot] = @"knob-slot"; NAMES_FOR_PARTS[CPScrollerKnobSlot] = @"knob-slot";
NAMES_FOR_PARTS[CPScrollerKnob] = @"knob"; NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
CPScrollerStyleLegacy = 0;
CPScrollerStyleOverlay = 1;
CPScrollerKnobStyleDefault = 0;
CPScrollerKnobStyleDark = 1;
CPScrollerKnobStyleLight = 2;
CPThemeStateScrollViewLegacy = CPThemeState("scroller-style-legacy");
CPThemeStateScrollerKnobLight = CPThemeState("scroller-knob-light");
CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
/*!
@ingroup appkit
@class CPScroller
*/
@implementation CPScroller : CPControl @implementation CPScroller : CPControl
{ {
CPControlSize _controlSize;
CPUsableScrollerParts _usableParts; CPUsableScrollerParts _usableParts;
CPArray _partRects; CPArray _partRects;
@@ -91,69 +68,59 @@ 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";
} }
+ (CPDictionary)themeAttributes + (id)themeAttributes
{ {
return @{ return [CPDictionary dictionaryWithJSObject:{
@"scroller-width": 7.0, @"scroller-width": 15.0,
@"knob-slot-color": [CPNull null], @"knob-slot-color": [CPColor lightGrayColor],
@"decrement-line-color": [CPNull null], @"decrement-line-color": [CPNull null],
@"increment-line-color": [CPNull null], @"increment-line-color": [CPNull null],
@"knob-color": [CPNull null], @"knob-color": [CPColor grayColor],
@"decrement-line-size": CGSizeMakeZero(), @"decrement-line-size":_CGSizeMakeZero(),
@"increment-line-size": CGSizeMakeZero(), @"increment-line-size":_CGSizeMakeZero(),
@"track-inset": CGInsetMakeZero(), @"track-inset":_CGInsetMakeZero(),
@"knob-inset": CGInsetMakeZero(), @"knob-inset": _CGInsetMakeZero(),
@"minimum-knob-length": 21.0, @"minimum-knob-length":21.0
@"track-border-overlay": 9.0 }]
};
} }
+ (float)scrollerWidth
// Calculating Layout
- (id)initWithFrame:(CGRect)aFrame
{ {
return [self scrollerWidthInStyle:CPScrollerStyleLegacy]; self = [super initWithFrame:aFrame];
if (self)
{
_controlSize = CPRegularControlSize;
_partRects = [];
[self setFloatValue:0.0];
[self setKnobProportion:1.0];
_hitPart = CPScrollerNoPart;
[self _calculateIsVertical];
}
return self;
} }
// Determining CPScroller Size
/*! /*!
Returns the CPScroller's width for a CPRegularControlSize. Returns the CPScroller's width for a CPRegularControlSize.
*/ */
+ (float)scrollerWidthInStyle:(int)aStyle + (float)scrollerWidth
{ {
if (!_CACHED_THEME_SCROLLER) return [[[CPScroller alloc] init] currentValueForThemeAttribute:@"scroller-width"];
_CACHED_THEME_SCROLLER = [[self alloc] init];
if (aStyle == CPScrollerStyleLegacy)
return [_CACHED_THEME_SCROLLER valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
return [_CACHED_THEME_SCROLLER currentValueForThemeAttribute:@"scroller-width"];
}
/*!
Returns the CPScroller's overlay value.
*/
+ (float)scrollerOverlay
{
if (!_CACHED_THEME_SCROLLER)
_CACHED_THEME_SCROLLER = [[self alloc] init];
return [_CACHED_THEME_SCROLLER currentValueForThemeAttribute:@"track-border-overlay"];
} }
/*! /*!
@@ -165,77 +132,27 @@ 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])
{
_partRects = [];
[self setFloatValue:0.0];
[self setKnobProportion:1.0];
_hitPart = CPScrollerNoPart;
_allowFadingOut = YES;
_isMouseOver = NO;
_style = CPScrollerStyleOverlay;
var paramAnimFadeOut = @{
CPViewAnimationTargetKey: self,
CPViewAnimationEffectKey: CPViewAnimationFadeOutEffect,
};
_animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
[_animationScroller setViewAnimations:[paramAnimFadeOut]];
[_animationScroller setDelegate:self];
[self setAlphaValue:0.0];
// We have to choose an orientation. If for some bizarre reason width === height,
// punt and choose vertical.
[self _setIsVertical:CGRectGetHeight(aFrame) >= CGRectGetWidth(aFrame)];
}
return self;
}
#pragma mark -
#pragma mark Getters / Setters
/*! /*!
Returns the scroller's style Sets the scroller's size.
@param aControlSize the scroller's size
*/ */
- (void)style - (void)setControlSize:(CPControlSize)aControlSize
{ {
return _style; if (_controlSize == aControlSize)
}
/*!
Set the scroller's control size
@param aStyle the scroller style: CPScrollerStyleLegacy or CPScrollerStyleOverlay
*/
- (void)setStyle:(id)aStyle
{
if (_style != nil && _style === aStyle)
return; return;
_style = aStyle; _controlSize = aControlSize;
if (_style === CPScrollerStyleLegacy) [self setNeedsLayout];
{ [self setNeedsDisplay:YES];
[self fadeIn]; }
[self setThemeState:CPThemeStateScrollViewLegacy];
}
else
{
_allowFadingOut = YES;
[self unsetThemeState:CPThemeStateScrollViewLegacy];
}
//[self _adjustScrollerSize]; /*!
Returns the scroller's control size
*/
- (CPControlSize)controlSize
{
return _controlSize;
} }
- (void)setObjectValue:(id)aValue - (void)setObjectValue:(id)aValue
@@ -243,6 +160,14 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
[super setObjectValue:MIN(1.0, MAX(0.0, +aValue))]; [super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
} }
- (void)setKnobProportion:(float)aProportion
{
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
/*! /*!
Return's the knob's proportion Return's the knob's proportion
*/ */
@@ -251,55 +176,22 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
return _knobProportion; return _knobProportion;
} }
/*! - (id)currentValueForThemeAttribute:(CPString)anAttributeName
Set the knob's proportion
@param aProportion the desired proportion
*/
- (void)setKnobProportion:(float)aProportion
{ {
if (!_IS_NUMERIC(aProportion)) var themeState = _themeState;
[CPException raise:CPInvalidArgumentException reason:"aProportion must be numeric"];
_knobProportion = MIN(1.0, MAX(0.0001, aProportion)); if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
themeState &= ~CPThemeStateHighlighted;
[self setNeedsDisplay:YES]; return [self valueForThemeAttribute:anAttributeName inState:themeState];
[self setNeedsLayout];
} }
// Calculating Layout
#pragma mark -
#pragma mark Privates
/*! @ignore */
- (void)_adjustScrollerSize
{
var frame = [self frame],
scrollerWidth = [self currentValueForThemeAttribute:@"scroller-width"];
if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
frame.size.width = scrollerWidth;
if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
frame.size.height = scrollerWidth;
[self setFrame:frame];
}
/*! @ignore */
- (void)_performFadeOut:(CPTimer)aTimer
{
[self fadeOut];
_timerFadeOut = nil;
}
#pragma mark -
#pragma mark Utilities
- (CGRect)rectForPart:(CPScrollerPart)aPart - (CGRect)rectForPart:(CPScrollerPart)aPart
{ {
if (aPart == CPScrollerNoPart) if (aPart == CPScrollerNoPart)
return CGRectMakeZero(); return _CGRectMakeZero();
return _partRects[aPart]; return _partRects[aPart];
} }
@@ -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] && ![self hasThemeState:CPThemeStateScrollViewLegacy])
return CPScrollerNoPart;
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint)) if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
return CPScrollerKnob; return CPScrollerKnob;
@@ -369,8 +258,8 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
var knobInset = [self currentValueForThemeAttribute:@"knob-inset"], var knobInset = [self currentValueForThemeAttribute:@"knob-inset"],
trackInset = [self currentValueForThemeAttribute:@"track-inset"], trackInset = [self currentValueForThemeAttribute:@"track-inset"],
width = CGRectGetWidth(bounds), width = _CGRectGetWidth(bounds),
height = CGRectGetHeight(bounds); height = _CGRectGetHeight(bounds);
if ([self isVertical]) if ([self isVertical])
{ {
@@ -380,26 +269,25 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom, effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight, slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"], minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
knobVerticalInset = knobInset.top + knobInset.bottom,
knobWidth = width - knobInset.left - knobInset.right, knobWidth = width - knobInset.left - knobInset.right,
knobHeight = MAX(minimumKnobLength, ((slotSize - knobVerticalInset) * _knobProportion)), knobHeight = MAX(minimumKnobLength, (slotSize * _knobProportion)),
knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight - knobVerticalInset) * [self floatValue] + knobInset.top; knobLocation = effectiveDecrementLineHeight + (slotSize - 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, slotSize);
_partRects[CPScrollerDecrementLine] = CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height); _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
_partRects[CPScrollerIncrementLine] = CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height); _partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom) if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
_partRects[CPScrollerKnob] = CGRectMakeZero(); _partRects[CPScrollerKnob] = _CGRectMakeZero();
if (height < decrementLineSize.height + incrementLineSize.height - 2) if (height < decrementLineSize.height + incrementLineSize.height - 2)
{ {
_partRects[CPScrollerIncrementLine] = CGRectMakeZero(); _partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
_partRects[CPScrollerDecrementLine] = CGRectMakeZero(); _partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
_partRects[CPScrollerKnobSlot] = CGRectMake(trackInset.left, 0, width - trackInset.left - trackInset.right, height); _partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, 0, width - trackInset.left - trackInset.right, height);
} }
} }
else else
@@ -410,26 +298,25 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right, effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
slotSize = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth, slotSize = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"], minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
knobHorizontalInset = knobInset.left + knobInset.right, knobWidth = MAX(minimumKnobLength, (slotSize * _knobProportion)),
knobWidth = MAX(minimumKnobLength, ((slotSize - knobHorizontalInset) * _knobProportion)),
knobHeight = height - knobInset.top - knobInset.bottom, knobHeight = height - knobInset.top - knobInset.bottom,
knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth - knobHorizontalInset) * [self floatValue] + knobInset.left; knobLocation = effectiveDecrementLineWidth + (slotSize - 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, slotSize, height - trackInset.top - trackInset.bottom);
_partRects[CPScrollerDecrementLine] = CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height); _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
_partRects[CPScrollerIncrementLine] = CGRectMake(width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height); _partRects[CPScrollerIncrementLine] = _CGRectMake(width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
if (width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right) if (width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
_partRects[CPScrollerKnob] = CGRectMakeZero(); _partRects[CPScrollerKnob] = _CGRectMakeZero();
if (width < decrementLineSize.width + incrementLineSize.width - 2) if (width < decrementLineSize.width + incrementLineSize.width - 2)
{ {
_partRects[CPScrollerIncrementLine] = CGRectMakeZero(); _partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
_partRects[CPScrollerDecrementLine] = CGRectMakeZero(); _partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
_partRects[CPScrollerKnobSlot] = CGRectMake(0.0, 0.0, width, slotSize); _partRects[CPScrollerKnobSlot] = _CGRectMake(0.0, 0.0, width, slotSize);
} }
} }
} }
@@ -443,32 +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
{
[_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
@@ -494,7 +356,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
- (CPView)createViewForPart:(CPScrollerPart)aPart - (CPView)createViewForPart:(CPScrollerPart)aPart
{ {
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()]; var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[view setHitTests:NO]; [view setHitTests:NO];
@@ -508,7 +370,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
- (CPView)createEphemeralSubviewNamed:(CPString)aName - (CPView)createEphemeralSubviewNamed:(CPString)aName
{ {
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()]; var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[view setHitTests:NO]; [view setHitTests:NO];
@@ -517,12 +379,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
- (void)layoutSubviews - (void)layoutSubviews
{ {
[self _adjustScrollerSize];
[self checkSpaceForParts]; [self checkSpaceForParts];
var index = 0, var index = 0,
count = PARTS_ARRANGEMENT.length, count = PARTS_ARRANGEMENT.length;
view;
for (; index < count; ++index) for (; index < count; ++index)
{ {
@@ -583,7 +443,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
{ {
var knobRect = [self rectForPart:CPScrollerKnob], var knobRect = [self rectForPart:CPScrollerKnob],
knobSlotRect = [self rectForPart:CPScrollerKnobSlot], knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
remainder = ![self isVertical] ? (CGRectGetWidth(knobSlotRect) - CGRectGetWidth(knobRect)) : (CGRectGetHeight(knobSlotRect) - CGRectGetHeight(knobRect)); remainder = ![self isVertical] ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
if (remainder <= 0) if (remainder <= 0)
[self setFloatValue:0.0]; [self setFloatValue:0.0];
@@ -598,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]];
} }
/*! /*!
@@ -628,20 +487,20 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
if ([anEvent modifierFlags] & CPAlternateKeyMask) if ([anEvent modifierFlags] & CPAlternateKeyMask)
{ {
if (_trackingPart === CPScrollerDecrementLine) if (_trackingPart == CPScrollerDecrementLine)
_hitPart = CPScrollerDecrementPage; _hitPart = CPScrollerDecrementPage;
else if (_trackingPart === CPScrollerIncrementLine) else if (_trackingPart == CPScrollerIncrementLine)
_hitPart = CPScrollerIncrementPage; _hitPart = CPScrollerIncrementPage;
else if (_trackingPart === CPScrollerDecrementPage || _trackingPart === CPScrollerIncrementPage) else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
{ {
var knobRect = [self rectForPart:CPScrollerKnob], var knobRect = [self rectForPart:CPScrollerKnob],
knobWidth = ![self isVertical] ? CGRectGetWidth(knobRect) : CGRectGetHeight(knobRect), knobWidth = ![self isVertical] ? _CGRectGetWidth(knobRect) : _CGRectGetHeight(knobRect),
knobSlotRect = [self rectForPart:CPScrollerKnobSlot], knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
remainder = (![self isVertical] ? CGRectGetWidth(knobSlotRect) : CGRectGetHeight(knobSlotRect)) - knobWidth; remainder = (![self isVertical] ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
[self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder]; [self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
_hitPart = CPScrollerKnob; _hitPart = CPScrollerKnob;
@@ -662,11 +521,11 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
{ {
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil]; _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
if (_trackingPart === CPScrollerDecrementPage || _trackingPart === CPScrollerIncrementPage) if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
{ {
var hitPart = [self testPart:[anEvent locationInWindow]]; var hitPart = [self testPart:[anEvent locationInWindow]];
if (hitPart === CPScrollerDecrementPage || hitPart === CPScrollerIncrementPage) if (hitPart == CPScrollerDecrementPage || hitPart == CPScrollerIncrementPage)
{ {
_trackingPart = hitPart; _trackingPart = hitPart;
_hitPart = hitPart; _hitPart = hitPart;
@@ -682,13 +541,18 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
} }
- (void)_setIsVertical:(BOOL)isVertical - (void)_calculateIsVertical
{ {
_isVertical = isVertical; // Recalculate isVertical.
var bounds = [self bounds],
width = _CGRectGetWidth(bounds),
height = _CGRectGetHeight(bounds);
if (_isVertical) _isVertical = width < height ? 1 : (width > height ? 0 : -1);
if (_isVertical === 1)
[self setThemeState:CPThemeStateVertical]; [self setThemeState:CPThemeStateVertical];
else else if (_isVertical === 0)
[self unsetThemeState:CPThemeStateVertical]; [self unsetThemeState:CPThemeStateVertical];
} }
@@ -700,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 = themeState.without(CPThemeStateHighlighted);
return [self valueForThemeAttribute:anAttributeName inState:themeState];
}
- (void)mouseDown:(CPEvent)anEvent - (void)mouseDown:(CPEvent)anEvent
{ {
if (![self isEnabled]) if (![self isEnabled])
@@ -723,67 +573,19 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
switch (_hitPart) switch (_hitPart)
{ {
case CPScrollerKnob: case CPScrollerKnob: return [self trackKnob:anEvent];
return [self trackKnob:anEvent];
case CPScrollerDecrementLine: case CPScrollerDecrementLine:
case CPScrollerIncrementLine: case CPScrollerIncrementLine:
case CPScrollerDecrementPage: case CPScrollerDecrementPage:
case CPScrollerIncrementPage: case CPScrollerIncrementPage: return [self trackScrollButtons:anEvent];
return [self trackScrollButtons:anEvent];
} }
} }
- (void)mouseEntered:(CPEvent)anEvent
{
[super mouseEntered:anEvent];
if (_timerFadeOut)
[_timerFadeOut invalidate];
if (![self isEnabled])
return;
_allowFadingOut = NO;
_isMouseOver = YES;
if ([self alphaValue] > 0 && _knobProportion != 1.0)
[self setThemeState:CPThemeStateSelected];
}
- (void)mouseExited:(CPEvent)anEvent
{
[super mouseExited:anEvent];
if ([self isHidden] || ![self isEnabled] || !_isMouseOver)
return;
_allowFadingOut = YES;
_isMouseOver = NO;
if (_timerFadeOut)
[_timerFadeOut invalidate];
if ([self hasThemeState:CPThemeStateScrollViewLegacy])
[self unsetThemeState:CPThemeStateSelected];
else
_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 CPScrollerIsVerticalKey = @"CPScrollerIsVerticalKey", var CPScrollerControlSizeKey = "CPScrollerControlSize",
CPScrollerKnobProportionKey = @"CPScrollerKnobProportion", CPScrollerKnobProportionKey = "CPScrollerKnobProportion";
CPScrollerStyleKey = @"CPScrollerStyleKey";
@implementation CPScroller (CPCoding) @implementation CPScroller (CPCoding)
@@ -791,8 +593,11 @@ var CPScrollerIsVerticalKey = @"CPScrollerIsVerticalKey",
{ {
if (self = [super initWithCoder:aCoder]) if (self = [super initWithCoder:aCoder])
{ {
_knobProportion = 1.0; _controlSize = CPRegularControlSize;
if ([aCoder containsValueForKey:CPScrollerControlSizeKey])
_controlSize = [aCoder decodeIntForKey:CPScrollerControlSizeKey];
_knobProportion = 1.0;
if ([aCoder containsValueForKey:CPScrollerKnobProportionKey]) if ([aCoder containsValueForKey:CPScrollerKnobProportionKey])
_knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey]; _knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
@@ -800,22 +605,20 @@ var CPScrollerIsVerticalKey = @"CPScrollerIsVerticalKey",
_hitPart = CPScrollerNoPart; _hitPart = CPScrollerNoPart;
_allowFadingOut = YES; [self _calculateIsVertical];
_isMouseOver = NO;
var paramAnimFadeOut = @{ // Adjust the size of the scroller if the size from cib
CPViewAnimationTargetKey: self, // isn't equal to the scrollerWidth
CPViewAnimationEffectKey: CPViewAnimationFadeOutEffect, var frame = [self frame],
}; scrollerWidth = [CPScroller scrollerWidth];
_animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut]; if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
[_animationScroller setViewAnimations:[paramAnimFadeOut]]; frame.size.width = scrollerWidth;
[_animationScroller setDelegate:self];
[self setAlphaValue:0.0];
[self setStyle:[aCoder decodeIntForKey:CPScrollerStyleKey]]; if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
frame.size.height = scrollerWidth;
[self _setIsVertical:[aCoder decodeBoolForKey:CPScrollerIsVerticalKey]]; [self setFrame:frame];
} }
return self; return self;
@@ -825,9 +628,8 @@ var CPScrollerIsVerticalKey = @"CPScrollerIsVerticalKey",
{ {
[super encodeWithCoder:aCoder]; [super encodeWithCoder:aCoder];
[aCoder encodeInt:_isVertical forKey:CPScrollerIsVerticalKey]; [aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey]; [aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
[aCoder encodeInt:_style forKey:CPScrollerStyleKey];
} }
@end @end
+76 -200
View File
@@ -21,21 +21,26 @@
*/ */
@import "CPButton.j" @import "CPButton.j"
@import "CPMenu.j"
@import "CPMenuItem.j"
@import "CPTextField.j" @import "CPTextField.j"
@class CPUserDefaults
@global CPApp
CPSearchFieldRecentsTitleMenuItemTag = 1000; CPSearchFieldRecentsTitleMenuItemTag = 1000;
CPSearchFieldRecentsMenuItemTag = 1001; CPSearchFieldRecentsMenuItemTag = 1001;
CPSearchFieldClearRecentsMenuItemTag = 1002; CPSearchFieldClearRecentsMenuItemTag = 1002;
CPSearchFieldNoRecentsMenuItemTag = 1003; CPSearchFieldNoRecentsMenuItemTag = 1003;
var CPSearchFieldSearchImage = nil,
CPSearchFieldFindImage = nil,
CPSearchFieldCancelImage = nil,
CPSearchFieldCancelPressedImage = nil;
var SEARCH_BUTTON_DEFAULT_WIDTH = 25.0,
CANCEL_BUTTON_DEFAULT_WIDTH = 22.0,
BUTTON_DEFAULT_HEIGHT = 22.0;
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification"; var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification";
var RECENT_SEARCH_PREFIX = @" ";
/*! /*!
@ingroup appkit @ingroup appkit
@class CPSearchField @class CPSearchField
@@ -65,25 +70,16 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
return @"searchfield" return @"searchfield"
} }
+ (CPDictionary)themeAttributes + (void)initialize
{ {
return @{ if (self != [CPSearchField class])
@"image-search": [CPNull null], return;
@"image-find": [CPNull null],
@"image-cancel": [CPNull null],
@"image-cancel-pressed": [CPNull null],
@"image-search-inset" : CGInsetMake(0, 0, 0, 5),
@"image-cancel-inset" : CGInsetMake(0, 5, 0, 0),
@"search-right-margin": 2
};
}
+ (Class)_binderClassForBinding:(CPString)aBinding var bundle = [CPBundle bundleForClass:self];
{ CPSearchFieldSearchImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldSearch.png"] size:_CGSizeMake(SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
if (aBinding === CPPredicateBinding) CPSearchFieldFindImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldFind.png"] size:_CGSizeMake(SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
return [_CPSearchFieldPredicateBinder class]; CPSearchFieldCancelImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancel.png"] size:_CGSizeMake(CANCEL_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
CPSearchFieldCancelPressedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancelPressed.png"] size:_CGSizeMake(CANCEL_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
return [super _binderClassForBinding:aBinding];
} }
- (id)initWithFrame:(CGRect)frame - (id)initWithFrame:(CGRect)frame
@@ -96,6 +92,10 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
_recentsAutosaveName = nil; _recentsAutosaveName = nil;
[self _init]; [self _init];
#if PLATFORM(DOM)
_cancelButton._DOMElement.style.cursor = "default";
_searchButton._DOMElement.style.cursor = "default";
#endif
} }
return self; return self;
@@ -124,28 +124,16 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
_canResignFirstResponder = YES; _canResignFirstResponder = YES;
} }
- (void)viewWillMoveToSuperview:(CPView)aView
#pragma mark -
#pragma mark Override observers
- (void)_removeObservers
{ {
if (!_isObserving) [super viewWillMoveToSuperview:aView];
return;
[super _removeObservers];
// First we remove any observer that may have been in place to avoid memory leakage.
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPControlTextDidChangeNotification object:self]; [[CPNotificationCenter defaultCenter] removeObserver:self name:CPControlTextDidChangeNotification object:self];
}
- (void)_addObservers // Register the observe here if we need to.
{ if (aView)
if (_isObserving) [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_searchFieldTextDidChange:) name:CPControlTextDidChangeNotification object:self];
return;
[super _addObservers];
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_searchFieldTextDidChange:) name:CPControlTextDidChangeNotification object:self];
} }
// Managing Buttons // Managing Buttons
@@ -182,10 +170,10 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
- (void)resetSearchButton - (void)resetSearchButton
{ {
var button = [self searchButton], var button = [self searchButton],
searchButtonImage = (_searchMenuTemplate === nil) ? [self currentValueForThemeAttribute:@"image-search"] : [self currentValueForThemeAttribute:@"image-find"]; searchButtonImage = (_searchMenuTemplate === nil) ? CPSearchFieldSearchImage : CPSearchFieldFindImage;
[button setBordered:NO]; [button setBordered:NO];
[button setImageScaling:CPImageScaleAxesIndependently]; [button setImageScaling:CPScaleToFit];
[button setImage:searchButtonImage]; [button setImage:searchButtonImage];
[button setAutoresizingMask:CPViewMaxXMargin]; [button setAutoresizingMask:CPViewMaxXMargin];
} }
@@ -205,7 +193,6 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
[_cancelButton setAutoresizingMask:CPViewMinXMargin]; [_cancelButton setAutoresizingMask:CPViewMinXMargin];
[_cancelButton setTarget:self]; [_cancelButton setTarget:self];
[_cancelButton setAction:@selector(cancelOperation:)]; [_cancelButton setAction:@selector(cancelOperation:)];
[_cancelButton setButtonType:CPMomentaryChangeButton];
[self _updateCancelButtonVisibility]; [self _updateCancelButtonVisibility];
[self addSubview:_cancelButton]; [self addSubview:_cancelButton];
} }
@@ -228,9 +215,9 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
{ {
var button = [self cancelButton]; var button = [self cancelButton];
[button setBordered:NO]; [button setBordered:NO];
[button setImageScaling:CPImageScaleAxesIndependently]; [button setImageScaling:CPScaleToFit];
[button setImage:[self valueForThemeAttribute:@"image-cancel"]]; [button setImage:CPSearchFieldCancelImage];
[button setAlternateImage:[self valueForThemeAttribute:@"image-cancel-pressed"]]; [button setAlternateImage:CPSearchFieldCancelPressedImage];
[button setAutoresizingMask:CPViewMinXMargin]; [button setAutoresizingMask:CPViewMinXMargin];
[button setTarget:self]; [button setTarget:self];
[button setAction:@selector(cancelOperation:)]; [button setAction:@selector(cancelOperation:)];
@@ -246,23 +233,22 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
- (CGRect)searchTextRectForBounds:(CGRect)rect - (CGRect)searchTextRectForBounds:(CGRect)rect
{ {
var leftOffset = 0, var leftOffset = 0,
width = CGRectGetWidth(rect), width = _CGRectGetWidth(rect),
bounds = [self bounds]; bounds = [self bounds];
if (_searchButton) if (_searchButton)
{ {
var searchBounds = [self searchButtonRectForBounds:bounds], var searchBounds = [self searchButtonRectForBounds:bounds];
rightMargin = [self currentValueForThemeAttribute:@"search-right-margin"]; leftOffset = _CGRectGetMaxX(searchBounds) + 2;
leftOffset = CGRectGetMaxX(searchBounds) + rightMargin;
} }
if (_cancelButton) if (_cancelButton)
{ {
var cancelRect = [self cancelButtonRectForBounds:bounds]; var cancelRect = [self cancelButtonRectForBounds:bounds];
width = CGRectGetMinX(cancelRect) - leftOffset; width = _CGRectGetMinX(cancelRect) - leftOffset;
} }
return CGRectMake(leftOffset, CGRectGetMinY(rect), width, CGRectGetHeight(rect)); return _CGRectMake(leftOffset, _CGRectGetMinY(rect), width, _CGRectGetHeight(rect));
} }
/*! /*!
@@ -272,10 +258,7 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
*/ */
- (CGRect)searchButtonRectForBounds:(CGRect)rect - (CGRect)searchButtonRectForBounds:(CGRect)rect
{ {
var size = [[self currentValueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero(), return _CGRectMake(5, (_CGRectGetHeight(rect) - BUTTON_DEFAULT_HEIGHT) / 2, SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT);
inset = [self currentValueForThemeAttribute:@"image-search-inset"];
return CGRectMake(inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
} }
/*! /*!
@@ -285,10 +268,7 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
*/ */
- (CGRect)cancelButtonRectForBounds:(CGRect)rect - (CGRect)cancelButtonRectForBounds:(CGRect)rect
{ {
var size = [[self currentValueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero(), return _CGRectMake(_CGRectGetWidth(rect) - CANCEL_BUTTON_DEFAULT_WIDTH - 5, (_CGRectGetHeight(rect) - CANCEL_BUTTON_DEFAULT_WIDTH) / 2, BUTTON_DEFAULT_HEIGHT, BUTTON_DEFAULT_HEIGHT);
inset = [self currentValueForThemeAttribute:@"image-cancel-inset"];
return CGRectMake(CGRectGetWidth(rect) - size.width + inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
} }
// Managing Menu Templates // Managing Menu Templates
@@ -392,9 +372,9 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
*/ */
- (void)setRecentSearches:(CPArray)searches - (void)setRecentSearches:(CPArray)searches
{ {
var max = MIN([self maximumRecents], [searches count]); var max = MIN([self maximumRecents], [searches count]),
searches = [searches subarrayWithRange:CPMakeRange(0, max)];
searches = [searches subarrayWithRange:CPMakeRange(0, max)];
_recentSearches = searches; _recentSearches = searches;
[self _autosaveRecentSearchList]; [self _autosaveRecentSearchList];
} }
@@ -478,7 +458,7 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
[self sendAction:[self action] to:[self target]]; [self sendAction:[self action] to:[self target]];
} }
- (BOOL)sendAction:(SEL)anAction to:(id)anObject - (void)sendAction:(SEL)anAction to:(id)anObject
{ {
[super sendAction:anAction to:anObject]; [super sendAction:anAction to:anObject];
@@ -502,7 +482,7 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
- (CPView)hitTest:(CGPoint)aPoint - (CPView)hitTest:(CGPoint)aPoint
{ {
// Make sure a hit anywhere within the search field returns the search field itself // Make sure a hit anywhere within the search field returns the search field itself
if (CGRectContainsPoint([self frame], aPoint)) if (_CGRectContainsPoint([self frame], aPoint))
return self; return self;
else else
return nil; return nil;
@@ -518,35 +498,33 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
var location = [anEvent locationInWindow], var location = [anEvent locationInWindow],
point = [self convertPoint:location fromView:nil]; point = [self convertPoint:location fromView:nil];
if (CGRectContainsPoint([self searchButtonRectForBounds:[self bounds]], point)) if (_CGRectContainsPoint([self searchButtonRectForBounds:[self bounds]], point))
{ {
if (_searchMenuTemplate == nil) if (_searchMenuTemplate == nil)
{ [self _sendAction:self];
if ([_searchButton target] && [_searchButton action])
[_searchButton mouseDown:anEvent];
else
[self _sendAction:self];
}
else else
[self _showMenu]; [self _showMenu];
} }
else if (CGRectContainsPoint([self cancelButtonRectForBounds:[self bounds]], point)) else if (_CGRectContainsPoint([self cancelButtonRectForBounds:[self bounds]], point))
[_cancelButton mouseDown:anEvent]; [_cancelButton mouseDown:anEvent];
else else
[super mouseDown:anEvent]; [super mouseDown:anEvent];
} }
/*! /*!
Provides the common case items for a recent searches menu. Provides the common case items for a recent searches menu. If there are not recent searches,
displays a single disabled item:
No Recent Searches
If there are 1 more recent searches, it displays: If there are 1 more recent searches, it displays:
Clear
---------------------
Recent Searches Recent Searches
recent search 1 recent search 1
recent search 2 recent search 2
etc. etc.
---------------------
Clear Recent Searches
If you wish to add items before or after the template, you can. If you put items If you wish to add items before or after the template, you can. If you put items
before, a separator will automatically be placed before the default template item. before, a separator will automatically be placed before the default template item.
@@ -571,13 +549,6 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
var template = [[CPMenu alloc] init], var template = [[CPMenu alloc] init],
item; item;
item = [[CPMenuItem alloc] initWithTitle:@"Clear"
action:@selector(_searchFieldClearRecents:)
keyEquivalent:@""];
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
[item setTarget:self];
[template addItem:item];
item = [[CPMenuItem alloc] initWithTitle:@"Recent Searches" item = [[CPMenuItem alloc] initWithTitle:@"Recent Searches"
action:nil action:nil
keyEquivalent:@""]; keyEquivalent:@""];
@@ -592,6 +563,20 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
[item setTarget:self]; [item setTarget:self];
[template addItem:item]; [template addItem:item];
item = [[CPMenuItem alloc] initWithTitle:@"Clear Recent Searches"
action:@selector(_searchFieldClearRecents:)
keyEquivalent:@""];
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
[item setTarget:self];
[template addItem:item];
item = [[CPMenuItem alloc] initWithTitle:@"No Recent Searches"
action:nil
keyEquivalent:@""];
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
[item setEnabled:NO];
[template addItem:item];
return template; return template;
} }
@@ -604,8 +589,6 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
countOfRecents = [_recentSearches count], countOfRecents = [_recentSearches count],
numberOfItems = [_searchMenuTemplate numberOfItems]; numberOfItems = [_searchMenuTemplate numberOfItems];
[menu setAutoenablesItems:NO];
for (var i = 0; i < numberOfItems; i++) for (var i = 0; i < numberOfItems; i++)
{ {
var item = [[_searchMenuTemplate itemAtIndex:i] copy]; var item = [[_searchMenuTemplate itemAtIndex:i] copy];
@@ -626,7 +609,8 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex) for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
{ {
var recentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:recentIndex] // RECENT_SEARCH_PREFIX is a hack until CPMenuItem -setIndentationLevel works
var recentItem = [[CPMenuItem alloc] initWithTitle:RECENT_SEARCH_PREFIX + [_recentSearches objectAtIndex:recentIndex]
action:itemAction action:itemAction
keyEquivalent:[item keyEquivalent]]; keyEquivalent:[item keyEquivalent]];
[item setTarget:self]; [item setTarget:self];
@@ -686,11 +670,11 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
- (void)_showMenu - (void)_showMenu
{ {
if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled] || ([_recentSearches count] === 0)) if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled])
return; return;
var aFrame = [[self superview] convertRect:[self frame] toView:nil], var aFrame = [[self superview] convertRect:[self frame] toView:nil],
location = CGPointMake(aFrame.origin.x + 10, aFrame.origin.y + aFrame.size.height - 4); location = CPMakePoint(aFrame.origin.x + 10, aFrame.origin.y + aFrame.size.height - 4);
var anEvent = [CPEvent mouseEventWithType:CPRightMouseDown location:location modifierFlags:0 timestamp:[[CPApp currentEvent] timestamp] windowNumber:[[self window] windowNumber] context:nil eventNumber:1 clickCount:1 pressure:0]; var anEvent = [CPEvent mouseEventWithType:CPRightMouseDown location:location modifierFlags:0 timestamp:[[CPApp currentEvent] timestamp] windowNumber:[[self window] windowNumber] context:nil eventNumber:1 clickCount:1 pressure:0];
@@ -714,7 +698,7 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
- (void)_searchFieldSearch:(id)sender - (void)_searchFieldSearch:(id)sender
{ {
var searchString = [sender title]; var searchString = [[sender title] substringFromIndex:[RECENT_SEARCH_PREFIX length]];
if ([sender tag] != CPSearchFieldRecentsMenuItemTag) if ([sender tag] != CPSearchFieldRecentsMenuItemTag)
[self _addStringToRecentSearches:searchString]; [self _addStringToRecentSearches:searchString];
@@ -768,75 +752,8 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
_recentSearches = list; _recentSearches = list;
} }
- (void)unbind:(CPString)aBinding
{
[super unbind:aBinding];
// our CPPredicateBinding binder adds a binding to the value.
// this private binding has to be also removed
if (aBinding === CPPredicateBinding)
[[[self class] _binderClassForBinding:aBinding] unbind:CPValueBinding forObject:self];
}
@end @end
#pragma mark -
@implementation CPSearchField (CPTrackingArea)
{
CPTrackingArea _searchButtonTrackingArea;
CPTrackingArea _cancelButtonTrackingArea;
}
- (void)updateTrackingAreas
{
if (_searchButtonTrackingArea)
{
[self removeTrackingArea:_searchButtonTrackingArea];
_searchButtonTrackingArea = nil;
}
if (_cancelButtonTrackingArea)
{
[self removeTrackingArea:_cancelButtonTrackingArea];
_cancelButtonTrackingArea = nil;
}
if (_searchButton)
{
_searchButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_searchButton frame]
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
owner:self
userInfo:@{ @"isButton": YES }];
[self addTrackingArea:_searchButtonTrackingArea];
}
if (_cancelButton)
{
_cancelButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_cancelButton frame]
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
owner:self
userInfo:@{ @"isButton": YES }];
[self addTrackingArea:_cancelButtonTrackingArea];
}
[super updateTrackingAreas];
}
- (void)cursorUpdate:(CPEvent)anEvent
{
if ([[[anEvent trackingArea] userInfo] objectForKey:@"isButton"])
[[CPCursor arrowCursor] set];
else
[super cursorUpdate:anEvent];
}
@end
#pragma mark -
var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey", var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey", CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey", CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
@@ -889,44 +806,3 @@ var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
} }
@end @end
@implementation _CPSearchFieldPredicateBinder : CPBinder
{
CPArrayController _controller;
CPString _predicateFormat;
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
if (aBinding === CPPredicateBinding)
{
var options = [_info objectForKey:CPOptionsKey];
_controller = [_info objectForKey:CPObservedObjectKey];
_predicateFormat = [options objectForKey:"CPPredicateFormat"];
[_source bind:CPValueBinding toObject:self withKeyPath:"searchFieldValue" options:nil];
}
}
- (void)setSearchFieldValue:(CPString)aValue
{
var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey];
var formatString = _predicateFormat.replace(/\$value/g, "%@");
[self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
if (aValue)
[_controller setFilterPredicate:[CPPredicate predicateWithFormat:formatString, aValue]];
else
[_controller setFilterPredicate:nil];
[self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
}
- (CPString)searchFieldValue
{
return [_source stringValue];
}
@end
+201 -439
View File
@@ -23,13 +23,8 @@
@import <Foundation/CPArray.j> @import <Foundation/CPArray.j>
@import "CPControl.j" @import "CPControl.j"
@import "CPWindow_Constants.j"
@import "_CPImageAndTextView.j"
@import "CPMenu.j"
@global CPApp
@typedef CPSegmentSwitchTracking
CPSegmentSwitchTrackingSelectOne = 0; CPSegmentSwitchTrackingSelectOne = 0;
CPSegmentSwitchTrackingSelectAny = 1; CPSegmentSwitchTrackingSelectAny = 1;
CPSegmentSwitchTrackingMomentary = 2; CPSegmentSwitchTrackingMomentary = 2;
@@ -42,7 +37,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
@implementation CPSegmentedControl : CPControl @implementation CPSegmentedControl : CPControl
{ {
CPArray _segments @accessors(getter=segments); CPArray _segments;
CPArray _themeStates; CPArray _themeStates;
int _selectedSegment; int _selectedSegment;
@@ -58,21 +53,10 @@ CPSegmentSwitchTrackingMomentary = 2;
return "segmented-control"; return "segmented-control";
} }
+ (CPDictionary)themeAttributes + (id)themeAttributes
{ {
return @{ return [CPDictionary dictionaryWithObjects:[CPCenterTextAlignment, CPCenterVerticalTextAlignment, CPImageLeft, CPScaleNone, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null], 1.0, 24.0]
@"alignment": CPCenterTextAlignment, forKeys:[@"alignment", @"vertical-alignment", @"image-position", @"image-scaling", @"bezel-inset", @"content-inset", @"left-segment-bezel-color", @"right-segment-bezel-color", @"center-segment-bezel-color", @"divider-bezel-color", @"divider-thickness", @"default-height"]];
@"vertical-alignment": CPCenterVerticalTextAlignment,
@"image-position": CPImageLeft,
@"image-scaling": CPImageScaleNone,
@"bezel-inset": CGInsetMakeZero(),
@"content-inset": CGInsetMakeZero(),
@"left-segment-bezel-color": [CPNull null],
@"right-segment-bezel-color": [CPNull null],
@"center-segment-bezel-color": [CPNull null],
@"divider-bezel-color": [CPNull null],
@"divider-thickness": 1.0,
};
} }
- (id)initWithFrame:(CGRect)aRect - (id)initWithFrame:(CGRect)aRect
@@ -87,76 +71,17 @@ CPSegmentSwitchTrackingMomentary = 2;
_selectedSegment = -1; _selectedSegment = -1;
_trackingMode = CPSegmentSwitchTrackingSelectOne; _trackingMode = CPSegmentSwitchTrackingSelectOne;
_trackingHighlighted = NO;
_trackingSegment = -1;
} }
return self; return self;
} }
/*!
Sets the control's size.
@param aControlSize the control's size
*/
- (void)setControlSize:(CPControlSize)aControlSize
{
[super setControlSize:aControlSize];
[self _sizeToControlSize];
}
/*! /*!
Returns the tag of the selected segment. Returns the tag of the selected segment.
*/ */
- (int)selectedTag - (int)selectedTag
{ {
return [[_segments objectAtIndex:_selectedSegment] tag]; return [_segments[_selectedSegment] tag];
}
/*! @ignore */
- (void)setSegments:(CPArray)segments
{
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self segmentCount])]];
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [segments count])]];
}
/*! @ignore */
- (void)insertSegments:(CPArray)segments atIndexes:(CPIndexSet)indices
{
if ([segments count] == 0)
return;
var newStates = @[],
count = [indices count];
while (count--)
[newStates addObject:CPThemeStateNormal];
[_segments insertObjects:segments atIndexes:indices];
[_themeStates insertObjects:newStates atIndexes:indices];
if (_selectedSegment >= [indices firstIndex])
_selectedSegment += [indices count];
}
/*! @ignore */
- (void)removeSegmentsAtIndexes:(CPIndexSet)indices
{
if ([indices count] == 0)
return;
[indices enumerateIndexesUsingBlock:function(idx, stop)
{
[[_segments objectAtIndex:idx] setSelected:NO];
}];
if ([indices containsIndex:_selectedSegment])
_selectedSegment = -1;
else if ([indices lastIndex] < _selectedSegment)
_selectedSegment -= [indices count];
[_segments removeObjectsAtIndexes:indices];
[_themeStates removeObjectsAtIndexes:indices];
} }
// Specifying the number of segments // Specifying the number of segments
@@ -166,32 +91,39 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (void)setSegmentCount:(unsigned)aCount - (void)setSegmentCount:(unsigned)aCount
{ {
var prevCount = [_segments count]; if (_segments.length == aCount)
if (aCount == prevCount)
return; return;
if (aCount > prevCount) var height = CGRectGetHeight([self bounds]),
dividersBefore = MAX(0, _segments.length - 1),
dividersAfter = MAX(0, aCount - 1);
if (_segments.length < aCount)
{ {
var count = aCount - prevCount, for (var index = _segments.length; index < aCount; ++index)
segments = @[]; {
_segments[index] = [[_CPSegmentItem alloc] init];
while (count--) _themeStates[index] = CPThemeStateNormal;
[segments addObject:[[_CPSegmentItem alloc] init]]; }
}
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(prevCount, aCount - prevCount)]]; else if (aCount < _segments.length)
{
_segments.length = aCount;
_themeStates.length = aCount;
} }
else
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(aCount, prevCount - aCount)]];
[self _updateSelectionIfNeeded]; if (_selectedSegment >= _segments.length)
[self tileWithChangedSegment:MAX(MIN(prevCount, aCount) - 1, 0)];
}
- (void)_updateSelectionIfNeeded
{
if (_selectedSegment >= [self segmentCount])
_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];
} }
/*! /*!
@@ -199,7 +131,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (unsigned)segmentCount - (unsigned)segmentCount
{ {
return [_segments count]; return _segments.length;
} }
// Specifying Selected Segment // Specifying Selected Segment
@@ -211,20 +143,7 @@ CPSegmentSwitchTrackingMomentary = 2;
- (void)setSelectedSegment:(unsigned)aSegment - (void)setSelectedSegment:(unsigned)aSegment
{ {
// setSelected:forSegment throws the exception for us (if necessary) // setSelected:forSegment throws the exception for us (if necessary)
if (_selectedSegment == aSegment) [self setSelected:YES forSegment:aSegment];
return;
if (aSegment == -1)
{
var count = [self segmentCount];
while (count--)
[self setSelected:NO forSegment:count];
_selectedSegment = -1;
}
else
[self setSelected:YES forSegment:aSegment];
} }
/*! /*!
@@ -242,23 +161,8 @@ CPSegmentSwitchTrackingMomentary = 2;
{ {
var index = 0; var index = 0;
for (; index < [_segments count]; ++index) for (; index < _segments.length; ++index)
if ([[_segments objectAtIndex:index] tag] == aTag) if (_segments[index].tag == aTag)
{
[self setSelectedSegment:index];
return YES;
}
return NO;
}
- (BOOL)_selectSegmentWithLabel:(CPString)aLabel
{
var index = 0;
for (; index < [_segments count]; ++index)
if ([[_segments objectAtIndex:index] label] == aLabel)
{ {
[self setSelectedSegment:index]; [self setSelectedSegment:index];
@@ -269,7 +173,7 @@ CPSegmentSwitchTrackingMomentary = 2;
} }
// Specifying Tracking Mode // Specifying Tracking Mode
/*! @ignore */
- (BOOL)isTracking - (BOOL)isTracking
{ {
@@ -287,7 +191,7 @@ CPSegmentSwitchTrackingMomentary = 2;
var index = 0, var index = 0,
selected = NO; selected = NO;
for (; index < [self segmentCount]; ++index) for (; index < _segments.length; ++index)
if ([_segments[index] selected]) if ([_segments[index] selected])
if (selected) if (selected)
[self setSelected:NO forSegment:index]; [self setSelected:NO forSegment:index];
@@ -299,7 +203,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{ {
var index = 0; var index = 0;
for (; index < [self segmentCount]; ++index) for (; index < _segments.length; ++index)
if ([_segments[index] selected]) if ([_segments[index] selected])
[self setSelected:NO forSegment:index]; [self setSelected:NO forSegment:index];
} }
@@ -322,7 +226,8 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment - (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
{ {
[[_segments objectAtIndex:aSegment] setWidth:aWidth]; [_segments[aSegment] setWidth:aWidth];
[self tileWithChangedSegment:aSegment]; [self tileWithChangedSegment:aSegment];
} }
@@ -333,7 +238,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (float)widthForSegment:(unsigned)aSegment - (float)widthForSegment:(unsigned)aSegment
{ {
return [[_segments objectAtIndex:aSegment] width]; return [_segments[aSegment] width];
} }
/*! /*!
@@ -344,7 +249,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment - (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
{ {
[[_segments objectAtIndex:aSegment] setImage:anImage]; [_segments[aSegment] setImage:anImage];
[self tileWithChangedSegment:aSegment]; [self tileWithChangedSegment:aSegment];
} }
@@ -356,7 +261,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (CPImage)imageForSegment:(unsigned)aSegment - (CPImage)imageForSegment:(unsigned)aSegment
{ {
return [[_segments objectAtIndex:aSegment] image]; return [_segments[aSegment] image];
} }
/*! /*!
@@ -367,7 +272,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment - (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
{ {
[[_segments objectAtIndex:aSegment] setLabel:aLabel]; [_segments[aSegment] setLabel:aLabel];
[self tileWithChangedSegment:aSegment]; [self tileWithChangedSegment:aSegment];
} }
@@ -379,7 +284,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (CPString)labelForSegment:(unsigned)aSegment - (CPString)labelForSegment:(unsigned)aSegment
{ {
return [[_segments objectAtIndex:aSegment] label]; return [_segments[aSegment] label];
} }
/*! /*!
@@ -390,7 +295,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment - (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
{ {
[[_segments objectAtIndex:aSegment] setMenu:aMenu]; [_segments[aSegment] setMenu:aMenu];
} }
/*! /*!
@@ -400,7 +305,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (CPMenu)menuForSegment:(unsigned)aSegment - (CPMenu)menuForSegment:(unsigned)aSegment
{ {
return [[_segments objectAtIndex:aSegment] menu]; return [_segments[aSegment] menu];
} }
/*! /*!
@@ -412,7 +317,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment - (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
{ {
var segment = [_segments objectAtIndex:aSegment]; var segment = _segments[aSegment];
// If we're already in this state, bail. // If we're already in this state, bail.
if ([segment selected] == isSelected) if ([segment selected] == isSelected)
@@ -429,7 +334,7 @@ CPSegmentSwitchTrackingMomentary = 2;
_selectedSegment = aSegment; _selectedSegment = aSegment;
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1 && oldSelectedSegment < _segments.length) if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
{ {
[_segments[oldSelectedSegment] setSelected:NO]; [_segments[oldSelectedSegment] setSelected:NO];
_themeStates[oldSelectedSegment] = CPThemeStateNormal; _themeStates[oldSelectedSegment] = CPThemeStateNormal;
@@ -452,7 +357,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (BOOL)isSelectedForSegment:(unsigned)aSegment - (BOOL)isSelectedForSegment:(unsigned)aSegment
{ {
return [[_segments objectAtIndex:aSegment] selected]; return [_segments[aSegment] selected];
} }
/*! /*!
@@ -461,19 +366,14 @@ CPSegmentSwitchTrackingMomentary = 2;
@param aSegment the segment to enable/disable @param aSegment the segment to enable/disable
@throws CPRangeException if \c aSegment is out of bounds @throws CPRangeException if \c aSegment is out of bounds
*/ */
- (void)setEnabled:(BOOL)shouldBeEnabled forSegment:(unsigned)aSegment - (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
{ {
var segment = [_segments objectAtIndex:aSegment]; [_segments[aSegment] setEnabled:isEnabled];
if ([segment enabled] === shouldBeEnabled) if (isEnabled)
return; _themeStates[aSegment] &= ~CPThemeStateDisabled;
[segment setEnabled:shouldBeEnabled];
if (shouldBeEnabled)
_themeStates[aSegment] = _themeStates[aSegment].without(CPThemeStateDisabled);
else else
_themeStates[aSegment] = _themeStates[aSegment].and(CPThemeStateDisabled); _themeStates[aSegment] |= CPThemeStateDisabled;
[self setNeedsLayout]; [self setNeedsLayout];
[self setNeedsDisplay:YES]; [self setNeedsDisplay:YES];
@@ -486,7 +386,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (BOOL)isEnabledForSegment:(unsigned)aSegment - (BOOL)isEnabledForSegment:(unsigned)aSegment
{ {
return [[_segments objectAtIndex:aSegment] enabled]; return [_segments[aSegment] enabled];
} }
/*! /*!
@@ -496,7 +396,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment - (void)setTag:(int)aTag forSegment:(unsigned)aSegment
{ {
[[_segments objectAtIndex:aSegment] setTag:aTag]; [_segments[aSegment] setTag:aTag];
} }
/*! /*!
@@ -505,7 +405,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (int)tagForSegment:(unsigned)aSegment - (int)tagForSegment:(unsigned)aSegment
{ {
return [[_segments objectAtIndex:aSegment] tag]; return [_segments[aSegment] tag];
} }
// Drawings // Drawings
@@ -516,13 +416,10 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight - (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight
{ {
if (aSegment < _themeStates.length) if (shouldHighlight)
{ _themeStates[aSegment] |= CPThemeStateHighlighted;
if (shouldHighlight) else
_themeStates[aSegment] = _themeStates[aSegment].and(CPThemeStateHighlighted); _themeStates[aSegment] &= ~CPThemeStateHighlighted;
else
_themeStates[aSegment] = _themeStates[aSegment].without(CPThemeStateHighlighted);
}
[self setNeedsLayout]; [self setNeedsLayout];
[self setNeedsDisplay:YES]; [self setNeedsDisplay:YES];
@@ -530,12 +427,14 @@ CPSegmentSwitchTrackingMomentary = 2;
- (float)_leftOffsetForSegment:(unsigned)segment - (float)_leftOffsetForSegment:(unsigned)segment
{ {
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
if (segment == 0) if (segment == 0)
return [self currentValueForThemeAttribute:@"bezel-inset"].left; return bezelInset.left;
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"]; var thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
return [self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) + thickness; return [self _leftOffsetForSegment:segment - 1] + [self widthForSegment:segment - 1] + thickness;
} }
- (unsigned)_indexOfLastSegment - (unsigned)_indexOfLastSegment
@@ -550,7 +449,7 @@ CPSegmentSwitchTrackingMomentary = 2;
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
{ {
var height = [self currentValueForThemeAttribute:@"min-size"].height, var height = [self currentValueForThemeAttribute:@"default-height"],
contentInset = [self currentValueForThemeAttribute:@"content-inset"], contentInset = [self currentValueForThemeAttribute:@"content-inset"],
bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"], bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
bounds = [self bounds]; bounds = [self bounds];
@@ -561,7 +460,7 @@ CPSegmentSwitchTrackingMomentary = 2;
} }
else if (aName === "right-segment-bezel") else if (aName === "right-segment-bezel")
{ {
return CGRectMake(CGRectGetWidth([self bounds]) - contentInset.right, return CPRectMake(CGRectGetWidth([self bounds]) - contentInset.right,
bezelInset.top, bezelInset.top,
contentInset.right, contentInset.right,
height); height);
@@ -569,7 +468,7 @@ CPSegmentSwitchTrackingMomentary = 2;
else if (aName.indexOf("segment-bezel") === 0) else if (aName.indexOf("segment-bezel") === 0)
{ {
var segment = parseInt(aName.substring("segment-bezel-".length), 10), var segment = parseInt(aName.substring("segment-bezel-".length), 10),
frame = CGRectCreateCopy([self frameForSegment:segment]); frame = CGRectCreateCopy([_segments[segment] frame]);
if (segment === 0) if (segment === 0)
{ {
@@ -577,7 +476,7 @@ CPSegmentSwitchTrackingMomentary = 2;
frame.size.width -= contentInset.left; frame.size.width -= contentInset.left;
} }
if (segment === [self segmentCount] - 1) if (segment === _segments.length - 1)
frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x; frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
return frame; return frame;
@@ -585,7 +484,7 @@ CPSegmentSwitchTrackingMomentary = 2;
else if (aName.indexOf("divider-bezel") === 0) else if (aName.indexOf("divider-bezel") === 0)
{ {
var segment = parseInt(aName.substring("divider-bezel-".length), 10), var segment = parseInt(aName.substring("divider-bezel-".length), 10),
width = CGRectGetWidth([self frameForSegment:segment]), width = [self widthForSegment:segment],
left = [self _leftOffsetForSegment:segment], left = [self _leftOffsetForSegment:segment],
thickness = [self currentValueForThemeAttribute:@"divider-thickness"]; thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
@@ -604,32 +503,19 @@ CPSegmentSwitchTrackingMomentary = 2;
- (CPView)createEphemeralSubviewNamed:(CPString)aName - (CPView)createEphemeralSubviewNamed:(CPString)aName
{ {
if ([aName hasPrefix:@"segment-content"]) if ([aName hasPrefix:@"segment-content"])
{ return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
var view = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
[view _setUsesSingleLineMode:YES];
return view; return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
}
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
} }
- (void)layoutSubviews - (void)layoutSubviews
{ {
if ([self segmentCount] <= 0) if (_segments.length <= 0)
return; return;
var themeState = _themeStates[0], var themeState = _themeStates[0];
isDisabled = [self hasThemeState:CPThemeStateDisabled],
isControlSizeSmall = [self hasThemeState:CPThemeStateControlSizeSmall],
isControlSizeMini = [self hasThemeState:CPThemeStateControlSizeMini];
themeState = isDisabled ? themeState.and(CPThemeStateDisabled) : themeState; themeState |= _themeState & CPThemeStateDisabled;
if (isControlSizeSmall)
themeState = themeState.and(CPThemeStateControlSizeSmall);
else if (isControlSizeMini)
themeState = themeState.and(CPThemeStateControlSizeMini);
var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color" var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
inState:themeState], inState:themeState],
@@ -642,12 +528,7 @@ CPSegmentSwitchTrackingMomentary = 2;
var themeState = _themeStates[_themeStates.length - 1]; var themeState = _themeStates[_themeStates.length - 1];
themeState = isDisabled ? themeState.and(CPThemeStateDisabled) : themeState; themeState |= _themeState & CPThemeStateDisabled;
if (isControlSizeSmall)
themeState = themeState.and(CPThemeStateControlSizeSmall);
else if (isControlSizeMini)
themeState = themeState.and(CPThemeStateControlSizeMini);
var rightCapColor = [self valueForThemeAttribute:@"right-segment-bezel-color" var rightCapColor = [self valueForThemeAttribute:@"right-segment-bezel-color"
inState:themeState], inState:themeState],
@@ -662,12 +543,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{ {
var themeState = _themeStates[i]; var themeState = _themeStates[i];
themeState = isDisabled ? themeState.and(CPThemeStateDisabled) : themeState; themeState |= _themeState & CPThemeStateDisabled;
if (isControlSizeSmall)
themeState = themeState.and(CPThemeStateControlSizeSmall);
else if (isControlSizeMini)
themeState = themeState.and(CPThemeStateControlSizeMini);
var bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color" var bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color"
inState:themeState], inState:themeState],
@@ -678,6 +554,7 @@ CPSegmentSwitchTrackingMomentary = 2;
[bezelView setBackgroundColor:bezelColor]; [bezelView setBackgroundColor:bezelColor];
// layout image/title views // layout image/title views
var segment = _segments[i], var segment = _segments[i],
contentView = [self layoutEphemeralSubviewNamed:@"segment-content-" + i contentView = [self layoutEphemeralSubviewNamed:@"segment-content-" + i
@@ -704,15 +581,11 @@ CPSegmentSwitchTrackingMomentary = 2;
if (i == count - 1) if (i == count - 1)
continue; continue;
var borderState = _themeStates[i] | _themeStates[i + 1];
var borderState = _themeStates[i].and(_themeStates[i + 1]); borderState = (borderState & CPThemeStateSelected & ~CPThemeStateHighlighted) ? CPThemeStateSelected : CPThemeStateNormal;
borderState = isDisabled ? borderState.and(CPThemeStateDisabled) : borderState; borderState |= _themeState & CPThemeStateDisabled;
if (isControlSizeSmall)
borderState = borderState.and(CPThemeStateControlSizeSmall);
else if (isControlSizeMini)
borderState = borderState.and(CPThemeStateControlSizeMini);
var borderColor = [self valueForThemeAttribute:@"divider-bezel-color" var borderColor = [self valueForThemeAttribute:@"divider-bezel-color"
inState:borderState], inState:borderState],
@@ -735,33 +608,61 @@ CPSegmentSwitchTrackingMomentary = 2;
{ {
} }
/*! @ignore */ - (void)tileWithChangedSegment:(unsigned)aSegment
- (void)tile
{ {
[self tileWithChangedSegment:0]; if (aSegment >= _segments.length)
}
/*! @ignore */
- (void)tileWithChangedSegment:(CPInteger)aSegment
{
var segmentCount = [self segmentCount];
// Corner case: when segmentCount == 0 and aSegment == 0, we do not return here because we still need to set the new frameSize bellow.
if (aSegment < 0 || (segmentCount > 0 && aSegment >= segmentCount))
return; return;
var width = 0; var segment = _segments[aSegment],
segmentWidth = [segment width],
themeState = _themeStates[aSegment] | (_themeState & CPThemeStateDisabled),
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
font = [self font];
if (segmentCount > 0) if (!segmentWidth)
{ {
// Invalidate frames for segments on the right. They will be lazily computed by -frameForSegment:. if ([segment image] && [segment label])
for (var i = aSegment; i < segmentCount; i++) segmentWidth = [[segment label] sizeWithFont:font].width + [[segment image] size].width + contentInset.left + contentInset.right;
[_segments[i] setFrame:CGRectMakeZero()]; else if (segment.image)
segmentWidth = [[segment image] size].width + contentInset.left + contentInset.right;
width = CGRectGetMaxX([self frameForSegment:(segmentCount - 1)]); else if (segment.label)
segmentWidth = [[segment label] sizeWithFont:font].width + contentInset.left + contentInset.right;
else
segmentWidth = 0.0;
} }
[self setFrameSize:CGSizeMake(width, CGRectGetHeight([self frame]))]; var delta = segmentWidth - CGRectGetWidth([segment frame]);
if (!delta)
{
[self setNeedsLayout];
[self setNeedsDisplay:YES];
return;
}
// Update control size
var frame = [self frame];
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
// Update segment width
[segment setWidth:segmentWidth];
[segment setFrame:[self frameForSegment:aSegment]];
// Update following segments widths
var index = aSegment + 1;
for (; index < _segments.length; ++index)
{
[_segments[index] frame].origin.x += delta;
[self drawSegmentBezel:index highlight:NO];
[self drawSegment:index highlight:NO];
}
[self drawSegmentBezel:aSegment highlight:NO];
[self drawSegment:aSegment highlight:NO];
[self setNeedsLayout]; [self setNeedsLayout];
[self setNeedsDisplay:YES]; [self setNeedsDisplay:YES];
@@ -773,57 +674,29 @@ CPSegmentSwitchTrackingMomentary = 2;
*/ */
- (CGRect)frameForSegment:(unsigned)aSegment - (CGRect)frameForSegment:(unsigned)aSegment
{ {
var segment = [_segments objectAtIndex:aSegment], return [self bezelFrameForSegment:aSegment];
frame = [segment frame];
if (CGRectEqualToRect(frame, CGRectMakeZero()))
{
frame = [self bezelFrameForSegment:aSegment];
[segment setFrame:frame];
}
return frame;
} }
- (CGRect)bezelFrameForSegment:(unsigned)aSegment - (CGRect)bezelFrameForSegment:(unsigned)aSegment
{ {
var left = [self _leftOffsetForSegment:aSegment], var height = [self currentValueForThemeAttribute:@"default-height"],
top = [self currentValueForThemeAttribute:@"bezel-inset"].top, bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
width = [self widthForSegment:aSegment], width = [self widthForSegment:aSegment],
height = [self currentValueForThemeAttribute:@"min-size"].height; left = [self _leftOffsetForSegment:aSegment];
if (width == 0) return CGRectMake(left, bezelInset.top, width, height);
{
var themeState = _themeState.hasThemeState(CPThemeStateDisabled) ? _themeStates[aSegment].and(CPThemeStateDisabled) : _themeStates[aSegment],
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
contentInsetWidth = contentInset.left + contentInset.right,
segment = _segments[aSegment],
label = [segment label],
image = [segment image];
width = (label ? [label sizeWithFont:[self font]].width : 4.0) + (image ? [image size].width : 0) + contentInsetWidth;
}
return CGRectMake(left, top, width, height);
} }
- (CGRect)contentFrameForSegment:(unsigned)aSegment - (CGRect)contentFrameForSegment:(unsigned)aSegment
{ {
var height = [self currentValueForThemeAttribute:@"min-size"].height, var height = [self currentValueForThemeAttribute:@"default-height"],
contentInset = [self currentValueForThemeAttribute:@"content-inset"], contentInset = [self currentValueForThemeAttribute:@"content-inset"],
width = CGRectGetWidth([self frameForSegment:aSegment]), width = [self widthForSegment:aSegment],
left = [self _leftOffsetForSegment:aSegment]; left = [self _leftOffsetForSegment:aSegment];
return CGRectMake(left + contentInset.left, contentInset.top, width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom); return CGRectMake(left + contentInset.left, contentInset.top, width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom);
} }
- (CGSize)_minimumFrameSize
{
// The current width is always the minimum width.
return CGSizeMake(CGRectGetWidth([self frame]), [self currentValueForThemeAttribute:@"min-size"].height);
}
/*! /*!
Returns the segment that is hit by the specified point. Returns the segment that is hit by the specified point.
@param aPoint the point to test for a segment hit @param aPoint the point to test for a segment hit
@@ -832,19 +705,19 @@ CPSegmentSwitchTrackingMomentary = 2;
- (unsigned)testSegment:(CGPoint)aPoint - (unsigned)testSegment:(CGPoint)aPoint
{ {
var location = [self convertPoint:aPoint fromView:nil], var location = [self convertPoint:aPoint fromView:nil],
count = [self segmentCount]; count = _segments.length;
while (count--) while (count--)
if (CGRectContainsPoint([self frameForSegment:count], aPoint)) if (CGRectContainsPoint([_segments[count] frame], aPoint))
return count; return count;
if ([self segmentCount]) if (_segments.length)
{ {
var adjustedLastFrame = CGRectCreateCopy([self frameForSegment:(_segments.length - 1)]); var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x; adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
if (CGRectContainsPoint(adjustedLastFrame, aPoint)) if (CGRectContainsPoint(adjustedLastFrame, aPoint))
return [self segmentCount] - 1; return _segments.length - 1;
} }
return -1; return -1;
@@ -872,68 +745,63 @@ CPSegmentSwitchTrackingMomentary = 2;
var type = [anEvent type], var type = [anEvent type],
location = [self convertPoint:[anEvent locationInWindow] fromView:nil]; location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
switch (type) if (type == CPLeftMouseUp)
{ {
case CPLeftMouseUp: if (_trackingSegment == -1)
if (_trackingSegment === CPNotFound)
return;
if (_trackingSegment === [self testSegment:location])
{
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
{
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
// With ANY, _selectedSegment means last pressed.
_selectedSegment = _trackingSegment;
}
else
[self setSelected:YES forSegment:_trackingSegment];
[self sendAction:[self action] to:[self target]];
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
{
[self setSelected:NO forSegment:_trackingSegment];
_selectedSegment = CPNotFound;
}
}
[self drawSegmentBezel:_trackingSegment highlight:NO];
_trackingSegment = CPNotFound;
return; return;
case CPLeftMouseDown: if (_trackingSegment === [self testSegment:location])
{
var trackingSegment = [self testSegment:location]; if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
if (trackingSegment > CPNotFound && [self isEnabledForSegment:trackingSegment])
{ {
_trackingHighlighted = YES; [self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
_trackingSegment = trackingSegment;
[self drawSegmentBezel:_trackingSegment highlight:YES]; // With ANY, _selectedSegment means last pressed.
_selectedSegment = _trackingSegment;
} }
else
[self setSelected:YES forSegment:_trackingSegment];
break; [self sendAction:[self action] to:[self target]];
case CPLeftMouseDragged: if (_trackingMode == CPSegmentSwitchTrackingMomentary)
if (_trackingSegment === CPNotFound)
return;
var highlighted = [self testSegment:location] === _trackingSegment;
if (highlighted != _trackingHighlighted)
{ {
_trackingHighlighted = highlighted; [self setSelected:NO forSegment:_trackingSegment];
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted]; _selectedSegment = -1;
} }
}
break; [self drawSegmentBezel:_trackingSegment highlight:NO];
_trackingSegment = -1;
return;
}
if (type == CPLeftMouseDown)
{
var trackingSegment = [self testSegment:location];
if (trackingSegment > -1 && [self isEnabledForSegment:trackingSegment])
{
_trackingHighlighted = YES;
_trackingSegment = trackingSegment;
[self drawSegmentBezel:_trackingSegment highlight:YES];
}
}
else if (type == CPLeftMouseDragged)
{
if (_trackingSegment == -1)
return;
var highlighted = [self testSegment:location] === _trackingSegment;
if (highlighted != _trackingHighlighted)
{
_trackingHighlighted = highlighted;
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
}
} }
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; [CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
@@ -943,7 +811,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{ {
[super setFont:aFont]; [super setFont:aFont];
[self tile]; [self tileWithChangedSegment:0];
} }
@end @end
@@ -975,49 +843,28 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey]) if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
_selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey]; _selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
else else
_selectedSegment = CPNotFound; _selectedSegment = -1;
if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey]) if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey])
_trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey]; _trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
else else
_trackingMode = CPSegmentSwitchTrackingSelectOne; _trackingMode = CPSegmentSwitchTrackingSelectOne;
// Here we update the themeStates array for each segments to know if there are selected or not // HACK
for (var i = 0; i < [self segmentCount]; i++)
for (var i = 0; i < _segments.length; i++)
{
_themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal; _themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal;
[self tileWithChangedSegment:i];
[self tile];
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
dividerExtraSpace = ([_segments count] - 1) * thickness,
difference = MAX(originalWidth - [self frame].size.width - dividerExtraSpace, 0.0),
remainingWidth = FLOOR(difference / [self segmentCount]),
widthOfAllSegments = 0;
// We do this in a second loop because it relies on all the themeStates being set first
for (var i = 0; i < [self segmentCount]; i++)
{
var frame = [_segments[i] frame];
frame.size.width += remainingWidth;
widthOfAllSegments += CGRectGetWidth(frame);
} }
// Here we handle the leftovers pixel, and we will add one pixel to each segment cell till we have the same size as the originalSize. var difference = MAX(originalWidth - [self frame].size.width, 0.0),
// This is needed to have a perfect/same alignment between our application and xCode. remainingWidth = FLOOR(difference / _segments.length);
var leftOversPixel = originalWidth - (widthOfAllSegments + dividerExtraSpace);
// Make sure we don't make an out of range for (var i = 0; i < _segments.length; i++)
if (leftOversPixel < [self segmentCount] - 1) [self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
{
for (var i = 0; i < leftOversPixel; i++)
{
[_segments[i] frame].size.width += 1;
}
}
[self setFrameSize:CGSizeMake(originalWidth, CGRectGetHeight([self frame]))]; [self tileWithChangedSegment:0];
[self tile];
} }
return self; return self;
@@ -1035,91 +882,6 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
@end @end
@implementation CPSegmentedControl (BindingSupport)
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if ([self _isSelectionBinding:aBinding])
return [_CPSegmentedControlBinder class];
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)_isSelectionBinding:(CPString)aBinding
{
return (aBinding === CPSelectedIndexBinding || aBinding === CPSelectedLabelBinding || aBinding === CPSelectedTagBinding);
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return [self _isSelectionBinding:aBinding];
}
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
{
if ([[self class] _isSelectionBinding:aBinding] && _trackingMode !== CPSegmentSwitchTrackingSelectOne)
{
CPLog.warn("Binding " + aBinding + " needs CPSegmentSwitchTrackingSelectOne tracking mode");
return;
}
[super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
}
- (void)_reverseSetBinding
{
[_CPSegmentedControlBinder _reverseSetValueFromExclusiveBinderForObject:self];
}
@end
var CPSegmentedControlNoSelectionPlaceholder = "CPSegmentedControlNoSelectionPlaceholder";
@implementation _CPSegmentedControlBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
[super _updatePlaceholdersWithOptions:options];
[self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPMultipleValuesMarker isDefault:YES];
[self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNoSelectionMarker isDefault:YES];
[self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNotApplicableMarker isDefault:YES];
[self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNullMarker isDefault:YES];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
if (aValue == CPSegmentedControlNoSelectionPlaceholder)
[_source setSelected:NO forSegment:[_source selectedSegment]];
else
[self setValue:aValue forBinding:aBinding];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
if (aBinding == CPSelectedIndexBinding)
[_source setSelectedSegment:aValue];
else if (aBinding == CPSelectedTagBinding)
[_source selectSegmentWithTag:aValue];
else if (aBinding == CPSelectedLabelBinding)
[_source _selectSegmentWithLabel:aValue];
}
- (id)valueForBinding:(CPString)aBinding
{
var selectedIndex = [_source selectedSegment];
if (aBinding == CPSelectedIndexBinding)
return selectedIndex;
else if (aBinding == CPSelectedTagBinding)
return [_source tagForSegment:selectedIndex];
else if (aBinding == CPSelectedLabelBinding)
return [_source labelForSegment:selectedIndex];
}
@end
@implementation _CPSegmentItem : CPObject @implementation _CPSegmentItem : CPObject
{ {
+30 -12
View File
@@ -22,9 +22,6 @@
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import "CGGeometry.j"
@import "CPColor.j"
@import "CPGraphicsContext.j"
/*! /*!
@deprecated @deprecated
@@ -34,9 +31,11 @@
*/ */
@implementation CPShadow : CPObject @implementation CPShadow : CPObject
{ {
CGSize _offset @accessors(property=shadowOffset); CPSize _offset;
float _blurRadius @accessors(property=shadowBlurRadius); float _blurRadius;
CPColor _color @accessors(property=shadowColor); CPColor _color;
CPString _cssString;
} }
/*! /*!
@@ -52,7 +51,7 @@
} }
/* @ignore */ /* @ignore */
- (id)_initWithOffset:(CGSize)anOffset blurRadius:(float)aBlurRadius color:(CPColor)aColor - (id)_initWithOffset:(CPSize)anOffset blurRadius:(float)aBlurRadius color:(CPColor)aColor
{ {
self = [super init]; self = [super init];
@@ -61,16 +60,35 @@
_offset = anOffset; _offset = anOffset;
_blurRadius = aBlurRadius; _blurRadius = aBlurRadius;
_color = aColor; _color = aColor;
_cssString = [_color cssString] + " " + Math.round(anOffset.width) + @"px " + Math.round(anOffset.height) + @"px " + Math.round(_blurRadius) + @"px";
} }
return self; return self;
} }
- (void)set /*!
Returns the shadow's offset.
*/
- (CGSize)shadowOffset
{ {
var context = [[CPGraphicsContext currentContext] graphicsPort]; return _offset;
}
CGContextSetShadowWithColor(context, _offset, _blurRadius, _color); /*!
Returns the shadow's blur radius
*/
- (float)shadowBlurRadius
{
return _blurRadius;
}
/*!
Returns the shadow's color.
*/
- (CPColor)shadowColor
{
return _color;
} }
/*! /*!
@@ -78,7 +96,7 @@
*/ */
- (CPString)cssString - (CPString)cssString
{ {
return [_color cssString] + " " + ROUND(_offset.width) + @"px " + ROUND(_offset.height) + @"px " + ROUND(_blurRadius) + @"px"; return _cssString;
} }
@end @end
+82 -55
View File
@@ -24,43 +24,71 @@
@import "CGGeometry.j" @import "CGGeometry.j"
@import "CPImage.j" @import "CPImage.j"
@import "CPImageView.j"
@import "CPView.j" @import "CPView.j"
@typedef CPShadowWeight
CPLightShadow = 0; CPLightShadow = 0;
CPHeavyShadow = 1; CPHeavyShadow = 1;
CPThemeStateShadowViewLight = CPThemeState("shadowview-style-light"); var CPShadowViewLightBackgroundColor = nil,
CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy"); CPShadowViewHeavyBackgroundColor = nil;
var LIGHT_LEFT_INSET = 3.0,
LIGHT_RIGHT_INSET = 3.0,
LIGHT_TOP_INSET = 3.0,
LIGHT_BOTTOM_INSET = 5.0,
HEAVY_LEFT_INSET = 7.0,
HEAVY_RIGHT_INSET = 7.0,
HEAVY_TOP_INSET = 5.0,
HEAVY_BOTTOM_INSET = 5.0;
/*! /*!
@ingroup appkit @ingroup appkit
*/ */
@implementation CPShadowView : CPView @implementation CPShadowView : CPView
{ {
CPShadowWeight _weight; CPShadowWeight _weight;
} }
+ (CPString)defaultThemeClass + (void)initialize
{ {
return "shadow-view"; if (self != [CPShadowView class])
} return;
+ (CPDictionary)themeAttributes var bundle = [CPBundle bundleForClass:[self class]];
{
return @{
@"bezel-color": [CPNull null],
@"content-inset": CGInsetMakeZero()
};
}
+ (CGRect)frameForContentFrame:(CGRect)aFrame withWeight:(CPShadowWeight)aWeight CPShadowViewLightBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
{ [
var shadowView = [CPShadowView new], [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopLeft.png"] size:CGSizeMake(9.0, 9.0)],
inset = [shadowView valueForThemeAttribute:@"content-inset" inState:(aWeight == CPLightShadow) ? CPThemeStateShadowViewLight : CPThemeStateShadowViewHeavy]; [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTop.png"] size:CGSizeMake(1.0, 9.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopRight.png"] size:CGSizeMake(9.0, 9.0)],
return CGRectMake(CGRectGetMinX(aFrame) - inset.left, CGRectGetMinY(aFrame) - inset.top, CGRectGetWidth(aFrame) + inset.left + inset.right, CGRectGetHeight(aFrame) + inset.top + inset.bottom); [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightLeft.png"] size:CGSizeMake(9.0, 1.0)],
nil,
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightRight.png"] size:CGSizeMake(9.0, 1.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomLeft.png"] size:CGSizeMake(9.0, 9.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottom.png"] size:CGSizeMake(1.0, 9.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomRight.png"] size:CGSizeMake(9.0, 9.0)]
]]];
CPShadowViewHeavyBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
[
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopLeft.png"] size:CGSizeMake(17.0, 17.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTop.png"] size:CGSizeMake(1.0, 17.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopRight.png"] size:CGSizeMake(17.0, 17.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyLeft.png"] size:CGSizeMake(17.0, 1.0)],
nil,
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyRight.png"] size:CGSizeMake(17.0, 1.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomLeft.png"] size:CGSizeMake(17.0, 17.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottom.png"] size:CGSizeMake(1.0, 17.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomRight.png"] size:CGSizeMake(17.0, 17.0)]
]]];
} }
+ (id)shadowViewEnclosingView:(CPView)aView + (id)shadowViewEnclosingView:(CPView)aView
@@ -70,25 +98,20 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
+ (id)shadowViewEnclosingView:(CPView)aView withWeight:(CPShadowWeight)aWeight + (id)shadowViewEnclosingView:(CPView)aView withWeight:(CPShadowWeight)aWeight
{ {
var shadowView = [[self alloc] initWithFrame:[aView frame]]; var shadowView = [[CPShadowView alloc] initWithFrame:[aView frame]];
[shadowView setWeight:aWeight];
if (shadowView) var size = [shadowView frame].size,
{ width = size.width - [shadowView leftInset] - [shadowView rightInset],
[shadowView setWeight:aWeight]; height = size.height - [shadowView topInset] - [shadowView bottomInset],
enclosingView = [aView superview];
var size = [shadowView frame].size, [shadowView setHitTests:[aView hitTests]];
inset = [shadowView currentValueForThemeAttribute:@"content-inset"], [shadowView setAutoresizingMask:[aView autoresizingMask]];
width = size.width - inset.left - inset.right, [aView removeFromSuperview];
height = size.height - inset.top - inset.bottom, [shadowView addSubview:aView];
enclosingView = [aView superview]; [aView setFrame:CGRectMake([shadowView leftInset], [shadowView topInset], width, height)]
[enclosingView addSubview:shadowView];
[shadowView setHitTests:[aView hitTests]];
[shadowView setAutoresizingMask:[aView autoresizingMask]];
[aView removeFromSuperview];
[shadowView addSubview:aView];
[aView setFrame:CGRectMake(inset.left, inset.top, width, height)];
[enclosingView addSubview:shadowView];
}
return shadowView; return shadowView;
} }
@@ -99,7 +122,9 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
if (self) if (self)
{ {
[self setWeight:CPLightShadow]; _weight = CPLightShadow;
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
[self setHitTests:NO]; [self setHitTests:NO];
} }
@@ -115,45 +140,54 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
_weight = aWeight; _weight = aWeight;
if (_weight == CPLightShadow) if (_weight == CPLightShadow)
[self setThemeState:CPThemeStateShadowViewLight]; [self setBackgroundColor:CPShadowViewLightBackgroundColor];
else
[self setThemeState:CPThemeStateShadowViewHeavy];
[self setNeedsLayout]; else
[self setBackgroundColor:CPShadowViewHeavyBackgroundColor];
} }
- (float)leftInset - (float)leftInset
{ {
return [self currentValueForThemeAttribute:@"content-inset"].left; return _weight == CPLightShadow ? LIGHT_LEFT_INSET : HEAVY_LEFT_INSET;
} }
- (float)rightInset - (float)rightInset
{ {
return [self currentValueForThemeAttribute:@"content-inset"].right; return _weight == CPLightShadow ? LIGHT_RIGHT_INSET : HEAVY_RIGHT_INSET;
} }
- (float)topInset - (float)topInset
{ {
return [self currentValueForThemeAttribute:@"content-inset"].top; return _weight == CPLightShadow ? LIGHT_TOP_INSET : HEAVY_TOP_INSET;
} }
- (float)bottomInset - (float)bottomInset
{ {
return [self currentValueForThemeAttribute:@"content-inset"].bottom; return _weight == CPLightShadow ? LIGHT_BOTTOM_INSET : HEAVY_BOTTOM_INSET;
} }
- (float)horizontalInset - (float)horizontalInset
{ {
var currentContentInset = [self currentValueForThemeAttribute:@"content-inset"]; if (_weight == CPLightShadow)
return LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET;
return currentContentInset.left + currentContentInset.right; return HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET;
} }
- (float)verticalInset - (float)verticalInset
{ {
var currentContentInset = [self currentValueForThemeAttribute:@"content-inset"]; if (_weight == CPLightShadow)
return LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET;
return currentContentInset.top + currentContentInset.bottom; return HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET;
}
+ (CGRect)frameForContentFrame:(CGRect)aFrame withWeight:(CPShadowWeight)aWeight
{
if (aWeight == CPLightShadow)
return CGRectMake(_CGRectGetMinX(aFrame) - LIGHT_LEFT_INSET, _CGRectGetMinY(aFrame) - LIGHT_TOP_INSET, _CGRectGetWidth(aFrame) + LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET, _CGRectGetHeight(aFrame) + LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET);
else
return CGRectMake(_CGRectGetMinX(aFrame) - HEAVY_LEFT_INSET, _CGRectGetMinY(aFrame) - HEAVY_TOP_INSET, _CGRectGetWidth(aFrame) + HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET, _CGRectGetHeight(aFrame) + HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET);
} }
- (CGRect)frameForContentFrame:(CGRect)aFrame - (CGRect)frameForContentFrame:(CGRect)aFrame
@@ -166,11 +200,4 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
[self setFrame:[self frameForContentFrame:aFrame]]; [self setFrame:[self frameForContentFrame:aFrame]];
} }
- (void)layoutSubviews
{
[super layoutSubviews];
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
}
@end @end
+28 -35
View File
@@ -21,7 +21,6 @@
*/ */
@import "CPControl.j" @import "CPControl.j"
@import "CPWindow_Constants.j"
/*! SLIDER STATES */ /*! SLIDER STATES */
@@ -40,8 +39,6 @@ CPCircularSlider = 1;
double _altIncrementValue; double _altIncrementValue;
BOOL _isVertical; BOOL _isVertical;
CGSize _dragOffset;
} }
+ (CPString)defaultThemeClass + (CPString)defaultThemeClass
@@ -49,14 +46,10 @@ CPCircularSlider = 1;
return "slider"; return "slider";
} }
+ (CPDictionary)themeAttributes + (id)themeAttributes
{ {
return @{ return [CPDictionary dictionaryWithObjects:[[CPNull null], _CGSizeMakeZero(), 0.0, [CPNull null]]
@"knob-color": [CPNull null], forKeys:[@"knob-color", @"knob-size", @"track-width", @"track-color"]];
@"knob-size": CGSizeMakeZero(),
@"track-width": 0.0,
@"track-color": [CPNull null],
};
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
@@ -162,16 +155,16 @@ CPCircularSlider = 1;
var trackWidth = [self currentValueForThemeAttribute:@"track-width"]; var trackWidth = [self currentValueForThemeAttribute:@"track-width"];
if (trackWidth <= 0) if (trackWidth <= 0)
return CGRectMakeZero(); return _CGRectMakeZero();
if ([self isVertical]) if ([self isVertical])
{ {
bounds.origin.x = (CGRectGetWidth(bounds) - trackWidth) / 2.0; bounds.origin.x = (_CGRectGetWidth(bounds) - trackWidth) / 2.0;
bounds.size.width = trackWidth; bounds.size.width = trackWidth;
} }
else else
{ {
bounds.origin.y = (CGRectGetHeight(bounds) - trackWidth) / 2.0; bounds.origin.y = (_CGRectGetHeight(bounds) - trackWidth) / 2.0;
bounds.size.height = trackWidth; bounds.size.height = trackWidth;
} }
} }
@@ -184,13 +177,13 @@ CPCircularSlider = 1;
var knobSize = [self currentValueForThemeAttribute:@"knob-size"]; var knobSize = [self currentValueForThemeAttribute:@"knob-size"];
if (knobSize.width <= 0 || knobSize.height <= 0) if (knobSize.width <= 0 || knobSize.height <= 0)
return CGRectMakeZero(); return _CGRectMakeZero();
var knobRect = CGRectMake(0.0, 0.0, knobSize.width, knobSize.height), var knobRect = _CGRectMake(0.0, 0.0, knobSize.width, knobSize.height),
trackRect = [self trackRectForBounds:bounds]; trackRect = [self trackRectForBounds:bounds];
// No track, do our best to approximate a place for this thing. // No track, do our best to approximate a place for this thing.
if (!trackRect || CGRectIsEmpty(trackRect)) if (!trackRect || _CGRectIsEmpty(trackRect))
trackRect = bounds; trackRect = bounds;
if ([self hasThemeState:CPThemeStateCircular]) if ([self hasThemeState:CPThemeStateCircular])
@@ -203,13 +196,13 @@ CPCircularSlider = 1;
} }
else if ([self isVertical]) else if ([self isVertical])
{ {
knobRect.origin.x = CGRectGetMidX(trackRect) - knobSize.width / 2.0; knobRect.origin.x = _CGRectGetMidX(trackRect) - knobSize.width / 2.0;
knobRect.origin.y = ((_maxValue - [self doubleValue]) / (_maxValue - _minValue)) * (CGRectGetHeight(trackRect) - knobSize.height); knobRect.origin.y = ((_maxValue - [self doubleValue]) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height);
} }
else else
{ {
knobRect.origin.x = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (CGRectGetWidth(trackRect) - knobSize.width); knobRect.origin.x = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetWidth(trackRect) - knobSize.width);
knobRect.origin.y = CGRectGetMidY(trackRect) - knobSize.height / 2.0; knobRect.origin.y = _CGRectGetMidY(trackRect) - knobSize.height / 2.0;
} }
return knobRect; return knobRect;
@@ -260,8 +253,8 @@ CPCircularSlider = 1;
{ {
// Recalculate isVertical. // Recalculate isVertical.
var bounds = [self bounds], var bounds = [self bounds],
width = CGRectGetWidth(bounds), width = _CGRectGetWidth(bounds),
height = CGRectGetHeight(bounds); height = _CGRectGetHeight(bounds);
_isVertical = width < height ? 1 : (width > height ? 0 : -1); _isVertical = width < height ? 1 : (width > height ? 0 : -1);
@@ -306,55 +299,55 @@ CPCircularSlider = 1;
if ([self hasThemeState:CPThemeStateCircular]) if ([self hasThemeState:CPThemeStateCircular])
{ {
var knobWidth = CGRectGetWidth(knobRect); var knobWidth = _CGRectGetWidth(knobRect);
trackRect.origin.x += knobWidth / 2; trackRect.origin.x += knobWidth / 2;
trackRect.size.width -= knobWidth; trackRect.size.width -= knobWidth;
var minValue = [self minValue], var minValue = [self minValue],
dx = aPoint.x - CGRectGetMidX(trackRect), dx = aPoint.x - _CGRectGetMidX(trackRect),
dy = aPoint.y - CGRectGetMidY(trackRect); dy = aPoint.y - _CGRectGetMidY(trackRect);
return MAX(0.0, MIN(1.0, 1.0 - (3 * PI_2 - ATAN2(dy, dx)) % PI2 / PI2)) * ([self maxValue] - minValue) + minValue; return MAX(0.0, MIN(1.0, 1.0 - (3 * PI_2 - ATAN2(dy, dx)) % PI2 / PI2)) * ([self maxValue] - minValue) + minValue;
} }
else if ([self isVertical]) else if ([self isVertical])
{ {
var knobHeight = CGRectGetHeight(knobRect); var knobHeight = _CGRectGetHeight(knobRect);
trackRect.origin.y += knobHeight / 2; trackRect.origin.y += knobHeight / 2;
trackRect.size.height -= knobHeight; trackRect.size.height -= knobHeight;
var minValue = [self minValue]; var minValue = [self minValue];
return MAX(0.0, MIN(1.0, (CGRectGetMaxY(trackRect) - aPoint.y) / CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue; return MAX(0.0, MIN(1.0, (_CGRectGetMaxY(trackRect) - aPoint.y) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue;
} }
else else
{ {
var knobWidth = CGRectGetWidth(knobRect); var knobWidth = _CGRectGetWidth(knobRect);
trackRect.origin.x += knobWidth / 2; trackRect.origin.x += knobWidth / 2;
trackRect.size.width -= knobWidth; trackRect.size.width -= knobWidth;
var minValue = [self minValue]; var minValue = [self minValue];
return MAX(0.0, MIN(1.0, (aPoint.x - CGRectGetMinX(trackRect)) / CGRectGetWidth(trackRect))) * ([self maxValue] - minValue) + minValue; return MAX(0.0, MIN(1.0, (aPoint.x - _CGRectGetMinX(trackRect)) / _CGRectGetWidth(trackRect))) * ([self maxValue] - minValue) + minValue;
} }
} }
- (BOOL)startTrackingAt:(CGPoint)aPoint - (BOOL)startTrackingAt:(CGPoint)aPoint
{ {
var bounds = [self bounds], var bounds = [self bounds],
knobRect = [self knobRectForBounds:CGRectMakeCopy(bounds)]; knobRect = [self knobRectForBounds:_CGRectMakeCopy(bounds)];
if (CGRectContainsPoint(knobRect, aPoint)) if (_CGRectContainsPoint(knobRect, aPoint))
_dragOffset = CGSizeMake(CGRectGetMidX(knobRect) - aPoint.x, CGRectGetMidY(knobRect) - aPoint.y); _dragOffset = _CGSizeMake(_CGRectGetMidX(knobRect) - aPoint.x, _CGRectGetMidY(knobRect) - aPoint.y);
else else
{ {
var trackRect = [self trackRectForBounds:bounds]; var trackRect = [self trackRectForBounds:bounds];
if (trackRect && CGRectContainsPoint(trackRect, aPoint)) if (trackRect && _CGRectContainsPoint(trackRect, aPoint))
{ {
_dragOffset = CGSizeMakeZero(); _dragOffset = _CGSizeMakeZero();
[self setObjectValue:[self _valueAtPoint:aPoint]]; [self setObjectValue:[self _valueAtPoint:aPoint]];
} }
@@ -373,7 +366,7 @@ CPCircularSlider = 1;
- (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint - (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
{ {
[self setObjectValue:[self _valueAtPoint:CGPointMake(aPoint.x + _dragOffset.width, aPoint.y + _dragOffset.height)]]; [self setObjectValue:[self _valueAtPoint:_CGPointMake(aPoint.x + _dragOffset.width, aPoint.y + _dragOffset.height)]];
return YES; return YES;
} }
+147 -155
View File
@@ -20,14 +20,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
@import "CPColorPicker.j"
@import "CPSlider.j"
@import "CPTextField.j"
@import "CPView.j" @import "CPView.j"
@import "CPColorPicker.j"
@global CPColorPickerViewWidth
@global CPColorPickerViewHeight
@global CPSliderColorPickerMode
/* /*
@@ -55,6 +49,7 @@
CPTextField _hexLabel; CPTextField _hexLabel;
CPTextField _hexValue; CPTextField _hexValue;
CPTextField _hexValue;
CPTextField _redValue; CPTextField _redValue;
CPTextField _greenValue; CPTextField _greenValue;
CPTextField _blueValue; CPTextField _blueValue;
@@ -65,166 +60,166 @@
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel - (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
{ {
return [super initWithPickerMask:mask colorPanel:owningColorPanel]; return [super initWithPickerMask:mask colorPanel: owningColorPanel];
} }
- (id)initView - (id)initView
{ {
var aFrame = CGRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight); aFrame = CGRectMake(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: CGRectMake(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: CGRectMake(0, 37, 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: CGRectMake(15, 35, aFrame.size.width - 70, 20)];
[_redSlider setMaxValue:1.0]; [_redSlider setMaxValue: 1.0];
[_redSlider setMinValue:0.0]; [_redSlider setMinValue: 0.0];
[_redSlider setTarget:self]; [_redSlider setTarget: self];
[_redSlider setAction:@selector(sliderChanged:)]; [_redSlider setAction: @selector(sliderChanged:)];
[_redSlider setAutoresizingMask:CPViewWidthSizable]; [_redSlider setAutoresizingMask: CPViewWidthSizable];
// red value input box // red value input box
_redValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 30, 45, 28)]; _redValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 30, 45, 28)];
[_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: CGRectMake(0, 63, 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: CGRectMake(15, 61, aFrame.size.width - 70, 20)];
[_greenSlider setMaxValue:1.0]; [_greenSlider setMaxValue: 1.0];
[_greenSlider setMinValue:0.0]; [_greenSlider setMinValue: 0.0];
[_greenSlider setTarget:self]; [_greenSlider setTarget: self];
[_greenSlider setAction:@selector(sliderChanged:)]; [_greenSlider setAction: @selector(sliderChanged:)];
[_greenSlider setAutoresizingMask:CPViewWidthSizable]; [_greenSlider setAutoresizingMask: CPViewWidthSizable];
// green value input box // green value input box
_greenValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 56, 45, 28)]; _greenValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 56, 45, 28)];
[_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: CGRectMake(0, 90, 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: CGRectMake(15, 87, aFrame.size.width - 70, 20)];
[_blueSlider setMaxValue:1.0]; [_blueSlider setMaxValue: 1.0];
[_blueSlider setMinValue:0.0]; [_blueSlider setMinValue: 0.0];
[_blueSlider setTarget:self]; [_blueSlider setTarget: self];
[_blueSlider setAction:@selector(sliderChanged:)]; [_blueSlider setAction: @selector(sliderChanged:)];
[_blueSlider setAutoresizingMask:CPViewWidthSizable]; [_blueSlider setAutoresizingMask: CPViewWidthSizable];
// blue value input box // blue value input box
_blueValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 82, 45, 28)]; _blueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 82, 45, 28)];
[_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: CGRectMake(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: CGRectMake(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: CGRectMake(15, 143, aFrame.size.width - 70, 20)];
[_hueSlider setMaxValue:0.999]; [_hueSlider setMaxValue: 359.0];
[_hueSlider setMinValue:0.0]; [_hueSlider setMinValue: 0.0];
[_hueSlider setTarget:self]; [_hueSlider setTarget: self];
[_hueSlider setAction:@selector(sliderChanged:)]; [_hueSlider setAction: @selector(sliderChanged:)];
[_hueSlider setAutoresizingMask:CPViewWidthSizable]; [_hueSlider setAutoresizingMask: CPViewWidthSizable];
// hue value input box // hue value input box
_hueValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 138, 45, 28)]; _hueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 138, 45, 28)];
[_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: CGRectMake(0, 170, 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: CGRectMake(15, 168, aFrame.size.width - 70, 20)];
[_saturationSlider setMaxValue:1.0]; [_saturationSlider setMaxValue: 100.0];
[_saturationSlider setMinValue:0.0]; [_saturationSlider setMinValue: 0.0];
[_saturationSlider setTarget:self]; [_saturationSlider setTarget: self];
[_saturationSlider setAction:@selector(sliderChanged:)]; [_saturationSlider setAction: @selector(sliderChanged:)];
[_saturationSlider setAutoresizingMask:CPViewWidthSizable]; [_saturationSlider setAutoresizingMask: CPViewWidthSizable];
// saturation value input box // saturation value input box
_saturationValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 164, 45, 28)]; _saturationValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 164, 45, 28)];
[_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: CGRectMake(0, 196, 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: CGRectMake(15, 194, aFrame.size.width - 70, 20)];
[_brightnessSlider setMaxValue:1.0]; [_brightnessSlider setMaxValue: 100.0];
[_brightnessSlider setMinValue:0.0]; [_brightnessSlider setMinValue: 0.0];
[_brightnessSlider setTarget:self]; [_brightnessSlider setTarget: self];
[_brightnessSlider setAction:@selector(sliderChanged:)]; [_brightnessSlider setAction: @selector(sliderChanged:)];
[_brightnessSlider setAutoresizingMask:CPViewWidthSizable]; [_brightnessSlider setAutoresizingMask: CPViewWidthSizable];
// brightness value input box // brightness value input box
_brightnessValue = [[CPTextField alloc] initWithFrame:CGRectMake(aFrame.size.width - 45, 190, 45, 28)]; _brightnessValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 190, 45, 28)];
[_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: CGRectMake(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, 28)];
[_hexValue setEditable:YES]; [_hexValue setEditable: YES];
[_hexValue setBezeled:YES]; [_hexValue setBezeled: YES];
[_hexValue setDelegate:self]; [_hexValue setDelegate: self];
[_contentView addSubview:_hexValue]; [_contentView addSubview: _hexValue];
[_contentView addSubview:_rgbLabel]; [_contentView addSubview: _rgbLabel];
[_contentView addSubview:_redLabel]; [_contentView addSubview: _redLabel];
[_contentView addSubview:_greenLabel]; [_contentView addSubview: _greenLabel];
[_contentView addSubview:_blueLabel]; [_contentView addSubview: _blueLabel];
[_contentView addSubview:_redSlider]; [_contentView addSubview: _redSlider];
[_contentView addSubview:_greenSlider]; [_contentView addSubview: _greenSlider];
[_contentView addSubview:_blueSlider]; [_contentView addSubview: _blueSlider];
[_contentView addSubview:_hsbLabel]; [_contentView addSubview: _hsbLabel];
[_contentView addSubview:_hueLabel]; [_contentView addSubview: _hueLabel];
[_contentView addSubview:_saturationLabel]; [_contentView addSubview: _saturationLabel];
[_contentView addSubview:_brightnessLabel]; [_contentView addSubview: _brightnessLabel];
[_contentView addSubview:_hueSlider]; [_contentView addSubview: _hueSlider];
[_contentView addSubview:_saturationSlider]; [_contentView addSubview: _saturationSlider];
[_contentView addSubview:_brightnessSlider]; [_contentView addSubview: _brightnessSlider];
[_contentView addSubview:_hexLabel]; [_contentView addSubview: _hexLabel];
} }
- (CPView)provideNewView:(BOOL)initialRequest - (CPView)provideNewView:(BOOL)initialRequest
@@ -255,38 +250,35 @@
{ {
case _hueSlider: case _hueSlider:
case _saturationSlider: case _saturationSlider:
case _brightnessSlider: newColor = [CPColor colorWithHue:[_hueSlider floatValue] case _brightnessSlider: newColor = [CPColor colorWithHue: [_hueSlider floatValue]
saturation:[_saturationSlider floatValue] saturation: [_saturationSlider floatValue]
brightness:[_brightnessSlider floatValue] brightness: [_brightnessSlider floatValue]
alpha:alpha]; alpha: alpha];
[self updateRGBSliders:newColor]; [self updateRGBSliders: newColor];
break; break;
case _redSlider: case _redSlider:
case _greenSlider: case _greenSlider:
case _blueSlider: newColor = [CPColor colorWithCalibratedRed:[_redSlider floatValue] case _blueSlider: newColor = [CPColor colorWithCalibratedRed: [_redSlider floatValue]
green:[_greenSlider floatValue] green: [_greenSlider floatValue]
blue:[_blueSlider floatValue] blue: [_blueSlider floatValue]
alpha:alpha]; alpha: alpha];
[self updateHSBSliders:newColor]; [self updateHSBSliders: newColor];
break; break;
} }
[self updateLabels]; [self updateLabels];
[self updateHex:newColor]; [self updateHex: newColor];
[colorPanel setColor:newColor]; [colorPanel setColor: newColor];
} }
- (void)setColor:(CPColor)aColor - (void)setColor:(CPColor)aColor
{ {
if (!aColor) [self updateRGBSliders: aColor];
[CPException raise:CPInvalidArgumentException reason:"aColor can't be nil"]; [self updateHSBSliders: aColor];
[self updateHex: aColor];
[self updateRGBSliders:aColor];
[self updateHSBSliders:aColor];
[self updateHex:aColor];
[self updateLabels]; [self updateLabels];
} }
@@ -315,13 +307,13 @@
- (void)updateLabels - (void)updateLabels
{ {
[_hueValue setStringValue:ROUND([_hueSlider floatValue] * 360.0)]; [_hueValue setStringValue: ROUND([_hueSlider floatValue])];
[_saturationValue setStringValue:ROUND([_saturationSlider floatValue] * 100.0)]; [_saturationValue setStringValue: ROUND([_saturationSlider floatValue])];
[_brightnessValue setStringValue:ROUND([_brightnessSlider floatValue] * 100.0)]; [_brightnessValue setStringValue: ROUND([_brightnessSlider floatValue])];
[_redValue setStringValue:ROUND([_redSlider floatValue] * 255)]; [_redValue setStringValue: ROUND([_redSlider floatValue] * 255)];
[_greenValue setStringValue:ROUND([_greenSlider floatValue] * 255)]; [_greenValue setStringValue: ROUND([_greenSlider floatValue] * 255)];
[_blueValue setStringValue:ROUND([_blueSlider floatValue] * 255)]; [_blueValue setStringValue: ROUND([_blueSlider floatValue] * 255)];
} }
- (CPImage)provideNewButtonImage - (CPImage)provideNewButtonImage
@@ -341,12 +333,12 @@
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
@@ -354,27 +346,27 @@
switch (field) switch (field)
{ {
case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)]; case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
[self sliderChanged:_redSlider]; [self sliderChanged: _redSlider];
break; break;
case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)]; case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
[self sliderChanged:_greenSlider]; [self sliderChanged: _greenSlider];
break; break;
case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)]; case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
[self sliderChanged:_blueSlider]; [self sliderChanged: _blueSlider];
break; break;
case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(value), 360) / 360.0, 0)]; case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(value), 360), 0)];
[self sliderChanged:_hueSlider]; [self sliderChanged: _hueSlider];
break; break;
case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(value), 100) / 100.0, 0)]; case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
[self sliderChanged:_saturationSlider]; [self sliderChanged: _saturationSlider];
break; break;
case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(value), 100) / 100.0, 0)]; case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
[self sliderChanged:_brightnessSlider]; [self sliderChanged: _brightnessSlider];
break; break;
} }
} }
+3 -66
View File
@@ -21,19 +21,6 @@
*/ */
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import <Foundation/CPRunLoop.j>
@typedef HTMLAudioElement
@protocol CPSoundDelegate <CPObject>
@optional
- (void)sound:(CPSound)aSound didFinishPlaying:(BOOL)finishedLoading;
@end
var CPSoundDelegate_sound_didFinishPlaying_ = 1 << 1;
CPSoundLoadStateEmpty = 0; CPSoundLoadStateEmpty = 0;
CPSoundLoadStateLoading = 1; CPSoundLoadStateLoading = 1;
@@ -58,11 +45,9 @@ CPSoundPlayBackStatePause = 2;
CPString _name @accessors(property=name); CPString _name @accessors(property=name);
id _delegate @accessors(property=delegate); id _delegate @accessors(property=delegate);
BOOL _playRequestBeforeLoad;
HTMLAudioElement _audioTag; HTMLAudioElement _audioTag;
int _loadStatus; int _loadStatus;
int _playBackStatus; int _playBackStatus;
unsigned _implementedDelegateMethods;
} }
#pragma mark - #pragma mark -
@@ -73,10 +58,9 @@ CPSoundPlayBackStatePause = 2;
if (self = [super init]) if (self = [super init])
{ {
_loadStatus = CPSoundLoadStateEmpty; _loadStatus = CPSoundLoadStateEmpty;
// _loops = NO; _loops = NO;
_audioTag = document.createElement("audio"); _audioTag = document.createElement("audio");
_audioTag.preload = YES; _audioTag.preload = YES;
_playRequestBeforeLoad = NO;
_audioTag.addEventListener("canplay", function() _audioTag.addEventListener("canplay", function()
{ {
@@ -146,25 +130,6 @@ CPSoundPlayBackStatePause = 2;
} }
#pragma mark -
#pragma mark Delegate methods
/*!
Sets the sound's delegate.
@param aDelegate the new delegate
*/
- (void)setDelegate:(id)aDelegate
{
if (_delegate === aDelegate)
return;
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(sound:didFinishPlaying:)])
_implementedDelegateMethods |= CPSoundDelegate_sound_didFinishPlaying_;
}
#pragma mark - #pragma mark -
#pragma mark Events listener #pragma mark Events listener
@@ -173,12 +138,6 @@ CPSoundPlayBackStatePause = 2;
- (void)_soundDidload - (void)_soundDidload
{ {
_loadStatus = CPSoundLoadStateCanBePlayed; _loadStatus = CPSoundLoadStateCanBePlayed;
if (_playRequestBeforeLoad)
{
_playRequestBeforeLoad = NO;
[self play];
}
} }
/*! @ignore /*! @ignore
@@ -208,12 +167,6 @@ CPSoundPlayBackStatePause = 2;
*/ */
- (BOOL)play - (BOOL)play
{ {
if (_loadStatus === CPSoundLoadStateLoading)
{
_playRequestBeforeLoad = YES;
return YES;
}
if ((_loadStatus !== CPSoundLoadStateCanBePlayed) if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|| (_playBackStatus === CPSoundPlayBackStatePlay)) || (_playBackStatus === CPSoundPlayBackStatePlay))
return NO; return NO;
@@ -239,7 +192,8 @@ CPSoundPlayBackStatePause = 2;
_audioTag.currentTime = 0.0; _audioTag.currentTime = 0.0;
_playBackStatus = CPSoundPlayBackStateStop; _playBackStatus = CPSoundPlayBackStateStop;
[self _sendDelegateSoundDidFinishPlaying:YES]; if (_delegate && [_delegate respondsToSelector:@selector(sound:didFinishPlaying:)])
[_delegate sound:self didFinishPlaying:YES];
return YES; return YES;
} }
@@ -347,20 +301,3 @@ CPSoundPlayBackStatePause = 2;
} }
@end @end
@implementation CPSound (CPSoundDelegate)
/*!
@ignore
Call delegate sound:didFinishPlaying:
*/
- (void)_sendDelegateSoundDidFinishPlaying:(BOOL)finishedPlaying
{
if (!(_implementedDelegateMethods & CPSoundDelegate_sound_didFinishPlaying_))
return;
[_delegate sound:self didFinishPlaying:finishedPlaying];
}
@end
+200 -790
View File
File diff suppressed because it is too large Load Diff
+69 -170
View File
@@ -20,20 +20,20 @@
* 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 "CPControl.j" @import <AppKit/CPControl.j>
@import "CPButton.j" @import <AppKit/CPButton.j>
@import "CPTextField.j" @import <AppKit/CPTextField.j>
/*! var CPStepperButtonsSize = CPSizeMake(19, 13);
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. /*! CPStepper is an implementation of Cocoa NSStepper.
This control display a two part button that can be used to increase or decrease a value with a given interval.
*/ */
@implementation CPStepper: CPControl @implementation CPStepper: CPControl
{ {
BOOL _valueWraps @accessors(property=valueWraps); BOOL _valueWraps @accessors(property=valueWraps);
BOOL _autorepeat @accessors(getter=autorepeat);
int _increment @accessors(property=increment); int _increment @accessors(property=increment);
int _maxValue @accessors(property=maxValue); int _maxValue @accessors(property=maxValue);
int _minValue @accessors(property=minValue); int _minValue @accessors(property=minValue);
@@ -45,58 +45,34 @@
#pragma mark - #pragma mark -
#pragma mark Initialization #pragma mark Initialization
/*! /*! Initializes a CPStepper with given values
Initializes a CPStepper with given values.
@param aValue the initial value of the CPStepper @param aValue the initial value of the CPStepper
@param minValue the minimal acceptable value of the stepper @param minValue the minimal acceptable value of the stepper
@param maxValue the maximal acceptable value of the stepper @param maxValue the maximal acceptable value of the stepper
@return Initialized CPStepper @return Initialized CPStepper
*/ */
+ (id)stepperWithInitialValue:(float)aValue minValue:(float)aMinValue maxValue:(float)aMaxValue + (CPStepper)stepperWithInitialValue:(float)aValue minValue:(float)aMinValue maxValue:(float)aMaxValue
{ {
var stepper = [[self alloc] initWithFrame:CGRectMakeZero()]; var stepper = [[CPStepper alloc] initWithFrame:CPRectMake(0, 0, 19, 25)];
[stepper setDoubleValue:aValue]; [stepper setDoubleValue:aValue];
[stepper setMinValue:aMinValue]; [stepper setMinValue:aMinValue];
[stepper setMaxValue:aMaxValue]; [stepper setMaxValue:aMaxValue];
// _sizeToFit will put the good size for the stepper depending of the current theme
[stepper _sizeToFit];
return stepper; return stepper;
} }
/*! /*! Initializes a CPStepper with default values:
Initializes a CPStepper with default values:
- minValue = 0.0 - minValue = 0.0
- maxValue = 59.0 - maxValue = 59.0
- value = 0.0 - value = 0.0
@return Initialized CPStepper @return Initialized CPStepper
*/ */
+ (id)stepper + (CPStepper)stepper
{ {
return [CPStepper stepperWithInitialValue:0.0 minValue:0.0 maxValue:59.0]; return [CPStepper stepperWithInitialValue:0.0 minValue:0.0 maxValue:59.0];
} }
+ (Class)_binderClassForBinding:(CPString)aBinding /*! Initializes the CPStepper
{
if (aBinding === CPValueBinding || aBinding === CPMinValueBinding || aBinding === CPMaxValueBinding)
return [_CPStepperValueBinder class];
return [super _binderClassForBinding:aBinding];
}
- (CPString)_replacementKeyPathForBinding:(CPString)aBinding
{
if (aBinding === CPValueBinding)
return @"doubleValue";
return [super _replacementKeyPathForBinding:aBinding];
}
/*!
Initializes a CPStepper.
@param aFrame the frame of the control @param aFrame the frame of the control
@return initialized CPStepper @return initialized CPStepper
*/ */
@@ -108,50 +84,33 @@
_minValue = 0.0; _minValue = 0.0;
_increment = 1.0; _increment = 1.0;
_valueWraps = YES; _valueWraps = YES;
_autorepeat = YES;
[self setDoubleValue:0.0]; [self setDoubleValue:0.0];
[self _init];
_buttonUp = [[CPButton alloc] initWithFrame:CPRectMake(aFrame.size.width - CPStepperButtonsSize.width, 0, CPStepperButtonsSize.width, CPStepperButtonsSize.height)];
[_buttonUp setContinuous:YES];
[_buttonUp setTarget:self];
[_buttonUp setAction:@selector(_buttonDidClick:)];
[_buttonUp setAutoresizingMask:CPViewNotSizable];
[self addSubview:_buttonUp];
_buttonDown = [[CPButton alloc] initWithFrame:CPRectMake(aFrame.size.width - CPStepperButtonsSize.width, CPStepperButtonsSize.height, CPStepperButtonsSize.width, CPStepperButtonsSize.height - 1)];
[_buttonDown setContinuous:YES];
[_buttonDown setTarget:self];
[_buttonDown setAction:@selector(_buttonDidClick:)];
[_buttonDown setAutoresizingMask:CPViewNotSizable];
[self addSubview:_buttonDown];
[self setNeedsLayout];
} }
return self; return self;
} }
/*! @ignore */
- (void)_init
{
_buttonUp = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
[_buttonUp setContinuous:_autorepeat];
[_buttonUp setTarget:self];
[_buttonUp setAction:@selector(_buttonDidClick:)];
[_buttonUp setAutoresizingMask:CPViewNotSizable];
[self addSubview:_buttonUp];
_buttonDown = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
[_buttonDown setContinuous:_autorepeat];
[_buttonDown setTarget:self];
[_buttonDown setAction:@selector(_buttonDidClick:)];
[_buttonDown setAutoresizingMask:CPViewNotSizable];
[self setContinuous:_autorepeat];
[self addSubview:_buttonDown];
[self _sizeToFit];
[self setNeedsLayout];
}
- (void)setControlSize:(CPControlSize)aControlSize
{
[super setControlSize:aControlSize];
[self _sizeToFit];
}
#pragma mark - #pragma mark -
#pragma mark Superclass overrides #pragma mark Superclass overrides
/*! /*! set if the CPStepper is enabled or not
Set if the CPStepper is enabled or not.
@param shouldEnabled BOOL that define if stepper is enabled or not. @param shouldEnabled BOOL that define if stepper is enabled or not.
*/ */
- (void)setEnabled:(BOOL)shouldEnabled - (void)setEnabled:(BOOL)shouldEnabled
@@ -162,65 +121,40 @@
[_buttonDown setEnabled:shouldEnabled]; [_buttonDown setEnabled:shouldEnabled];
} }
/*! set the frame of the CPStepper and check if width is not smaller than theme min-size
@param aFrame the frame
*/
- (void)setFrame:(CGRect)aFrame - (void)setFrame:(CGRect)aFrame
{ {
var upSize = [self currentValueForThemeAttribute:@"up-button-size"], if (aFrame.size.width >= CGRectGetWidth(aFrame))
downSize = [self currentValueForThemeAttribute:@"down-button-size"], [super setFrame:aFrame];
minSize = CGSizeMake(upSize.width, upSize.height + downSize.height),
frame = CGRectMakeCopy(aFrame);
frame.size.width = MAX(minSize.width, frame.size.width);
frame.size.height = MAX(minSize.height, frame.size.height);
[super setFrame:frame];
} }
/*! @ignore */ /*! @ignore
*/
- (void)layoutSubviews - (void)layoutSubviews
{ {
var controlSizeThemeState = [self _controlSizeThemeState], [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
aFrame = [self frame], [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
upSize = [self valueForThemeAttribute:@"up-button-size" inState:controlSizeThemeState], [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
downSize = [self valueForThemeAttribute:@"down-button-size" inState:controlSizeThemeState], [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
upFrame = CGRectMake(0, 0, upSize.width, upSize.height), [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
downFrame = CGRectMake(0, upSize.height, downSize.width, downSize.height); [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
[_buttonUp setFrame:upFrame];
[_buttonDown setFrame:downFrame];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
} }
- (void)_sizeToFit /*! set if CPStepper should autorepeat
{
[self setFrame:CGRectMake([self frameOrigin].x, [self frameOrigin].y, 0, 0)];
}
/*!
Set if CPStepper should autorepeat.
@param shouldAutoRepeat if YES, the first mouse down does one increment (decrement) and, after each delay of 0.5 seconds @param shouldAutoRepeat if YES, the first mouse down does one increment (decrement) and, after each delay of 0.5 seconds
*/ */
- (void)setAutorepeat:(BOOL)shouldAutoRepeat - (void)setAutorepeat:(BOOL)shouldAutoRepeat
{ {
if (shouldAutoRepeat !== _autorepeat) [_buttonUp setContinuous:shouldAutoRepeat];
{ [_buttonDown setContinuous:shouldAutoRepeat];
[_buttonUp setContinuous:shouldAutoRepeat];
[_buttonDown setContinuous:shouldAutoRepeat];
}
[self setContinuous:shouldAutoRepeat];
} }
/*! /*! set the current value of the stepper
Set the current value of the stepper.
@param aValue a float containing the value @param aValue a float containing the value
*/ */
- (void)setDoubleValue:(double)aValue - (void)setDoubleValue:(float)aValue
{ {
if (aValue > _maxValue) if (aValue > _maxValue)
[super setDoubleValue:_valueWraps ? _minValue : _maxValue]; [super setDoubleValue:_valueWraps ? _minValue : _maxValue];
@@ -233,22 +167,23 @@
#pragma mark - #pragma mark -
#pragma mark Actions #pragma mark Actions
/*! @ignore */ /*! @ignore
*/
- (IBAction)_buttonDidClick:(id)aSender - (IBAction)_buttonDidClick:(id)aSender
{ {
if (![self isEnabled]) if (![self isEnabled])
return; return;
if (aSender === _buttonUp) if (aSender == _buttonUp)
[self setDoubleValue:([self doubleValue] + _increment)]; [self setDoubleValue:([self doubleValue] + _increment)];
else else
[self setDoubleValue:([self doubleValue] - _increment)]; [self setDoubleValue:([self doubleValue] - _increment)];
[self sendAction:[self action] to:[self target]]; if (_target && _action && [_target respondsToSelector:_action])
[self sendAction:_action to:_target];
} }
/*! /*! @perform a programatic click on up button
Perform a programatic click on up button.
@param aSender sender of the action @param aSender sender of the action
*/ */
- (IBAction)performClickUp:(id)aSender - (IBAction)performClickUp:(id)aSender
@@ -256,8 +191,7 @@
[_buttonUp performClick:aSender]; [_buttonUp performClick:aSender];
} }
/*! /*! @perform a programatic click on down button
Perform a programatic click on down button.
@param aSender sender of the action @param aSender sender of the action
*/ */
- (IBAction)performClickDown:(id)aSender - (IBAction)performClickDown:(id)aSender
@@ -274,57 +208,27 @@
return @"stepper"; return @"stepper";
} }
+ (CPDictionary)themeAttributes + (id)themeAttributes
{ {
return @{ return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null]]
@"bezel-color-up-button": [CPNull null], forKeys:[@"bezel-color-up-button", @"bezel-color-down-button"]];
@"bezel-color-down-button": [CPNull null],
@"up-button-size": CGSizeMakeZero(),
@"down-button-size": CGSizeMakeZero(),
};
} }
@end @end
@implementation _CPStepperValueBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding @implementation CPStepper (CPCodingCompliance)
{
var placeholder = (aBinding === CPMaxValueBinding) ? [_source maxValue] : [_source minValue];
[super _updatePlaceholdersWithOptions:options];
[self _setPlaceholder:placeholder forMarker:CPMultipleValuesMarker isDefault:YES];
[self _setPlaceholder:placeholder forMarker:CPNoSelectionMarker isDefault:YES];
[self _setPlaceholder:placeholder forMarker:CPNotApplicableMarker isDefault:YES];
[self _setPlaceholder:placeholder forMarker:CPNullMarker isDefault:YES];
}
@end
var CPStepperMinValue = @"CPStepperMinValue",
CPStepperMaxValue = @"CPStepperMaxValue",
CPStepperValueWraps = @"CPStepperValueWraps",
CPStepperAutorepeat = @"CPStepperAutorepeat",
CPStepperIncrement = @"CPStepperIncrement";
@implementation CPStepper (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder - (id)initWithCoder:(CPCoder)aCoder
{ {
if (self = [super initWithCoder:aCoder]) if (self = [super initWithCoder:aCoder])
{ {
_increment = [aCoder decodeIntForKey:CPStepperIncrement]; _maxValue = [aCoder decodeObjectForKey:@"_maxValue"];
_minValue = [aCoder decodeIntForKey:CPStepperMinValue]; _minValue = [aCoder decodeObjectForKey:@"_minValue"];
_maxValue = [aCoder decodeIntForKey:CPStepperMaxValue]; _increment = [aCoder decodeObjectForKey:@"_increment"];
_valueWraps = [aCoder decodeBoolForKey:CPStepperValueWraps]; _buttonUp = [aCoder decodeObjectForKey:@"_buttonUp"];
_autorepeat = [aCoder decodeBoolForKey:CPStepperAutorepeat]; _buttonDown = [aCoder decodeObjectForKey:@"_buttonDown"];
[self _init];
} }
return self; return self;
} }
@@ -332,16 +236,11 @@ var CPStepperMinValue = @"CPStepperMinValue",
{ {
[super encodeWithCoder:aCoder]; [super encodeWithCoder:aCoder];
[aCoder encodeInt:_increment forKey:CPStepperIncrement]; [aCoder encodeObject:_maxValue forKey:@"_maxValue"];
[aCoder encodeObject:_minValue forKey:@"_minValue"];
if (_minValue) [aCoder encodeObject:_increment forKey:@"_increment"];
[aCoder encodeInt:_minValue forKey:CPStepperMinValue]; [aCoder encodeObject:_buttonUp forKey:@"_buttonUp"];
if (_maxValue) [aCoder encodeObject:_buttonDown forKey:@"_buttonDown"];
[aCoder encodeInt:_maxValue forKey:CPStepperMaxValue];
if (_valueWraps)
[aCoder encodeBool:_valueWraps forKey:CPStepperValueWraps];
if (_autorepeat)
[aCoder encodeBool:_autorepeat forKey:CPStepperAutorepeat];
} }
@end @end
+72
View File
@@ -0,0 +1,72 @@
/*
* CPString+Additions.j
* AppKit
*
* Created by Randy Luecke
* Copyright 2011, RCLConcepts, LLC.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
AppKit adds two methods to the CPString class to support drawing string directly in an CPView.
AppKit also adds similar methods to CPAttributedString.
The two drawing methods draw a string object with a single set of attributes that apply to the entire string.
To draw a string with multiple attributes, such as multiple text fonts, you must use an attributed string.
*/
@implementation CPString (AppKitAdditions)
/*!
Draws a string in the current graphics context.
This method applies the attributes to the entier string
and displays it on a single "infinately long" line.
@param aPoint - The starting point to draw the string
@param attributes - the dictionary of attributes to apply to the string
*/
- (void)drawAtPoint:(CGPoint)aPoint withAttributes:(CPDictionary)attributes
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
run = _CTRunCreate([self copy], attributes);
CGContextSetTextPosition(context, aPoint.x, aPoint.y);
CTRunDraw(run, context, nil);
}
/*!
Draws a string in the current graphics context.
This method applies the attributes to the entier string
and displays it within the given rect.
@param aRect - The rect for which the string should be drawn into
@param attributes - the dictionary of attributes to apply to the string
*/
- (void)drawInRect:(CGRect)aRect withAttributes:(CPDictionary)attributes
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
string = [[CPAttributedString alloc] initWithString:self attributes:attributes];
frameSetter = CTFramesetterCreateWithAttributedString(string),
path = CGPathCreateMutable();
CGPathAddRect(path, nil, aRect);
var frame = CTFramesetterCreateFrame(frameSetter, CPMakeRange(0, [string length]), path, nil);
CTFrameDraw(frame, context);
}
@end
+7 -58
View File
@@ -22,15 +22,10 @@
@import <Foundation/CPString.j> @import <Foundation/CPString.j>
@import "CGGeometry.j"
@import "CPPlatformString.j" @import "CPPlatformString.j"
@import "CPFont.j"
@import "CPCompatibility.j"
var CPStringSizeWithFontInWidthCache = [], var CPStringSizeWithFontInWidthCache = {};
CPStringSizeWithFontHeightCache = [],
CPStringSizeMeasuringContext;
CPStringSizeCachingEnabled = YES; CPStringSizeCachingEnabled = YES;
@@ -57,67 +52,21 @@ CPStringSizeCachingEnabled = YES;
return [self sizeWithFont:aFont inWidth:NULL]; return [self sizeWithFont:aFont inWidth:NULL];
} }
+ (void) initialize - (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
{ {
if ([self class] != [CPString class])
return;
#if PLATFORM(DOM)
if (CPFeatureIsCompatible(CPHTMLCanvasFeature) && !CPStringSizeMeasuringContext)
CPStringSizeMeasuringContext = CGBitmapGraphicsContextCreate();
#endif
}
- (CGSize)_sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
{
var size;
#if PLATFORM(DOM)
if (!CPStringSizeCachingEnabled) if (!CPStringSizeCachingEnabled)
return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth]; return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
var sizeCacheForFont = CPStringSizeWithFontInWidthCache[self]; var cacheKey = self + [aFont cssString] + aWidth,
size = CPStringSizeWithFontInWidthCache[cacheKey];
if (sizeCacheForFont === undefined) if (size === undefined)
sizeCacheForFont = CPStringSizeWithFontInWidthCache[self] = [];
if (!aWidth)
aWidth = '0';
var cssString = [aFont cssString],
cacheKey = cssString + '_' + aWidth;
size = sizeCacheForFont[cacheKey];
if (size !== undefined && sizeCacheForFont.hasOwnProperty(cacheKey))
return CGSizeMakeCopy(size);
if (!CPFeatureIsCompatible(CPHTMLCanvasFeature) || aWidth > 0)
size = [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
else
{ {
if (CPPlatformHasBug(CPTextSizingAlwaysNeedsSetFontBug) || CPStringSizeMeasuringContext.font !== cssString) size = [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
CPStringSizeMeasuringContext.font = cssString; CPStringSizeWithFontInWidthCache[cacheKey] = size;
var fontHeight = CPStringSizeWithFontHeightCache[cssString];
if (fontHeight === undefined)
fontHeight = CPStringSizeWithFontHeightCache[cssString] = [aFont defaultLineHeightForFont];
size = CGSizeMake(CPStringSizeMeasuringContext.measureText(self).width, fontHeight);
} }
sizeCacheForFont[cacheKey] = size;
#else
size = CGSizeMake(0, 0);
#endif
return CGSizeMakeCopy(size); return CGSizeMakeCopy(size);
} }
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
{
var size = [self _sizeWithFont:aFont inWidth:aWidth];
return CGSizeMake(CEIL(size.width), size.height);
}
@end @end
+121 -575
View File
@@ -1,31 +1,8 @@
/*
* CPTabView.j
* AppKit
*
* Created by Derek Hammer.
* Copyright 2010, Derek Hammer.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPBox.j" @import "CPBox.j"
@import "CPSegmentedControl.j" @import "CPSegmentedControl.j"
@import "CPTabViewItem.j" @import "CPTabViewItem.j"
@import "CPView.j" @import "CPView.j"
@typedef CPTabViewType
CPTopTabsBezelBorder = 0; CPTopTabsBezelBorder = 0;
//CPLeftTabsBezelBorder = 1; //CPLeftTabsBezelBorder = 1;
CPBottomTabsBezelBorder = 2; CPBottomTabsBezelBorder = 2;
@@ -34,25 +11,10 @@ CPNoTabsBezelBorder = 4; //Displays no tabs and has a bezeled border.
CPNoTabsLineBorder = 5; //Has no tabs and displays a line border. CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
CPNoTabsNoBorder = 6; //Displays no tabs and no border. CPNoTabsNoBorder = 6; //Displays no tabs and no border.
@class _CPTabViewBox var CPTabViewDidSelectTabViewItemSelector = 1,
@class _CPSegmentedControl CPTabViewShouldSelectTabViewItemSelector = 2,
CPTabViewWillSelectTabViewItemSelector = 4,
var CPTabViewDidSelectTabViewItemSelector = 1 << 1, CPTabViewDidChangeNumberOfTabViewItemsSelector = 8;
CPTabViewShouldSelectTabViewItemSelector = 1 << 2,
CPTabViewWillSelectTabViewItemSelector = 1 << 3,
CPTabViewDidChangeNumberOfTabViewItemsSelector = 1 << 4;
@protocol CPTabViewDelegate <CPObject>
@optional
- (BOOL)tabView:(CPTabView)tabView shouldSelectTabViewItem:(CPTabViewItem)tabViewItem;
- (void)tabView:(CPTabView)tabView didSelectTabViewItem:(CPTabViewItem)tabViewItem;
- (void)tabView:(CPTabView)tabView willSelectTabViewItem:(CPTabViewItem)tabViewItem;
- (void)tabViewDidChangeNumberOfTabViewItems:(CPTabView)tabView;
@end
/*! /*!
@ingroup appkit @ingroup appkit
@@ -64,27 +26,28 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
@implementation CPTabView : CPView @implementation CPTabView : CPView
{ {
CPArray _items; CPArray _items;
_CPSegmentedControl _tabs; CPSegmentedControl _tabs;
_CPTabViewBox _box; CPBox _box;
CPView _placeholderView @accessors;
CPTabViewItem _selectedTabViewItem; CPNumber _selectedIndex;
CPTabViewType _type; CPTabViewType _type;
CPFont _font; CPFont _font;
id <CPTabViewDelegate> _delegate; id _delegate;
unsigned _delegateSelectors; unsigned _delegateSelectors;
} }
- (id)initWithFrame:(CGRect)aFrame - (id)initWithFrame:(CGRect)aFrame
{ {
if (self = [super initWithFrame:aFrame]) if (self = [super initWithFrame:aFrame])
{ {
_items = [CPArray array];
_selectedIndex = CPNotFound;
[self _init]; [self _init];
_selectedTabViewItem = nil;
[self setTabViewType:CPTopTabsBezelBorder]; [self setTabViewType:CPTopTabsBezelBorder];
} }
@@ -93,26 +56,20 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
- (void)_init - (void)_init
{ {
_tabs = [[_CPSegmentedControl alloc] initWithFrame:CGRectMakeZero()]; _tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
[_tabs setTabView:self]; [_tabs setHitTests:NO];
[_tabs setSegments:[CPArray array]];
var height = [_tabs valueForThemeAttribute:@"min-size"].height; var height = [_tabs valueForThemeAttribute:@"default-height"];
[_tabs setFrameSize:CGSizeMake(0, height)]; [_tabs setFrameSize:CGSizeMake(0, height)];
_box = [[_CPTabViewBox alloc] initWithFrame:[self bounds]]; _box = [[CPBox alloc] initWithFrame:[self bounds]];
[_box setTabView:self];
[self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]]; [self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
[_box setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
[_tabs setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
[self addSubview:_box]; [self addSubview:_box];
[self addSubview:_tabs]; [self addSubview:_tabs];
_placeholderView = nil;
}
- (CPArray)items
{
return [_tabs segments];
} }
// Adding and Removing Tabs // Adding and Removing Tabs
@@ -122,7 +79,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem - (void)addTabViewItem:(CPTabViewItem)aTabViewItem
{ {
[self insertTabViewItem:aTabViewItem atIndex:[self numberOfTabViewItems]]; [self insertTabViewItem:aTabViewItem atIndex:[_items count]];
} }
/*! /*!
@@ -130,26 +87,17 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
@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:(CPUInteger)anIndex - (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex
{ {
[self _insertTabViewItems:[aTabViewItem] atIndexes:[CPIndexSet indexSetWithIndex:anIndex]]; [_items insertObject:aTabViewItem atIndex:anIndex];
}
- (void)_insertTabViewItems:(CPArray)tabViewItems atIndexes:(CPIndexSet)indexes [self _updateItems];
{ [self _repositionTabs];
var prevItemsCount = [self numberOfTabViewItems];
[_tabs insertSegments:tabViewItems atIndexes:indexes]; [aTabViewItem _setTabView:self];
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
[self tileWithChangedItem:[tabViewItems firstObject]]; if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[self _reverseSetContent]; [_delegate tabViewDidChangeNumberOfTabViewItems:self];
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
// Do not allow empty selection if selection bindings are not enabled.
if (prevItemsCount == 0 && [self numberOfTabViewItems] > 0 && ![self _isSelectionBinded])
[self _selectTabViewItemAtIndex:0];
} }
/*! /*!
@@ -158,39 +106,23 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem - (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
{ {
var idx = [[self items] indexOfObjectIdenticalTo:aTabViewItem]; var count = [_items count];
for (var i = 0; i < count; i++)
{
if ([_items objectAtIndex:i] === aTabViewItem)
{
[_items removeObjectAtIndex:i];
break;
}
}
if (idx == CPNotFound) [self _updateItems];
return; [self _repositionTabs];
[_tabs removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndex:idx]];
[aTabViewItem _setTabView:nil]; [aTabViewItem _setTabView:nil];
[self tileWithChangedItem:nil]; if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[self _didRemoveTabViewItem:aTabViewItem atIndex:idx]; [_delegate tabViewDidChangeNumberOfTabViewItems:self];
[self _reverseSetContent];
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
}
- (void)_didRemoveTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPInteger)idx
{
// If the selection is managed by bindings, let the binder do that.
if ([self _isSelectionBinded])
return;
if (_selectedTabViewItem == aTabViewItem)
{
var didSelect = NO;
if (idx > 0)
didSelect = [self selectTabViewItemAtIndex:idx - 1];
else if ([self numberOfTabViewItems] > 0)
didSelect = [self selectTabViewItemAtIndex:0];
if (didSelect == NO)
_selectedTabViewItem == nil;
}
} }
// Accessing Tabs // Accessing Tabs
@@ -201,7 +133,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem - (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
{ {
return [[self items] indexOfObjectIdenticalTo:aTabViewItem]; return [_items indexOfObjectIdenticalTo:aTabViewItem];
} }
/*! /*!
@@ -211,10 +143,11 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier - (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
{ {
return [[self items] indexOfObjectPassingTest:function(item, idx, stop) for (var index = [_items count]; index >= 0; index--)
{ if ([[_items[index] identifier] isEqual:anIdentifier])
return [[item identifier] isEqual:anIdentifier]; return index;
}];
return CPNotFound;
} }
/*! /*!
@@ -223,16 +156,16 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (unsigned)numberOfTabViewItems - (unsigned)numberOfTabViewItems
{ {
return [[self 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 @return a tab view item, or nil
*/ */
- (CPTabViewItem)tabViewItemAtIndex:(CPUInteger)anIndex - (CPTabViewItem)tabViewItemAtIndex:(unsigned)anIndex
{ {
return [[self items] objectAtIndex:anIndex]; return [_items objectAtIndex:anIndex];
} }
/*! /*!
@@ -241,7 +174,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (CPArray)tabViewItems - (CPArray)tabViewItems
{ {
return [[self items] copy]; // Copy? return [_items copy]; // Copy?
} }
// Selecting a Tab // Selecting a Tab
@@ -251,7 +184,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (void)selectFirstTabViewItem:(id)aSender - (void)selectFirstTabViewItem:(id)aSender
{ {
if ([self numberOfTabViewItems] === 0) if ([_items count] === 0)
return; // throw? return; // throw?
[self selectTabViewItemAtIndex:0]; [self selectTabViewItemAtIndex:0];
@@ -263,10 +196,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (void)selectLastTabViewItem:(id)aSender - (void)selectLastTabViewItem:(id)aSender
{ {
if ([self numberOfTabViewItems] === 0) if ([_items count] === 0)
return; // throw? return; // throw?
[self selectTabViewItemAtIndex:[self numberOfTabViewItems] - 1]; [self selectTabViewItemAtIndex:[_items count] - 1];
} }
/*! /*!
@@ -275,12 +208,12 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (void)selectNextTabViewItem:(id)aSender - (void)selectNextTabViewItem:(id)aSender
{ {
if (_selectedTabViewItem === nil) if (_selectedIndex === CPNotFound)
return; return;
var nextIndex = [self indexOfTabViewItem:_selectedTabViewItem] + 1; var nextIndex = _selectedIndex + 1;
if (nextIndex === [self numberOfTabViewItems]) 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;
@@ -293,10 +226,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (void)selectPreviousTabViewItem:(id)aSender - (void)selectPreviousTabViewItem:(id)aSender
{ {
if (_selectedTabViewItem === nil) if (_selectedIndex === CPNotFound)
return; return;
var previousIndex = [self indexOfTabViewItem:_selectedTabViewItem] - 1; var previousIndex = _selectedIndex - 1;
if (previousIndex < 0) if (previousIndex < 0)
return; // does nothing. See above. return; // does nothing. See above.
@@ -317,63 +250,26 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
Selects the item at the specified index. Selects the item at the specified index.
@param anIndex the index of the item to display. @param anIndex the index of the item to display.
*/ */
- (BOOL)selectTabViewItemAtIndex:(CPUInteger)anIndex - (void)selectTabViewItemAtIndex:(unsigned)anIndex
{ {
if (![self _selectTabViewItemAtIndex:anIndex]) var aTabViewItem = [_items objectAtIndex:anIndex];
return NO;
[self _reverseSetSelectedIndex]; if (anIndex === _selectedIndex)
return;
return YES;
}
// Like selectTabViewItemAtIndex: but without bindings interaction
- (BOOL)_selectTabViewItemAtIndex:(CPUInteger)anIndex
{
var aTabViewItem = [self tabViewItemAtIndex:anIndex]; var aTabViewItem = [self tabViewItemAtIndex:anIndex];
if (aTabViewItem == _selectedTabViewItem) if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
return NO; return;
if (![self _sendDelegateShouldSelectTabViewItem:aTabViewItem]) if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
return NO; [_delegate tabView:self willSelectTabViewItem:aTabViewItem];
[self _sendDelegateWillSelectTabViewItem:aTabViewItem]; [_tabs selectSegmentWithTag:anIndex];
[self _setSelectedIndex:anIndex];
[_tabs setSelectedSegment:anIndex]; if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
_selectedTabViewItem = aTabViewItem; [_delegate tabView:self didSelectTabViewItem:aTabViewItem];
[self _loadTabViewItem:aTabViewItem];
[self _sendDelegateDidSelectTabViewItem:aTabViewItem];
return YES;
}
- (void)_loadTabViewItem:(CPTabViewItem)aTabViewItem
{
var controller = [aTabViewItem viewController];
if (controller !== nil && ![controller isViewLoaded])
{
[controller loadViewWithCompletionHandler:function(view, error)
{
if (error !== nil)
{
CPLog.warn("Could not load the view for item " + aTabViewItem + ". " + error);
}
else if (view !== nil)
{
[aTabViewItem setView:view];
if ([self selectedTabViewItem] == aTabViewItem)
[self _displayItemView:view];
}
}];
}
else
{
[self _displayItemView:[aTabViewItem view]];
}
} }
/*! /*!
@@ -382,7 +278,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/ */
- (CPTabViewItem)selectedTabViewItem - (CPTabViewItem)selectedTabViewItem
{ {
return _selectedTabViewItem; return [_items objectAtIndex:_selectedIndex];
} }
// Modifying the font // Modifying the font
@@ -421,9 +317,21 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
_type = aTabViewType; _type = aTabViewType;
if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder) if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder)
{
[_box setFrame:[self bounds]];
[_tabs removeFromSuperview]; [_tabs removeFromSuperview];
}
else else
{
var aFrame = [self frame],
segmentedHeight = CGRectGetHeight([_tabs frame]),
origin = _type === CPTopTabsBezelBorder ? segmentedHeight / 2 : 0;
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
CGRectGetHeight(aFrame) - segmentedHeight / 2)];
[self addSubview:_tabs]; [self addSubview:_tabs];
}
switch (_type) switch (_type)
{ {
@@ -439,37 +347,6 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
[_box setBorderType:CPNoBorder]; [_box setBorderType:CPNoBorder];
break; break;
} }
[self setNeedsLayout];
}
- (void)tileWithChangedItem:(CPTabViewItem)aTabViewItem
{
var segment = aTabViewItem ? [self indexOfTabViewItem:aTabViewItem] : 0;
[_tabs tileWithChangedSegment:segment];
[self setNeedsLayout];
}
- (void)layoutSubviews
{
// Even if CPTabView's autoresizesSubviews is NO, _tabs and _box has to be laid out.
// This means we can't rely on autoresize masks.
if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder)
{
[_box setFrame:[self bounds]];
}
else
{
var aFrame = [self frame],
segmentedHeight = CGRectGetHeight([_tabs frame]),
origin = _type === CPTopTabsBezelBorder ? segmentedHeight / 2 : 0;
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
CGRectGetHeight(aFrame) - segmentedHeight / 2)];
[self _repositionTabs];
}
} }
/*! /*!
@@ -494,7 +371,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
Sets the delegate for this tab view. Sets the delegate for this tab view.
@param aDelegate the tab view's delegate @param aDelegate the tab view's delegate
*/ */
- (void)setDelegate:(id <CPTabViewDelegate>)aDelegate - (void)setDelegate:(id)aDelegate
{ {
if (_delegate == aDelegate) if (_delegate == aDelegate)
return; return;
@@ -526,6 +403,17 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
return [_box backgroundColor]; return [_box backgroundColor];
} }
- (void)mouseDown:(CPEvent)anEvent
{
var segmentIndex = [_tabs testSegment:[_tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
if (segmentIndex != CPNotFound)
{
[self selectTabViewItemAtIndex:segmentIndex];
[_tabs trackSegment:anEvent];
}
}
- (void)_repositionTabs - (void)_repositionTabs
{ {
var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2, var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2,
@@ -537,243 +425,28 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
[_tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)]; [_tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
} }
- (void)_displayItemView:(CPView)aView - (void)_setSelectedIndex:(CPNumber)index
{ {
[_box setContentView:aView]; _selectedIndex = index;
[_box setContentView:[[_items objectAtIndex:_selectedIndex] view]];
} }
// DELEGATE METHODS - (void)_updateItems
- (BOOL)_sendDelegateShouldSelectTabViewItem:(CPTabViewItem)aTabViewItem
{ {
if (_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) var count = [_items count];
return [_delegate tabView:self shouldSelectTabViewItem:aTabViewItem]; [_tabs setSegmentCount:count];
return YES; for (var i = 0; i < count; i++)
}
- (void)_sendDelegateWillSelectTabViewItem:(CPTabViewItem)aTabViewItem
{
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
}
- (void)_sendDelegateDidSelectTabViewItem:(CPTabViewItem)aTabViewItem
{
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
}
- (void)_sendDelegateTabViewDidChangeNumberOfTabViewItems
{
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
}
@end
@implementation CPTabView (BindingSupport)
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding == CPContentBinding)
return [_CPTabViewContentBinder class];
else if (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding)
return [_CPTabViewSelectionBinder class];
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding);
}
- (void)_reverseSetContent
{
var theBinder = [self binderForBinding:CPContentBinding];
[theBinder reverseSetValueFor:@"items"];
}
- (void)_reverseSetSelectedIndex
{
var theBinder = [self binderForBinding:CPSelectionIndexesBinding];
if (theBinder !== nil)
[theBinder reverseSetValueFor:@"selectionIndexes"];
else
{ {
theBinder = [self binderForBinding:CPSelectedIndexBinding]; [_tabs setLabel:[[_items objectAtIndex:i] label] forSegment:i];
[theBinder reverseSetValueFor:@"selectedIndex"]; [_tabs setTag:i forSegment:i];
}
}
- (CPBinder)binderForBinding:(CPString)aBinding
{
var cls = [[self class] _binderClassForBinding:aBinding];
return [cls getBinding:aBinding forObject:self];
}
- (BOOL)_isSelectionBinded
{
return [self binderForBinding:CPSelectionIndexesBinding] || [self binderForBinding:CPSelectedIndexBinding];
}
- (void)setItems:(CPArray)tabViewItems
{
if ([tabViewItems isEqualToArray:[_tabs segments]])
return;
[[self items] makeObjectsPerformSelector:@selector(_setTabView:) withObject:nil];
[_tabs setSegments:tabViewItems];
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
[self tileWithChangedItem:nil];
// Update the selection because setSegments: did remove all previous segments AND the selection.
[_tabs setSelectedSegment:[self indexOfTabViewItem:_selectedTabViewItem]];
// should we send delegate methods in bindings mode ?
//[self _delegateTabViewDidChangeNumberOfTabViewItems:self];
}
- (void)_deselectAll
{
[_tabs setSelectedSegment:-1];
_selectedTabViewItem = nil;
}
- (void)_displayPlaceholder:(CPString)aPlaceholder
{
if (_placeholderView == nil)
{
_placeholderView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
[textField setTag:1000];
[textField setTextColor:[CPColor whiteColor]];
[textField setFont:[CPFont boldFontWithName:@"Geneva" size:18 italic:YES]];
[_placeholderView addSubview:textField];
} }
var textField = [_placeholderView viewWithTag:1000]; if (_selectedIndex === CPNotFound)
[textField setStringValue:aPlaceholder];
[textField sizeToFit];
var boxBounds = [_box bounds],
textFieldBounds = [textField bounds],
origin = CGPointMake(CGRectGetWidth(boxBounds)/2 - CGRectGetWidth(textFieldBounds)/2, CGRectGetHeight(boxBounds)/2 - CGRectGetHeight(textFieldBounds));
[textField setFrameOrigin:origin];
[self _displayItemView:_placeholderView];
}
#pragma mark -
#pragma mark Override
/*!
Enabled controls accept first mouse by default.
*/
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
{
return YES;
}
@end
var _CPTabViewContentBinderNull = @"NO CONTENT";
@implementation _CPTabViewContentBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
[super _updatePlaceholdersWithOptions:options];
[self _setPlaceholder:_CPTabViewContentBinderNull forMarker:CPNullMarker isDefault:YES];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
[_source setItems:@[]];
[_source _setPlaceholderView:aValue];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source setItems:aValue];
}
- (id)valueForBinding:(CPString)aBinding
{
return [_source items];
}
@end
var _CPTabViewSelectionBinderMultipleValues = @"Multiple Selection",
_CPTabViewSelectionBinderNoSelection = @"No Selection";
@implementation _CPTabViewSelectionBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
[super _updatePlaceholdersWithOptions:options];
[self _setPlaceholder:_CPTabViewSelectionBinderMultipleValues forMarker:CPMultipleValuesMarker isDefault:YES];
[self _setPlaceholder:_CPTabViewSelectionBinderNoSelection forMarker:CPNoSelectionMarker isDefault:YES];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
if (aMarker == CPNoSelectionMarker || aMarker == CPNullMarker)
[_source _deselectAll];
[_source _displayPlaceholder:aValue];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
if (aBinding == CPSelectionIndexesBinding)
{ {
if (aValue == nil || [aValue count] == 0) [self selectFirstTabViewItem:self];
{
[_source _deselectAll];
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
}
else if ([aValue count] > 1)
[_source _displayPlaceholder:_CPTabViewSelectionBinderMultipleValues];
else if ([aValue firstIndex] < [_source numberOfTabViewItems])
[_source _selectTabViewItemAtIndex:[aValue firstIndex]];
} }
else if (aBinding == CPSelectedIndexBinding)
{
if (aValue == CPNotFound)
{
[_source _deselectAll];
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
}
else if (aValue < [_source numberOfTabViewItems])
[_source _selectTabViewItemAtIndex:aValue];
}
}
- (id)valueForBinding:(CPString)aBinding
{
if (aBinding == CPSelectionIndexesBinding)
{
var result = [CPIndexSet indexSet],
idx = [_source indexOfTabViewItem:[_source selectedTabViewItem]];
if (idx !== CPNotFound)
[result addIndex:idx];
return result;
}
else if (aBinding == CPSelectedIndexBinding)
return [_source indexOfTabViewItem:[_source selectedTabViewItem]];
} }
@end @end
@@ -795,60 +468,30 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
_font = [aCoder decodeObjectForKey:CPTabViewFontKey]; _font = [aCoder decodeObjectForKey:CPTabViewFontKey];
[_tabs setFont:_font]; [_tabs setFont:_font];
var items = [aCoder decodeObjectForKey:CPTabViewItemsKey] || [CPArray array]; _items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
[self _insertTabViewItems:items atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [items count])]];
[self _updateItems];
[self _repositionTabs];
var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
if (selected)
[self selectTabViewItem:selected];
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]]; [self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
_selectedTabViewItem = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey]; [self setTabViewType:[aCoder decodeIntForKey:CPTabViewTypeKey]];
_type = [aCoder decodeIntForKey:CPTabViewTypeKey];
} }
return self; return self;
} }
- (void)awakeFromCib
{
[super awakeFromCib];
// This cannot be run in initWithCoder because it might call selectTabViewItem:, which is
// not safe to call before the views of the tab views items are fully decoded.
if (_selectedTabViewItem)
{
var idx = [self indexOfTabViewItem:_selectedTabViewItem];
if (idx !== CPNotFound)
{
// Temporarily set the selected item to not selected.
// It allows the initial selection to be made correctly.
_selectedTabViewItem = nil;
[self selectTabViewItemAtIndex:idx];
}
}
var type = _type;
_type = nil;
[self setTabViewType:type];
[self setNeedsLayout];
}
- (void)encodeWithCoder:(CPCoder)aCoder - (void)encodeWithCoder:(CPCoder)aCoder
{ {
// Don't bother to encode the CPBox. We will recreate it on decode and its content view is already
// stored by the tab view item. Not encoding _box makes the resulting archive smaller and reduces
// the surface for decoding bugs (of which we've had many in tab view).
var subviews = [self subviews];
[_box removeFromSuperview];
[super encodeWithCoder:aCoder]; [super encodeWithCoder:aCoder];
[self setSubviews:subviews];
[aCoder encodeObject:_items forKey:CPTabViewItemsKey]; [aCoder encodeObject:_items forKey:CPTabViewItemsKey];
[aCoder encodeObject:[self selectedTabViewItem] forKey:CPTabViewSelectedItemKey];
[aCoder encodeConditionalObject:_selectedTabViewItem forKey:CPTabViewSelectedItemKey];
[aCoder encodeInt:_type forKey:CPTabViewTypeKey]; [aCoder encodeInt:_type forKey:CPTabViewTypeKey];
[aCoder encodeObject:_font forKey:CPTabViewFontKey]; [aCoder encodeObject:_font forKey:CPTabViewFontKey];
@@ -857,100 +500,3 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
} }
@end @end
@implementation _CPTabViewBox : CPBox
{
CPTabView _tabView @accessors(property=tabView);
}
#pragma mark -
#pragma mark Override
- (CPView)hitTest:(CGPoint)aPoint
{
// Here we check if we have clicked on the segmentedControl of the tabView or not
// If YES, the CPBox should not handle the click
var segmentIndex = [_tabView._tabs testSegment:[_tabView._tabs convertPoint:aPoint fromView:[self superview]]];
if (segmentIndex != CPNotFound)
return nil;
return [super hitTest:aPoint];
}
@end
# pragma mark -
// This subclass of CPSegmentedControl implements specific behaviour needed by CPTabView, which
// differs in some ways from normal CPSegmentedControl habits :
//
// - all items are enabled
// - when you select a tab (mouse down, holding) and drag upon another tab, the second one reacts
// (showing a pushed state) and can then be selected by releasing the mouse over it.
// - a delegate has the opportunity to accept or not the selection of an item
@implementation _CPSegmentedControl : CPSegmentedControl
{
CPTabView _tabView @accessors(property=tabView);
}
- (void)trackSegment:(CPEvent)anEvent
{
var type = [anEvent type],
location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
currentSegment = [self testSegment:location];
switch (type)
{
case CPLeftMouseUp:
if (_trackingSegment === CPNotFound)
return;
if ((_trackingSegment !== _selectedSegment) &&
[_tabView _sendDelegateShouldSelectTabViewItem:[_tabView tabViewItemAtIndex:_trackingSegment]])
{
[self setSelected:YES forSegment:_trackingSegment];
_selectedSegment = _trackingSegment;
[_tabView selectTabViewItemAtIndex:_selectedSegment];
}
[self drawSegmentBezel:_trackingSegment highlight:NO];
_trackingSegment = CPNotFound;
return;
case CPLeftMouseDown:
if (currentSegment > CPNotFound)
{
_trackingSegment = currentSegment;
[self drawSegmentBezel:_trackingSegment highlight:YES];
}
break;
case CPLeftMouseDragged:
if (_trackingSegment !== currentSegment)
{
if (_trackingSegment > CPNotFound)
[self drawSegmentBezel:_trackingSegment highlight:NO];
_trackingSegment = currentSegment;
if (_trackingSegment > CPNotFound)
[self drawSegmentBezel:_trackingSegment highlight:YES];
}
break;
}
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
}
@end
+8 -108
View File
@@ -23,8 +23,7 @@
@import <Foundation/CPObject.j> @import <Foundation/CPObject.j>
@import "CPView.j" @import "CPView.j"
@class CPTabView
@class CPViewController
/* /*
The tab is currently selected. The tab is currently selected.
@@ -54,34 +53,13 @@ CPPressedTab = 2;
*/ */
@implementation CPTabViewItem : CPObject @implementation CPTabViewItem : CPObject
{ {
id _identifier; id _identifier;
CPString _label; CPString _label;
CPInteger _tag @accessors(property=tag);
CPView _view; CPView _view;
CPView _auxiliaryView; CPView _auxiliaryView;
CPTabView _tabView; CPTabView _tabView;
unsigned _tabState; // Looks like it is not yet implemented
CPImage _image @accessors(property=image);
CPViewController _viewController @accessors(getter=viewController);
BOOL _enabled @accessors(property=enabled);
BOOL _selected @accessors(property=selected);
CGRect _tabRect @accessors(property=frame);
float _width @accessors(property=width);
}
/*
*/
+ (CPTabViewItem)tabViewItemWithViewController:(CPViewController)aViewController
{
var item = [[CPTabViewItem alloc] init];
[item setViewController:aViewController];
return item;
} }
- (id)init - (id)init
@@ -89,19 +67,6 @@ CPPressedTab = 2;
return [self initWithIdentifier:@""]; return [self initWithIdentifier:@""];
} }
- (void)_init
{
_tag = 0;
_viewController = nil;
_image = nil;
_tabState = 0;
_tabView = nil;
_enabled = YES;
_selected = NO;
_tabRect = CGRectMakeZero();
_width = 0;
}
/*! /*!
Initializes the tab view item with the specified identifier. Initializes the tab view item with the specified identifier.
@return the initialized CPTabViewItem @return the initialized CPTabViewItem
@@ -110,12 +75,8 @@ CPPressedTab = 2;
{ {
self = [super init]; self = [super init];
[self _init]; if (self)
_identifier = anIdentifier;
_identifier = anIdentifier;
_label = nil;
_view = nil;
//_auxiliaryView = nil;
return self; return self;
} }
@@ -127,11 +88,7 @@ CPPressedTab = 2;
*/ */
- (void)setLabel:(CPString)aLabel - (void)setLabel:(CPString)aLabel
{ {
if ([aLabel isEqualToString:_label])
return;
_label = aLabel; _label = aLabel;
[_tabView tileWithChangedItem:self];
} }
/*! /*!
@@ -142,28 +99,6 @@ CPPressedTab = 2;
return _label; return _label;
} }
// Working With Images
/*!
Sets the CPTabViewItem's image.
@param anImage the image for the item
*/
- (void)setImage:(CPImage)anImage
{
if ([anImage isEqual:_image])
return;
_image = anImage;
[_tabView tileWithChangedItem:self];
}
/*!
Returns the CPTabViewItem's image
*/
- (CPImage)image
{
return _image;
}
// Checking the Tab Display State // Checking the Tab Display State
/*! /*!
Returns the tab's current state. Returns the tab's current state.
@@ -197,13 +132,7 @@ CPPressedTab = 2;
*/ */
- (void)setView:(CPView)aView - (void)setView:(CPView)aView
{ {
if (_view == aView)
return;
_view = aView; _view = aView;
if ([_tabView selectedTabViewItem] == self)
[_tabView _displayItemView:_view];
} }
/*! /*!
@@ -211,9 +140,6 @@ CPPressedTab = 2;
*/ */
- (CPView)view - (CPView)view
{ {
if (!_view && _viewController)
return [_viewController view]; // The view controller loads here.
return _view; return _view;
} }
@@ -252,32 +178,10 @@ CPPressedTab = 2;
_tabView = aView; _tabView = aView;
} }
/*!
Sets the specified view controller for the tab view item.
@param aViewController an instance of CPViewController.
*/
- (void)setViewController:(CPViewController)aViewController
{
_viewController = aViewController;
var identifier = [aViewController cibName],
title = [_viewController title];
if (identifier)
_identifier = identifier;
if (title)
[self setLabel:title];
if ([_tabView selectedTabViewItem] == self)
[_tabView _loadTabViewItem:self];
}
@end @end
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey", var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
CPTabViewItemLabelKey = "CPTabViewItemLabelKey", CPTabViewItemLabelKey = "CPTabViewItemLabelKey",
CPTabViewItemImageKey = "CPTabViewItemImageKey",
CPTabViewItemViewKey = "CPTabViewItemViewKey", CPTabViewItemViewKey = "CPTabViewItemViewKey",
CPTabViewItemAuxViewKey = "CPTabViewItemAuxViewKey"; CPTabViewItemAuxViewKey = "CPTabViewItemAuxViewKey";
@@ -290,11 +194,8 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
if (self) if (self)
{ {
[self _init];
_identifier = [aCoder decodeObjectForKey:CPTabViewItemIdentifierKey]; _identifier = [aCoder decodeObjectForKey:CPTabViewItemIdentifierKey];
_label = [aCoder decodeObjectForKey:CPTabViewItemLabelKey]; _label = [aCoder decodeObjectForKey:CPTabViewItemLabelKey];
_image = [aCoder decodeObjectForKey:CPTabViewItemImageKey];
_view = [aCoder decodeObjectForKey:CPTabViewItemViewKey]; _view = [aCoder decodeObjectForKey:CPTabViewItemViewKey];
_auxiliaryView = [aCoder decodeObjectForKey:CPTabViewItemAuxViewKey]; _auxiliaryView = [aCoder decodeObjectForKey:CPTabViewItemAuxViewKey];
@@ -307,7 +208,6 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
{ {
[aCoder encodeObject:_identifier forKey:CPTabViewItemIdentifierKey]; [aCoder encodeObject:_identifier forKey:CPTabViewItemIdentifierKey];
[aCoder encodeObject:_label forKey:CPTabViewItemLabelKey]; [aCoder encodeObject:_label forKey:CPTabViewItemLabelKey];
[aCoder encodeObject:_image forKey:CPTabViewItemImageKey];
[aCoder encodeObject:_view forKey:CPTabViewItemViewKey]; [aCoder encodeObject:_view forKey:CPTabViewItemViewKey];
[aCoder encodeObject:_auxiliaryView forKey:CPTabViewItemAuxViewKey]; [aCoder encodeObject:_auxiliaryView forKey:CPTabViewItemAuxViewKey];

Some files were not shown because too many files have changed in this diff Show More